设计模式 – 工厂模式
OOP(面向对象的七大原则)
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换原则:继承必须确保超类所拥有的性质再子类中仍然成立
- 依赖倒置原则:要面向接口编程,不要面向实现编程
- 单一职责原则:要控制类的粒度大小,将对象解耦,提高其内聚性
- 接口隔离原则:要为各个类建立它们需要的专用接口
- 迪米特法则:只与你的直接朋友交谈,不跟陌生人说话
- 合成复用原则:尽量先使用或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
工厂模式介绍:
- 工厂模式介绍:
- 它提供了一种创建对象的最佳方式,我们再创建对象的时候,不会对客户端暴漏创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
- 例子:
- 工厂生产电脑,除了A品牌、还可以生产B、C、D品牌电脑
- 业务开发种,支付很常见,里面有统一下单和支付接口,具体的支付实现可以微信、支付宝、银行卡等
- 工厂模式有三种不同的实现方式:
- 简单工厂模式:通过传入相关的类型来返回相应的类,这种方式比较单一,可扩展性相对较差
- 工厂方法模式:通过实现类实现相应的方法来决定相应的返回结果,这种方式的可扩展性强
- 抽象工厂模式:基于上述两种模式的扩展,且支持细化产品。
- 应用场景:
- 解耦:分离职责,把复杂对象的创建和使用的过程分开
- 复用代码技术:降低维护成本
- 如果对象创建复杂且多处需要用到,如果每处都进行编写,则很多重复代码,如果业务逻辑发生了改变,需要四处修改
- 使用工厂模式统一创建,只需要修改工厂类即可,降低成本
工厂模式 ---------- 简单工厂模式
- 简单工厂模式(静态工厂模式)
- 又称静态工厂方法,可以根据参数的不同返回不同类的实例,专门定义一个类来负责创建其他类的实例,被创建的实例都具有相同的父类
- 由于工厂方法是静态方法,可以通过类名直接调用,而且只需要传入简单的·参数即可
- 核心组成
- Factory:工厂类,简单工厂模式的核心类,它负责实现创建所有实例的内部逻辑
- IProduct:抽象产品类,简单工厂模式所创建的所有对象的父类,描述所有实例所共有的公共接口
- Product:具体产品类,是简单工厂模式的创建目标
- 实现步骤:
- 创建抽象产品类:里面有产品的抽象方法,由具体的产品类去实现
- 创建具体产品类,继承它们的父类,并实现具体方法
- 创建工厂类,提供了一个静态方法createXXX()用来生成产品,只需要传入你想要生成的产品名称
- 优点:
- 将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来相对容易
- 缺点:
- 工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则相违背
- 即开闭原则(Open Close Principle)对扩展开放,对修改关闭,程序需要进行扩展的时候,不难去修改原有的代码,实现一个热插拔的效果
- 将会增加系统中类的个数,在一定程度上增加了系统的复杂度和理解难度,不利于系统的扩展和维护,创建简单对象就不用模式
下面我们来简单使用一下简单(静态)工厂设计模式:
功能描述:
我们简单使用伪代码模拟一下支付流程:
创建Product抽象产品接口 ----------------- IPay
/**
* @author wcc
* @date 2021/8/19 9:13
* @Description: IPay抽象统一支付下单接口
*/
public interface IPay {
/**
* 统一下单
*/
void unifiedOrder();
}
创建Product具体产品类 ----------- AliPay/WeChatPay
AliPay.java
/**
* @author wcc
* @date 2021/8/19 9:15
* @Description:支付宝具体实现类
*/
public class AliPay implements IPay {
@Override
public void unifiedOrder() {
System.out.println("支付宝统一下单");
}
}
WeChatPay.java
/**
* @author wcc
* @date 2021/8/19 9:16
* @Description:微信支付具体实现类
*/
public class WeChatPay implements IPay {
@Override
public void unifiedOrder() {
System.out.println("微信支付具体实现类");
}
}
创建Factory工厂类 ------------- SimplePayFactory
/**
* @author wcc
* @date 2021/8/19 9:17
* @Description:简单支付工厂类(静态工厂类)
*/
public class SimplePayFactory {
/**
* 工厂创建方法:根据参数返回对应的支付对象
* @param payType
* @return
*/
public static IPay createPay(String payType){
if(payType==null){
return null;
}else if(payType.equalsIgnoreCase("WECHAT_PAY")){
return new WeChatPay();
}else if (payType.equalsIgnoreCase("ALI_PAY")){
return new AliPay();
}
//如果可以复制 可以编写更多
return null;
}
}
测试简单支付用户工厂:
@Test
public void testSimplePayFactory(){
IPay wechat_pay = SimplePayFactory.createPay("WECHAT_PAY");
IPay ali_pay = SimplePayFactory.createPay("ALI_PAY");
wechat_pay.unifiedOrder();
ali_pay.unifiedOrder();
}
// 输出结果:
// 微信支付统一下单...
// 支付宝支付统一下单...
上述就说工厂设计模式 ------ 简单工厂(静态工厂的一个简单使用例子),那么我们来分析下其缺点与不足之处:
需求:
- 如果我需要额外再添加一个A银行的银行卡支付,那么就需要在SimplePayFactory类中添加响应的判断逻辑,比如在加上一个if判断,添加一个A银行支付的逻辑
- 而如果再需要一个B银行的银行卡支付,那么还需要再添加一个if判断添加一个B银行支付的逻辑,依次加下去。
- 那么这时就违背了工厂类所要遵循的开闭原则(Open Close Principle)对扩展开放,对修改关闭,程序需要进行拓展的时候,不难去修改原有的代码,实现一个热插拔的效果,这样就导致,每次扩展功能的时候都需要添加新的逻辑,并且需要对工厂类进行修改,如果是真实复杂的业务,这就增加了成本。
下面我们来看一个工厂方法模式是如何解决简单工厂模式的这一缺点:
工厂模式 ----------- 工厂方法模式
- 工厂方法模式
- 又称工厂模式,是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则
- 通过工厂父类定义负责创建产品的接口,通过子类来确定所需要创建的类型
- 相比简单工厂而言,此种方法具有更多可扩展性和复用性,同时也增强了代码的可读性
- 将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂中完成),即由子类来决定应该实例化哪一个类
- 核心组成:
- IProduct:抽象产品接口,描述所有实例所共有的公共接口
- Product:具体产品类,实现抽象类的接口,工厂类创建对象,如果需要多个定义多个
- IFactory:抽象工厂类接口,描述具体工厂的公共接口
- Factory:具体工厂类,实现创建产品类对象,实现抽象工厂类的接口,如果有多个定义多个
要实现工厂方法模式,只需要在原来简单工厂模式基础上,做出改进,而之前我们创建的IPay抽象产品接口和AliPay WeChatPay两个具体产品类不需要改动
首先创建IPayFactory抽象工厂接口:
/**
* @author wcc
* @date 2021/8/19 14:23
* @Description:抽象工厂接口
*/
public interface IPayFactory {
IPay getPay();
}
然后创建AliPayFactory和WechatFactory两个具体工厂类:
/**
* @author wcc
* @date 2021/8/19 14:26
* @Description:具体工厂类 AliPayFactory
*/
public class AliPayFactory implements IPayFactory{
@Override
public IPay getPay() {
return new AliPay();
}
}
/**
* @author wcc
* @date 2021/8/19 14:29
*/
public class WeChatFactory implements IPayFactory {
@Override
public IPay getPay() {
return new WeChatPay();
}
}
进行测试:
@Test
public void testMethodPayFactory(){
AliPayFactory aliPayFactory = new AliPayFactory();
IPay ali_pay = aliPayFactory.getPay();
ali_pay.unifiedOrder();// 输出:支付宝支付统一下单...
WeChatFactory weChatFactory = new WeChatFactory();
IPay wechat_pay = weChatFactory.getPay();
wechat_pay.unifiedOrder();// 输出:微信支付统一下单...
}
- 工厂方法模式优点:
- 符合开闭原则,增加一个产品类,只需要实现对其他具体的产品类和具体的工厂类
- 符合单一职责原则,每个工厂只负责生产对应的产品
- 使用者只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则:
- 迪米特法则:最少知道原则,实体应当尽量少地与其他实体之间发生相互作用
- 依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体
- 里氏替换原则:俗称LSP,任何基类可以出现地地方,子类一定可以出现,对实现抽象化地具体步骤地规范
- 工厂模式的缺点:
- 每增加一个产品,需要实现对应的具体工厂类和具体产品类
- 每个产品需要有对应的具体工厂和具体产品类
工厂模式 ----------- 抽象工厂方法模式
抽象工厂方法模式是简单模式和工厂方法模式的整合升级版
- 工厂模式有三种不同的实现方式:
- 简单工厂模式:通过传入相关的类型来返回相应的类,这种方式比较单一,可扩展性相对较差
- 工厂方法模式:通过实现类实现相应的方法来决定相应的返回结果,这种方式的可扩展性比较强
- 抽象工厂模式:基于上述两种模式的拓展,是工厂方法模式的升级版,当需要创建的产品有多个产品线时使用抽象工厂模式是比较好的选择
- 抽象工厂模式在Spring中应用的最为广泛的一种设计模式
- 背景:
- 工厂方法模式引入公司等级结构,解决了简单工厂模式中工厂类职责过重的问题
- 但工厂方法模式中每个工厂只创建一类具体类的对象,后续发展可能会导致工厂类过多,因此将相关的具体类组成一个具体类族,由同一个工厂来统一生产,强调的是一系列相关的产品对象
- 实现步骤:
- 定义两个接口IPay(支付)、IRefund(退款)
- 创建具体的Pay产品、创建具体的Refund产品
- 创建抽象工厂IOrderFactory接口里面两个方法createPay/createRefund
- 创建支付宝产品族AliOrderFactory,实现OrderFactory抽象工厂
- 创建微信支付品族WeChatOrderFactory,实现OrderFactory抽象工厂
- 定义一个超级工厂创造器FactoryProceducer,通过传递参数获取对应的工厂
接下来我们就按照步骤使用一下抽象工厂方法模式:
1.定义两个接口IphoneProduct(手机产品)、IRouterProduct(路由器产品)
/**
* @author wcc
* @date 2021/8/19 16:46
* @Description:手机产品接口
*/
public interface IphoneProduct {
void start();
void shutdown();
void callup();
void senMessage();
}
/**
* @author wcc
* @date 2021/8/19 16:50
* @Description:路由器产品接口
*/
public interface IRouterProduct {
void start();
void shutdown();
void openWifi();
void setting();
}
2.创建具体的手机品牌和路由器品牌
/**
* @author wcc
* @date 2021/8/19 16:53
*/
public class HuaWeiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("开启华为手机");
}
@Override
public void shutdown() {
System.out.println("关闭华为手机");
}
@Override
public void callup() {
System.out.println("华为手机打电话");
}
@Override
public void senMessage() {
System.out.println("华为手机发短信");
}
}
/**
* @author wcc
* @date 2021/8/19 16:55
*/
public class HuaWeiRouter implements IRouterProduct {
@Override
public void start() {
System.out.println("启动华为路由器");
}
@Override
public void shutdown() {
System.out.println("关闭华为路由器");
}
@Override
public void openWifi() {
System.out.println("打开华为wifi");
}
@Override
public void setting() {
System.out.println("设置华为wifi");
}
}
/**
* @author wcc
* @date 2021/8/19 16:52
*/
public class XiaomiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("开启小米手机");
}
@Override
public void shutdown() {
System.out.println("关闭小米手机");
}
@Override
public void callup() {
System.out.println("小米手机打电话");
}
@Override
public void senMessage() {
System.out.println("小米手机发短信");
}
}
/**
* @author wcc
* @date 2021/8/19 16:54
*/
public class XiaomiRouter implements IRouterProduct {
@Override
public void start() {
System.out.println("启动小米路由器");
}
@Override
public void shutdown() {
System.out.println("关闭小米路由器");
}
@Override
public void openWifi() {
System.out.println("小米打开wifi");
}
@Override
public void setting() {
System.out.println("小米设置路由器");
}
}
3.创建抽象工厂IProductFactory,里面两个方法iphoneProduct()和irouterProduct()分别用以生产手机和路由器
/**
* @author wcc
* @date 2021/8/19 16:58
* @Description:抽象产品工厂
*/
public interface IProductFactory {
//生产手机和路由器
IphoneProduct iphoneProduct();
IRouterProduct irouterProduct();
}
4.创建华为品牌工厂和小米品牌工厂
/**
* @author wcc
* @date 2021/8/19 17:01
*/
public class HuaWeiFactory implements IProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new HuaWeiPhone();
}
@Override
public IRouterProduct irouterProduct() {
return new HuaWeiRouter();
}
}
/**
* @author wcc
* @date 2021/8/19 17:00
*/
public class XiaomiFactory implements IProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new XiaomiPhone();
}
@Override
public IRouterProduct irouterProduct() {
return new XiaomiRouter();
}
}
5.创建一个超级工厂创造器,通过传递参数获取对应的工厂
public class FactoryProducer {
public static IProductFactory getFactory(String type){
if (type.equalsIgnoreCase("huawei")){
return new HuaWeiFactory();
}else if (type.equalsIgnoreCase("xiaomi")){
return new XiaomiFactory();
}
return null;
}
}
抽象工厂模式和工厂方法模式的区别:
-
工厂方法模式:定义一个用于创建对象工厂的接口,让子类去决定实例化哪一个类
-
抽象工厂模式:为创建一组相关或者相互依赖的对象提供一个接口,而且无需指定他们的具体类
-
区别在于产品,如果产品单一,最合适用工厂方法模式,但是如果又多个业务品种、业务分类的时候,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。在通俗深话理解下:工厂模式针对的是一个产品等级结构,抽象工厂模式针对的是面向多个产品等级的结构
-
再来看看工厂方法模式与抽象工厂模式的对比
三种模式: -
简单工厂模式:
用来生产同一等级结构中的任意产品(对于新增加的产品,需要覆盖已有的代码)
-
工厂方法模式
用来生产同一等级结构中的固定产品(支持增加任意产品)
-
抽象工厂模式
围绕一个超级工厂创建其他厂,该超级工厂又称为其他工厂的工厂