工厂模式分析总结

工厂模式

工厂模式属于设计模式中的创建型。

工厂模式的分类

工厂模式又可以细分为三类。
分别为简单工厂、工厂方法、抽象工厂三种。
其中简单工厂一般看做是工厂方法中的一个特例。

工厂模式的好处

封装变化: 创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
代码复用: 创建代码抽离到独立的工厂类之后可以复用。
隔离复杂性: 封装复杂的创建逻辑,调用者无需了解如何创建对象。
控制复杂度: 将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁
增加代码可读性: 工厂内部封装不同的入参构造函数并提供语义清晰的调用方法,避免了类构造函数只能与类名相同。

什么情况下选择工厂模式

  1. 代码中存在if-else 或者 switch-case 语句,动态的根据不同的类型创建不同的对象,针对这种情况就可以考虑使用工厂模式,将这部分创建对象的代码抽离出来,放到工厂类中。
  2. 单个对象的创建过程复杂,例如需要组合其他类对象,做各种初始化操作,这种情况下,也可以封装这部分创建逻辑,放到工厂类中。

简单工厂

在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。

简单工厂把实例的创建过程单独放到一个类中,这个类就是简单工厂类,由简单工厂类来决定具体实例化哪个子类。

例如一个游戏启动程序,根据不同的输入打开不同的游戏

public class GameConfigSource{

	public Game load(String gameName){

		Game game = null;

		if ("lol".equalsIgnoreCase(gameName)) {
		      game = new LolGameClient();    
		} else if ("cs".equalsIgnoreCase(gameName)) { 
		      game = new CsGameClient();    
		} else if ("wow".equalsIgnoreCase(gameName)) { 
		      game = new WowGameClient();    
		} else if ("ddz".equalsIgnoreCase(gameName)) {  
		      game = new DdzGameClient();   
		} else {     
		     throw new InvalidGameException("Game is not found: " + ruleConfigFilePath);  
		}

		game.start();
		return game;
	}

}

这个类目前来看好像没什么问题。我们来具体看一下,这个类对象的创建和对象的使用耦合在了一起,未来不管是新增对象还是新增使用方法,我都需要重新修改这个类,这违反了开闭原则

那么为了使类的职责更加单一、代码更加清晰,我们可以将创建这一部分放到另一个独立的类中,只负责创建

public class GameFactory{

   public static Game createGame(String gameName){

   	 Game game = null;
		
		if ("lol".equalsIgnoreCase(gameName)) {
		      game = new LolGameClient();
		} else if ("cs".equalsIgnoreCase(gameName)) {
		      game = new CsGameClient();
		} else if ("wow".equalsIgnoreCase(gameName)) {
		      game = new WowGameClient();
		} else if ("ddz".equalsIgnoreCase(gameName)) {
		      game = new DdzGameClient()
		}
		return game;
   }

}
public class GameConfigSource{

	public Game load(String gameName){

		Game game = GameFactory.createGame(gameName);

		if(null == game){
			throw new InvalidGameException("Game is not found: " + gameName);  
		}

		game.start();
		return game;
	}
}

上述代码如果频繁的添加新的游戏,那么势必会频繁的修改GameFactory类,这违反了开闭原则,但是如果不是特别频繁,只是偶尔修改,还是可以接受的。
如果不想违反开闭原则的话,可以通过反射加配置文件的方式,通常类的全路径较长,通过配置文件可读性好一些。

配置文件中 : lol=com.test.game.LolGameClient;

 //读取配置文件 
 String name = parserConfig(gameName);
 //通过反射获取类
 Object obj = Class.forName(name).newInstance();

工厂方法

定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类

工厂方法模式是简单工厂的进一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说 每个对象都有一个与之对应的工厂。

public interface GameFactory{
	public Game getGame();
}

public class LolGameFactory implements GameFactory{
	@Override
    public Game getGame() {
        return new LolGameClient();
    }

}
public class CsGameFactory implements GameFactory{
	@Override
    public Game getGame() {
        return new CsGameClient();
    }

}
public class WowGameFactory implements GameFactory{
	@Override
    public Game getGame() {
        return new WowGameClient();
    }

}
public class DdzGameFactory implements GameFactory{
	@Override
    public Game getGame() {
        return new DdzGameClient();
    }

}

