1.什么是工厂模式
- 作用:
- 实现了创建者和调用者的分离
- 详细分类:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式(不属于工厂模式,但是可以放在一起进行对比)
- 只包含简单工厂模式和工厂方法模式
- 核心本质:
- 实例化对象不使用new,用工厂方法代替
- 将选择实现类,创建对象统一管理和控制,从而将调用者跟我们的实现类解耦
- 三种模式
- 简单工厂模式:用来生产同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码)
- 工厂方法模式:用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式:围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂
2.代码实现
1.简单/静态 工厂模式
- 我们以买车为例,首先我们创建一个Car接口,然后创建两个Car的实现类:五菱宏光、奔驰
public interface Car { //实体类的接口 void name(); } public class WuLing implements Car{ //五菱宏光 @Override public void name() { System.out.println("我是五菱宏光"); } } public class Benz implements Car{ @Override public void name() { System.out.println("我是梅赛德斯-奔驰"); } }
- 模拟卖出,创建测试类
public class Test { public static void main(String[] args) { Car car1 = new WuLing();//模拟买了五菱宏光 Car car2 = new Benz();//模拟买了奔驰 car1.name(); car2.name(); } }
- 上面的例子虽然已经测试成功了,但是我们可以发现一些问题:上面的模拟买车的过程中,首先我们需要知道接口Car,我们还需要了解它的两个实现类WuLing和Benz的构造方法,即我想要这两辆车,我需要自己动手制造,但是在显示生活中并不是这样的
- 在现实生活中我们只需要去车行,说明我们要买什么车,然后就能获得什么车,而不是我想要什么车我需要自己造出来
- 解决办法:使用工厂模式,将对象的创建全部丢给工厂,我们只需要告诉工厂我们需要什么,工厂就能为我们返回对应的对象供我们使用
测试public class CarFactory { //建车工厂类 public static Car car; //返回的车 public static Car getInstance(String name){ if ("五菱".equals(name)){ car = new WuLing(); } else if ("奔驰".equals(name)){ car = new Benz(); } return car; } }
public class Test { public static void main(String[] args) { Car car1 = CarFactory.getInstance("五菱");//模拟买了五菱宏光 Car car2 = CarFactory.getInstance("奔驰");//模拟买了奔驰 car1.name(); car2.name(); } }
- 从上面的代码+效果来看,我们使用工厂模式就实现了给出我们的要求就能获取到我们想要的东西的需求,我输入想要五菱宏光就获得了五菱宏光,我输入想要奔驰就获得了奔驰
- 但是使用上面的实现逻辑我们可以看出一些问题:如果我们的车的实现类多一个/少一个,我们就去要修改工厂类中的if判断,这显然违背了OOP 7大原则中的"开闭原则"(对对扩展开放,对修改关闭);
- 我们也可以将原来的if判断全部拆分,将每一个返回对象的过程都写成一个static方法;这样我们新增/删除一个实现类的时候就是在
public class CarFactory { public static Car getWuLing(){ return new WuLing(); } public static Car getBenz(){ return new Benz(); } }
- 但是这种做法也存在弊端/缺陷,代码冗余,一旦实现类多起来就会出现大量的代码冗余,并且维护困难的问题,并且上面这个做法也是不满足"开闭原则"的
小结
- 对于像上面这样的工厂类,我们一般称为简单/静态 工厂模式,原因:因为这个类里面的所有方法都必须是静态的,并且对于类的扩展,不修改类的源代码是做不到的,所以简单/静态 工厂模式是有很大的弊端的
- 使用 简单/静态 工厂模式,屏蔽了我们实际使用具体实现类的时候的细节,主要就是new一个对象的时候需要传入哪些参数(即调用实现类的有参构造的调用),我们不需要管了,方便了调用者使用,只是它不满足开闭原则
2.工厂方法模式
- 工厂方法模式解决了简单/静态工厂模式每一次进行扩展的时候需要修改原工厂代码的弊端
- 我们可以分析原来扩展实现类的时候必须修改工厂类的原因:所有Car类的对象的创建都在一个Factory中,如果我们能够一个Car的实现类一个工厂,则当实现类扩张的时候,我们只需要重新定义一个这个实现类的工厂类就可以实现原来简单/静态工厂模式相同的效果,这样的做法就叫工厂方法模式
- 首先我们定义一个CarFactory接口,用于规范所有实现类的工厂类
public interface CarFactory1 { public Car1 getInstance(); }
- 现在Car有一个wuling实现类和Benz实现类,按照工厂方法模式,我们应该创建两个CarFactory接口的实现类WulingFactory和BenzFactory,让用户获取对应实例的时候去这两个工厂中获取
public class WulingFactory implements CarFactory1{ public Car1 getInstance() { return new WuLing1(); } } public class BenzFactory implements CarFactory1 { public Car1 getInstance() { return new Benz1(); } }
- 测试
public class Test1 { public static void main(String[] args) { Car1 car1 = new WulingFactory().getInstance();//模拟买了五菱宏光 Car1 car2 = new BenzFactory().getInstance();//模拟买了奔驰 car1.name(); car2.name(); } }
- 你可能觉得在使用对应的实现类的工厂的时候还要new,这不是返祖现象?回到了直接new Car的实现类的时代了吗?
- 注意:上面使用对应的实现类的工厂的时候确实使用了new,但是我们可以保证所有的工厂new的时候没有任何参数需要传入,但是new实现类的时候不能保证,所以使用工厂方法模式还是保留了工厂模式的好处,不用关系具体创建的细节
3.对比工厂方法模式和简单/静态 工厂模式
- 当我们使用工厂方法模式的时候我们首先只需要定义一个Car接口,一个Factory接口;然后只要有一个Car的实现类,我们就去创建一个这个类对应的Factory类,且实现Factory接口;最后的效果就是横向扩张的时候我们不会修改原来的代码,只需要跟着横向扩展一个实现类对应的工厂实现类即可
- 所以工厂方法模式的优点:在保留工厂模式特点的基础上,符合了OOP 7大原则中的开闭原则;缺点:每扩展一个实现类就要多扩展一个实现类对应的工厂类,这导致文件数量几乎达到使用简单/静态 工厂模式的时候的2倍
- 我们也可以从实际使用方面来对二者进行比较
- 可见,不管在哪一个方面,我们在实际使用方面,都应该更优先的使用简单/静态 工厂模式,而不是工厂方法模式,虽然工厂方法模式符合开闭原则,而简单/静态 工厂模式不符合,但是为了符合开闭原则就将整个项目结构变得复杂实在没有必要
- 即在实际开发中,我们不一定必须满足OOP 7大原则,而是根据实际的开发中使用哪一种方式进行开发更加便利且实用来决定(思想)
- 所以在实际的开发中我们更多的使用的是简单/静态 工厂模式
3.抽象工厂模式
- 首先明确:抽象工厂模式是围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂
- 7种常用的设计模式(5) —— 抽象工厂模式