3、创建型设计模式-工厂模式
工厂模式介绍:
- 它提供了一种创建对象的最佳方式,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
- 例子:
- 需要购买一辆车,不用管车辆如何组装,且可以购买不同类型的比如轿车、SUV、跑车,直接去4s店购买就行(4s店就是工厂)
- 工厂生产电脑,除了A品牌、还可以生产B、C、D品牌电脑
- 业务开发中,支付很常见,里面有统一下单和支付接口,具体的支付实现可以微信、支付宝、银行卡等
工厂模式有 3 种不同的实现方式
- 简单工厂模式:通过传入相关的类型来返回相应的类,这种方式比较单 一,可扩展性相对较差;
- 工厂方法模式:通过实现类实现相应的方法来决定相应的返回结果,这种方式的可扩展性比较强;
- 抽象工厂模式:基于上述两种模式的拓展,且支持细化产品
应用场景:
- 解耦:分离职责,把复杂对象的创建和使用的过程分开
- 复用代码 降低维护成本:
- 如果对象创建复杂且多处需用到,如果每处都进行编写,则很多重复代码,如果业务逻辑发生了改变,需用四处修改;
- 使用工厂模式统一创建,则只要修改工厂类即可,降低成本
简单工厂模式
- 又称静态工厂方法, 可以根据参数的不同返回不同类的实例,专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类
- 由于工厂方法是静态方法,可通过类名直接调用,而且只需要传入简单的参数即可
核心组成:
- Factory:工厂类,简单工厂模式的核心,它负责实现创建所有实例的内部逻辑
- IProduct:抽象产品类,简单工厂模式所创建的所有对象的父类,描述所有实例所共有的公共接口
- Product:具体产品类,是简单工厂模式的创建目标
实现步骤
- 创建抽象产品类,里面有产品的抽象方法,由具体的产品类去实现
- 创建具体产品类,继承了他们的父类,并实现具体方法
- 创建工厂类,提供了一个静态方法createXXX用来生产产品,只需要传入你想产品名称
优点:
- 将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。
缺点
- 工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背
- 即开闭原则(Open Close Principle)对扩展开放,对修改关闭,程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果
- 将会增加系统中类的个数,在一定程度上增加了系统的复杂度和理解难度,不利于系统的扩展和维护,创建简单对象就不用模式
文件目录:
项目文件目录
抽象类:
public interface Pay {
/**
* 统一下单
*/
void unifiedorder();
}
具体产品类-微信支付:
public class WechatPay implements Pay {
@Override
public void unifiedorder() {
System.out.println("微信支付支付 统一下单接口");
}
}
具体产品类-支付宝支付:
public class AliPay implements Pay {
@Override
public void unifiedorder() {
System.out.println("支付宝支付 统一下单接口");
}
}
工厂类:
public class SimplePayFactory {
/**
* 根据参数 返回对应的支付对象
*
* @param payType
* @return
*/
public static Pay 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;
}
}
Main入口:
public class Main {
public static void main(String[] args) {
//Pay pay = SimplePayFactory.createPay("WECHAT_PAY");
Pay pay = SimplePayFactory.createPay("ALI_PAY");
pay.unifiedorder();
}
}
工厂方法模式
-
又称工厂模式,是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则
-
通过工厂父类定义负责创建产品的公共接口,通过子类来确定所需要创建的类型
-
相比简单工厂而言,此种方法具有更多的可扩展性和复用性,同时也增强了代码的可读性
-
将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化哪一个类。
-
核心组成
- IProduct:抽象产品类,描述所有实例所共有的公共接口
- Product:具体产品类,实现抽象产品类的接口,工厂类创建对象,如果有多个需要定义多个
- IFactory:抽象工厂类,描述具体工厂的公共接口
- Factory:具体工场类,实现创建产品类对象,实现抽象工厂类的接口,如果有多个需要定义多个
代码:
/**
* 抽象工厂方法
*/
public interface PayFactory {
Pay getPay();
}
/**
* 具体产品工厂
*/
public class AliPayFactory implements PayFactory {
@Override
public Pay getPay() {
return new AliPay();
}
}
/**
* 抽象产品
*/
public interface Pay {
/**
* 统一下单
*/
void unifiedorder();
}
/**
* 具体产品
*/
public class AliPay implements Pay {
@Override
public void unifiedorder() {
System.out.println("支付宝支付 统一下单接口");
}
}
工厂方法
主代码:
public class Main {
public static void main(String[] args) {
PayFactory aliPayFactory = new AliPayFactory();
Pay aliPay = aliPayFactory.getPay();
aliPay.unifiedorder();
}
}
-
优点:
-
符合开闭原则,增加一个产品类,只需要实现其他具体的产品类和具体的工厂类;
-
符合单一职责原则,每个工厂只负责生产对应的产品
-
使用者只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则
- 迪米特法则:最少知道原则,实体应当尽量少地与其他实体之间发生相互作用
- 依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体
- 里氏替换原则:俗称LSP, 任何基类可以出现的地方,子类一定可以出现, 对实现抽象化的具体步骤的规范
-
-
缺点:
- 增加一个产品,需要实现对应的具体工厂类和具体产品类;
- 每个产品需要有对应的具体工厂和具体产品类
抽象工厂方法模式
工厂模式有 3 种不同的实现方式
- 简单工厂模式:通过传入相关的类型来返回相应的类,这种方式比较单 一,可扩展性相对较差;
- 工厂方法模式:通过实现类实现相应的方法来决定相应的返回结果,这种方式的可扩展性比较强;
- 抽象工厂模式:基于上述两种模式的拓展,是工厂方法模式的升级版,当需要创建的产品有多个产品线时使用抽象工厂模式是比较好的选择
- 抽象工厂模式在 Spring 中应用得最为广泛的一种设计模式
背景
- 工厂方法模式引入工厂等级结构,解决了简单工厂模式中工厂类职责过重的问题
- 但工厂方法模式中每个工厂只创建一类具体类的对象,后续发展可能会导致工厂类过多,因此将一些相关的具体类组成一个“具体类族”,由同一个工厂来统一生产,强调的是一系列相关的产品对象!!!
实现步骤:
1、定义两个接口 Pay、Refund
2、创建具体的Pay产品、创建具体的Refund产品
3、创建抽象工厂 OrderFactory 接口
里面两个方法 createPay/createRefund
4、创建支付宝产品族AliOderFactory,实现OrderFactory抽象工厂
5、创建微信支付产品族WechatOderFactory,实现OrderFactory抽象工厂
6、定义一个超级工厂创造器,通过传递参数获取对应的工厂
抽象工厂实现步骤
编码实践:
代码结构
超级工厂:
public interface OrderFactory {
PayFactory createPay();
RefundFactory createRefund();
}
产品族工厂的产品,可以不叫Factory,看公司团队规范,比如类名叫 IPay 也可以的
public interface PayFactory {
/**
* 统一下单
*/
void unifiedorder();
}
同理,退款工厂
public interface RefundFactory {
/**
* 退款
*/
void refund();
}
产品族工厂
public class AliOrderFactory implements OrderFactory {
@Override
public PayFactory createPay() {
return new AliPay();
}
@Override
public RefundFactory createRefund() {
return new AliRefund();
}
}
具体产品-支付
public class AliPay implements PayFactory {
@Override
public void unifiedorder() {
System.out.println("支付宝支付 统一下单接口");
}
}
具体产品-退款
public class AliRefund implements RefundFactory {
@Override
public void refund() {
System.out.println("支付宝 退款");
}
}
超级工厂生产器,传参生产对应的子工厂
public class FactoryProducer {
public static OrderFactory getFactory(String type) {
if (type.equalsIgnoreCase("WECHAT")) {
return new WechatOrderFactory();
} else if (type.equalsIgnoreCase("ALI")) {
return new AliOrderFactory();
}
return null;
}
}
Main函数调用:
public class Main {
public static void main(String[] args) {
OrderFactory orderFactory = FactoryProducer.getFactory("ALI");
orderFactory.createPay().unifiedorder();
orderFactory.createRefund().refund();
}
}
工厂方法模式和抽象工厂方法模式:
- 当抽象工厂模式中每一个具体工厂类只创建一个产品对象,抽象工厂模式退化成工厂方法模式;
优点:
- 当一个产品族中的多个对象被设计成一起工作时,它能保证使用方始终只使用同一个产品族中的对象
- 产品等级结构扩展容易,如果需要增加多一个产品等级,只需要增加新的工厂类和产品类即可, 比如增加银行支付、退款
缺点:
- 产品族扩展困难,要增加一个系列的某一产品,既要在抽象的工厂和抽象产品里修改代码,不是很符合开闭原则
- 增加了系统的抽象性和理解难度