## 为什么要用工厂模式? 工厂模式其实也称创建模式,是用于创建对象的一种方式。本质上就是用工厂方法来代替new实例化对象。 举个例子:我们在编写代码的时候,在一个 A 类中通过 new 的方式实例化了类 B,那么 A 类和 B 类之间就存在耦合,如果以后修改了 B 类的代码和使用方式,例如需要在构造函数中传入参数,那么 A 类也就需要跟着修改了,一个类的依赖可能影响不大,但若有多个类依赖了 B 类,那么这个工作量将会相当的大,这无疑是件非常痛苦的事。这种情况下,我们需要把创建实例的工作单独分离,与调用方解耦,也就是使用工厂方法创建实例的工作封装起来。这样我们在需要调用对象的时候就不需要关心那些复杂的实例化问题。 ## 工厂模式 工厂模式可分为两类:简单工厂模式和工厂模式。 ## 简单工厂模式 定义一个接口和实现类,建立一个工厂类这些实现类进行实例的创建。 我们用运动员来举例,定义一个基本的接口 Player,和一个抽象方法Play, ```java interface Player { void play(); } ``` 创建一个篮球运动员和一个足球运动员,并实现该接口, ```java class BasketBallPlayer implements Player { public void play() { System.out.println("打篮球~~~"); } } class class FootBallPlayer implements Player { public void play() { System.out.println("踢足球~~~"); } } ``` 然后,创建一个工厂类,可以用于生产打篮球或者踢足球的运动员, ```java class PlayerFactory { public Player produce(String type) { if ("basketball".equals(type)) { return new BasketBallPlayer(); } else if ("football".equals(type)) { return new FootBallPlayer(); } return null; } } ``` 工厂类建好以后,我们就可以实例化工厂类,并调用 produce 方法来创建对应的实例对象, ```java public static void main(String[] args) { PlayerFactory factory = new PlayerFactory(); Player player = factory.produce("basketball"); player.play(); } ``` 结果输出:`打篮球~~~` 这就是简单工厂模式的基本实现,用关系图来表示就是:  这种模式的优点是代码简单,能够根据具体的参数返回对应的实例对象。 当然缺点也很明显,就是工厂类集中了所有实例的创建逻辑,如果增加业务就要多出相应的工厂方法,不仅代码可能变得臃肿,也容易违反GRASPR的高内聚的责任分配原则 ## 工厂模式 又称多态性工厂模式,是对简单工厂模式的改进。工厂模式中,一个子类对应一个工厂类,这些工厂类都实现了一个工厂接口。这相当于把一个简单工厂类拆分成多个工厂,这样代码就不会都耦合在同一个类里了。 具体的产品接口和实现类还是复用上面的代码,我们只需关注工厂方法的逻辑即可, 先创建一个工厂的接口 ```java interface IFactory { void produce(); } ``` 然后创建对应业务的工厂类 ```java class BasketballPlayerFactory implements IFactory { public Player produce() { return new BasketBallPlayer(); } } class FootballPlayerFactory implements IFactory { public Player produce() { return new FootBallPlayer(); } } ``` 测试代码 ```java public static void main(String[] args) { BasketballPlayerFactory basketballPlayerFactory = new BasketballPlayerFactory(); Player basketballPlayer = basketballPlayerFactory.produce(); FootballPlayerFactory footPlayerFactory = new FootballPlayerFactory(); Player footballPlayer = footballFactory.produce(); basketballPlayer.play(); footballPlayer.play(); } ``` 输出结果是: ``` 打篮球~~~ 踢足球~~ ``` 如上所示,如果需要添加新的产品,如排球运动员,我们就多写一个工厂类即可,这样就不会把所有的业务都耦合到一个工厂类中了,用关系图表示如下:  实际开发中,上面的多态工厂模式实现存在大量的代码冗余,解决方式就是让一个 Factory 类负责创建尽可能多的对象。例如下面的 Factory 类,通过一个静态方法通过反射根据指定的类名来构造对象: ```java class Factory { private Factory() {} public static IMessage getInstance(String className) { Player instance = null; try { instance = (Player) Class.forName(className).getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); } return instance; } } public class Main { public static void main(String[] args) { Player player = Factory.getInstance("BasketBallPlayer"); player.play(); } } ``` 这样的 Factory 只能构造指定接口的对象,可以使用泛型改进下,使之称为超级工厂,能够构造任意类型的对象: ```java class MegaFactory { public static <T> T getInstance(Class<T> clazz) { T instance = null; try { instance = (T) clazz.getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); } return instance; } } class Main { public static void main(String[] args) { Player player = MegaFactory.getInstance("BasketBallPlayer", BasketBallPlayer.class); player.play(); } } ``` ## 延迟初始化 延迟初始化就是一个对象被消费完毕后,并不立即释放,工厂类保持其初始状态,等待再次被使用。 延迟初始化是工厂方法模式的一个扩展应用,其通用类如图:  ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用保留的对象: ```java class ProductFactory extends AbstractProductFactory { private static final Map<String, Product> productMap = new HashMap<String, Product>(); @Override public synchronized <T extends Product> T createProduct(Class<T> c) { Product product = null; try { String type = c.getSimpleName(); if (productMap.containsKey(type)) { product = productMap.get(type); } else { product = (Product) Class.forName(c.getName()).newInstance(); productMap.put(type, product); } } catch (Exception e) { } return (T) product; } } ``` ## 将 Annotation 整合到工厂方法 JDK 8 引入了对 Annotation 的支持,因此可以通过整合 Annotation 实现更新一代的超级工厂: ```java @Retention(RetentionPolicy.RUNTIME) @interface PlayerInjector { public Class<?> clazz(); } class GigaFactory { public static <T> T getInstance(Class<T> clazz) { T instance = null; try { instance = (T) clazz.getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); } return instance; } } ``` 通过`PlayerInjector` 这个 Annotation 可以用来将 FootBallPlayer.class 注入到 Factory 类: ```java @PlayerInjector(clazz=FootBallPlayer.class) class Main { public static void main(String[] args) { PlayerInjector injector = Main.class.getAnnotation(PlayerInjector.class); Player player = (Player)GigaFactory.getInstance(injector.clazz()); player.play(); } } ``` 最后,总结一下工厂模式的优点: 1. 良好的封装性,代码结构清晰,调用者只需知道产品的类名即可,不需要知道创建对象的过程,降低代码间的耦合。 2. 扩展性优秀,如果增加一个产品类,只需增加一个对应的工厂类。 3. 屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,只需关心产品的接口,只要接口保持不变,系统中的上层模块就不会发生变化。 4. 工厂模式是典型的解耦框架,高层模块只需要知道产品的抽象类,其他的实现类都不需要关心,符合迪米特法则,符合依赖倒置原则,符合里氏替换原则。 --- 参考: - 《设计模式之禅》
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up