工厂模式
作用:<mark>实现创建者和调用者的分离</mark>
分类:
- 简单工厂模式
- 工厂方法模式
- 抽象方法模式
遵循的OOP原则:
- 开闭原则:一个软件的实体应当对扩展开放,对修改关闭
- 依赖倒转原则:要针对接口编程,不要针对实现编程
- 迪米特法则:只与你直接的朋友通信,避免和陌生人通信
核心本质:
- 实例化不使用new,而使用工厂类的方法
- 将选择实现类,创建对象统一管理和控制,从而将调用者和实现类解耦。
- 传统的工厂模式(模拟生产车子工厂)
车子接口:
/** * 车子接口 */
public interface Car {
void name();
}
五菱宏光:
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光666~");
}
}
奥迪:
public class AoDi implements Car {
@Override
public void name() {
System.out.println("奥迪666~");
}
}
顾客:
public class Consumer {
public static void main(String[] args) {
Car wuling = new WuLing(); //购卖五菱
Car aodi = new AoDi(); //购卖奥迪
wuling.name();
aodi.name();
}
}
存在的问题:
- 顾客不应该去new🚗,它按理说应该关注买到已有的车
- 如果此时有新的品牌车被创造,则它必须实现🚗接口,顾客也必须再去new
- 调用者(顾客),和实现类(品牌车)的耦合度较高,也违反了三个OOP原则
1. 简单工厂模式(静态工厂模式)
将调用者new这个动作交给工厂来操作,解耦了调用者和实现类。
车子接口:
/** * 车子接口 */
public interface Car {
void name();
}
五菱宏光:
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光666~");
}
}
奥迪:
public class AoDi implements Car {
@Override
public void name() {
System.out.println("奥迪666~");
}
}
🚗工厂:
public class SimpleFactory {
private static Car car = null;
public static Car createCar(String name){
if("五菱".equals(name)){
car = new WuLing();
}else{
car = new AoDi();
}
return car;
}
}
消费者:
public class Consumer {
public static void main(String[] args) {
Car wuling = SimpleFactory.createCar("五菱"); //购买五菱
Car aodi = SimpleFactory.createCar("奥迪"); //奥迪
wuling.name();
aodi.name();
}
}
此时,将生产车子的new动作交给了SimpleFactory类来做,而不用调用者来直接操作
存在的问题:
没有遵循着开闭原则,因为即使不用我们调用者生产车,但如果有新品牌的车,我们还是要修改车工厂的代码,横向扩展比较难。
2. 工厂方法模式
抽取生产车子工厂,为每一个品牌的车创建工厂,可以做到不去修改某一个车工厂的代码,这样做到在增加某一品牌的时候不去修改其它车工厂的代码, 可以提高横向扩展性,但是代码量却增加了不少。
车子接口:
public interface Car {
void name();
}
车子工厂接口:
public interface CarFactory {
public Car getCar();
}
五菱:
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光666~");
}
}
五菱工厂:
public class WuLingFactory implements CarFactory {
@Override
public Car getCar() {
return new WuLing();
}
}
奥迪:
public class AoDi implements Car {
@Override
public void name() {
System.out.println("奥迪666~");
}
}
奥迪工厂:
public class AoDiFactory implements CarFactory {
@Override
public Car getCar() {
return new AoDi();
}
}
消费者:
public class Consumer {
public static void main(String[] args) {
Car wuling = new WuLingFactory().getCar();
wuling.name();
Car aodi = new AoDiFactory().getCar();
aodi.name();
}
}
聪明的人都已经发现了,除了代码量增加了许多,结构和管理上的复杂度也增加了不少,但是可以满足横向扩展,就是说,遵循了开闭原则,做到了对扩展开放,对修改关闭。
简单工厂模式和工厂方法的对比:
- 如果需要满足设计原则,应当选用工厂方法
- 没有设计原则的要求,应尽量选择简单工厂模式,因为简单工厂模式无论从编程难度,代码结构,管理的复杂性来说都是比工厂方法更低
3. 抽象方法模式
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类,围绕一个超级工厂创建其它工厂,该超级工厂又被称之为工厂的工厂。
适用场景:
- 客户端不依赖产品类实例如何被创建,需要的时候工厂取出来即可,也无需关系创建对象的细节
- 强调一系列相关的产品对象属于同一产品族,一起使用创建对象需要大量的代码
- 提供一个产品类的库,所有的产品类以同样的接口出现,从而使得客户端不依赖于具体的实习
如下:
模拟小米和华为工厂有各自的产品族:手机族和路由器族,用抽象工厂的实现方式,实现消费者从工厂中买到手机的细节:
- 抽象的抽象接口:
/** * 抽象的抽象工厂:可以生产手机和路由器,包含两个小工厂:小米和华为 */
public interface IProductFactory {
IphoneProduct getPhone();
IRouterProduct getRouter();
}
- 实现抽象的抽象接口:
//华为工厂可以生产手机和路由器
public class HuaweiProductFactory implements IProductFactory {
@Override
public IphoneProduct getPhone() {
return new HuaweiPhone();
}
@Override
public IRouterProduct getRouter() {
return new HuaweiRouter();
}
}
//小米工厂可以生产路由器和手机
public class XiaoMiProductFactory implements IProductFactory{
@Override
public IphoneProduct getPhone() {
return new XiaoMiPhone();
}
@Override
public IRouterProduct getRouter() {
return new XiaoMiRouter();
}
}
- 产品族的接口及实现类:
路由器:
//路由器产品
public interface IRouterProduct {
void start();
void stop();
void openWifi();
}
public class HuaweiRouter implements IRouterProduct {
@Override
public void start() {
System.out.println("启动华为路由器");
}
@Override
public void stop() {
System.out.println("关闭华为路由器");
}
@Override
public void openWifi() {
System.out.println("启动华为路由器wifi");
}
}
public class XiaoMiRouter implements IRouterProduct {
@Override
public void start() {
System.out.println("启动小米路由器");
}
@Override
public void stop() {
System.out.println("关闭小米路由器");
}
@Override
public void openWifi() {
System.out.println("启动小米wifi");
}
}
手机:
//手机产品
public interface IphoneProduct {
void start();
void stop();
void call();
void sendMsg();
}
public class HuaweiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("启动华为手机");
}
@Override
public void stop() {
System.out.println("关闭华为手机");
}
@Override
public void call() {
System.out.println("拨打华为手机");
}
@Override
public void sendMsg() {
System.out.println("华为手机发消息");
}
}
public class XiaoMiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("启动小米手机");
}
@Override
public void stop() {
System.out.println("关闭小米手机");
}
@Override
public void call() {
System.out.println("拨打小米手机");
}
@Override
public void sendMsg() {
System.out.println("小米手机发消息");
}
}
- 消费者:
public class Consumer {
public static void main(String[] args) {
System.out.println("-----------------生产小米系列产品-----------------");
XiaoMiProductFactory xmf = new XiaoMiProductFactory();
IphoneProduct xiaomiPhone = xmf.getPhone();
xiaomiPhone.start();
xiaomiPhone.call();
IRouterProduct xiaomiRouter = xmf.getRouter();
xiaomiRouter.start();
xiaomiRouter.openWifi();
System.out.println("-----------------生产华为系列产品-----------------");
HuaweiProductFactory huaweiFactory = new HuaweiProductFactory();
IphoneProduct huaweiPhone = huaweiFactory.getPhone();
huaweiPhone.start();
huaweiPhone.sendMsg();
IRouterProduct huaweiRouter = huaweiFactory.getRouter();
huaweiRouter.start();
huaweiRouter.openWifi();
}
}
- 运行结果:
抽象工厂模式内部类与接口直接的依赖图:
看着类直接的依赖关系显然头很大,不如看个轻松点的
可见,无论是依赖图还是我画结构图,抽象工厂模式的内部结构依旧十分复杂,而且不易扩展,怎么说呢?倘若,我们要为抽象的抽象工厂增加一个生产笔记本的方法,那么如果可以生产笔记本的厂家也都必须增加一个对应的生产方法,那如果底下有很多厂家都可以生产笔记本,那么每个品牌的工厂就要修改多份,这样也就破坏了开闭原则。
抽象工厂模式的优点:
可以将一系列产品统一到一起创建,然后将具体的产品代码隔离,创建具体的产品可以无需关注其细节。
抽象工厂模式的缺点:
规定了所有可能被创建的产品集合,在产品簇中扩展新的产品困难,增加了系统的抽象性和理解难度。
4. 小结
- 简单工厂模式(静态工厂模式):
虽然某种程度上不符合设计原则(开闭原则),但实际使用最多。
- 工厂方法模式
不修改已有类的前提上,通过增加新的工厂类实现扩展性
- 抽象工厂模式
不可以增加产品,可以增加产品簇。
应用场景
- JDK中的Calendar的getInstance方法
- JDBC中的Connection对象的获取
- Spring IOC容器创建管理bean对象
- 反射中Class对象的newInstance方法
参考B站:https://www.bilibili.com/video/BV1mc411h719?t=1413&p=4