public class GameConfigFactory{
	

    public static GameFactory createFactory(String gameName){

   		GameFactory gameFactory = null;
		if ("lol".equalsIgnoreCase(gameName)) {
		      game = new LolGameFactory();    
		} else if ("cs".equalsIgnoreCase(gameName)) { 
		      game = new CsGameFactory();    
		} else if ("wow".equalsIgnoreCase(gameName)) { 
		      game = new WowGameFactory();    
		} else if ("ddz".equalsIgnoreCase(gameName)) {  
		      game = new DdzGameFactory();   
		} 
		return gameFactory;
   }
}

其实这样看来,还是不能避免大量if-else的问题,反而加深了代码的复杂性,而且每个类的创建只是简单的new操作。所以这种情况下简单工厂比工厂方法要合适。

什么时候用简单工厂什么时候用工厂方法

  • 当创建的对象只是通过new操作,推荐使用简单工厂模式。
  • 当创建的对象逻辑比较复杂,不只是简单的new操作,而是要组合其他类,做各种初始化操作的时候,推荐使用工厂方法,将复杂的创建逻辑拆分到多个工厂类中,这样不至于使每个类过于臃肿。

抽象工厂

提供一个接口,用于创建 相关的对象家族 。

抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。

例如我们的例子中,开启游戏不仅需要游戏客户端,还需要打开游戏音效。

public interface GameFactory{
	public GameClient getClient();
	public GameVoice getVoice();
}

public class LolGameFactory implements GameFactory{
	@Override
    public GameClient getClient() {
        return new LolGameClient();
    }

    @Override
    public GameVoice getVoice() {
        return new LolGameVoice();
    }
}

public class CsGameFactory implements GameFactory{
	@Override
    public GameClient getClient() {
        return new CsGameClient();
    }

    @Override
    public GameVoice getVoice() {
        return new CsGameVoice();
    }
}

public class WowGameFactory implements GameFactory{
	@Override
    public GameClient getClient() {
        return new WowGameClient();
    }

    @Override
    public GameVoice getVoice() {
        return new WowGameVoice();
    }
}

public class DdzGameFactory implements GameFactory{
	@Override
    public GameClient getClient() {
        return new DdzGameClient();
    }

    @Override
    public GameVoice getVoice() {
        return new DdzGameVoice();
    }
}

public class Client {
    public static void main(String[] args) {
        GameFactory gameFactory = new LolGameFactory();
        GameClient client = gameFactory.getClient();
        GameVoice voice = gameFactory.getVoice();
        // do something
    }
}

扩展

如果工厂对象可以复用,可以通过一个map来缓存工厂实例,达到复用的效果,也避免了大量if-else的情况,每次添加维护map即可。

// 简单工厂
public class GameFactoryMap {
  private static final Map<String, Game> cachedGame = new HashMap<>();

  static {
    cachedGame.put("lol", new LolGameClient());
    cachedGame.put("cs",  new CsGameClient());
    cachedGame.put("wow", new WowGameClient());
    cachedGame.put("ddz", new DdzGameClient());
  }

  public static Game createGame(String gameName) {
    if (gameName == null || gameName.isEmpty()) {
      return null;
    }
    Game geme = cachedGame.get(gameName.toLowerCase());
    return geme;
  }
}
// 工厂方法、抽象工厂
public class GameFactoryMap {
  private static final Map<String, GameFactory> cachedGameFactory = new HashMap<>();

  static {
    cachedGameFactory.put("lol", new LolGameFactory());
    cachedGameFactory.put("cs",  new CsGameFactory());
    cachedGameFactory.put("wow", new WowGameFactory());
    cachedGameFactory.put("ddz", new DdzGameFactory());
  }

  public static GameFactory createFactory(String gameName) {
    if (gameName == null || gameName.isEmpty()) {
      return null;
    }
    GameFactory gemeFactory = cachedGameFactory.get(gameName.toLowerCase());
    return gemeFactory;
  }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值