Java萌新面试常问设计模式——工厂模式
Posted 兴趣使然的草帽路飞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java萌新面试常问设计模式——工厂模式相关的知识,希望对你有一定的参考价值。
- 临近秋招,备战暑期实习,祝大家每天进步亿点点!Day17
- 本篇总结的是 工厂设计模式,后续会每日更新~
1、工厂模式简介
- ⼯⼚模式介绍:它提供了⼀种创建对象的最佳⽅式,在创建对象时 不会对客户端暴露创建逻辑,并且是通过使⽤⼀个共同的接⼝来指向新创建的对象。
- 例⼦:
- ⼯⼚⽣产电脑,除了 A 品牌、还可以⽣产B、C、D品牌电脑;
- 业务开发中,⽀付很常⻅,⾥⾯有统⼀下单和⽀付接 ⼝,具体的⽀付实现可以微信、⽀付宝、银⾏卡等;
- ⼯⼚模式有 3 种不同的实现⽅式:
- ① 简单⼯⼚模式(静态工厂):通过传⼊相关的类型来返回相应的类,这 种⽅式⽐较单 ⼀,可扩展性相对较差。
- ② ⼯⼚⽅法模式:通过实现类实现相应的⽅法来决定相应的返回结果,这种⽅式的可扩展性⽐较强。
- ③ 抽象⼯⼚模式:基于上述两种模式的拓展,且⽀持细化产品。
- 应⽤场景:
- 解耦:分离职责,把复杂对象的创建和使⽤的过程分开。
- 复⽤代码 降低维护成本:如果对象创建复杂且多处需⽤到,如果每处都进⾏编写,则很多重复代码,如果业务逻辑发⽣了改 变,需⽤四处修改;使⽤⼯⼚模式统⼀创建,则只要修改⼯⼚类即可, 降低成本。
2、工厂模式——简单工厂模式
-
⼜称静态⼯⼚⽅法,可以根据参数的不同返回不同类的实例,专⻔定义⼀个类来负责创建其他类的实例,被创建的实例通常都具有共同的⽗类。由于⼯⼚⽅法是静态⽅法,可通过类名直接调⽤,⽽且只需要传⼊简单的参数即可。
-
核⼼组成:
- Factory:⼯⼚类,简单⼯⼚模式的核⼼,它负责实现 创建所有实例的内部逻辑。
- IProduct:抽象产品类,简单⼯⼚模式所创建的所有对象的⽗类,描述所有实例所共有的公共接⼝。
- Product:具体产品类,是简单⼯⼚模式的创建⽬标。
-
实现步骤:
- 创建抽象产品类,⾥⾯有产品的抽象⽅法,由具体的产 品类去实现。
- 创建具体产品类,继承了他们的⽗类,并实现具体⽅法。
- 创建⼯⼚类,提供了⼀个静态⽅法
createXXX()
⽤来⽣产产品,只需要传⼊你想产品名称。
-
优点:将对象的创建和对象本身业务处理分离可以降低系统的 耦合度,使得两者修改起来都相对容易。
-
缺点:
- ⼚类的职责相对过重,增加新的产品需要修改⼯⼚类的判断逻辑,这⼀点与开闭原则是相违背。
- 即开闭原则(Open Close Principle)对扩展开放,对修改关闭,程序需要进⾏拓展的时候,不能去修改原有的代码,实现⼀个热插拔的效果。
- 将会增加系统中类的个数,在⼀定程度上增加了系统的复杂度和理解难度,不利于系统的扩展和维护,创建简单对象就不⽤模式。
下面我们来简单使用一下简单(静态)工厂设计模式:
我们简单使用伪代码模拟一下支付流程:
创建 IProduct 抽象产品接口——IPay:
/**
* @Auther: csp1999
* @Date: 2020/11/07/11:00
* @Description: IPay 抽象统一支付下单接口
*/
public interface IPay {
/**
* 统一下单
*/
void unifiedOrder();
}
创建 Product 具体产品类——AliPay/WeChatPay:
AliPay.java
/**
* @Auther: csp1999
* @Date: 2020/11/07/11:29
* @Description: 支付宝支付具体实现类
*/
public class AliPay implements IPay{
@Override
public void unifiedOrder() {
System.out.println("支付宝支付统一下单...");
}
}
WeChatPay.java
/**
* @Auther: csp1999
* @Date: 2020/11/07/11:30
* @Description: 微信支付具体实现类
*/
public class WeChatPay implements IPay{
@Override
public void unifiedOrder() {
System.out.println("微信支付统一下单...");
}
}
创建Factory工厂类——SimplePayFactory:
/**
* @Auther: csp1999
* @Date: 2020/11/07/11:31
* @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)(对扩展开放,对修改关闭,程序需要进⾏拓展的时候,不能去修改原有的代码,实现⼀个热插拔的效果),这样就导致,每次扩展功能的时候都需要添加新的逻辑,并且需要对工厂类进行修改,如果是真实复杂的业务,这就增加了成本。
下面我们来看一下工厂方法模式是如何解决简单工厂模式的这一缺点:
3、工厂模式——工厂方法模式
- ⼜称⼯⼚模式,是对简单⼯⼚模式的进⼀步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满⾜开闭原则。
- 通过⼯⼚⽗类定义负责创建产品的公共接⼝,通过⼦类来确定所需要创建的类型。
- 相⽐简单⼯⼚⽽⾔,此种⽅法具有更多的可扩展性和复⽤性,同时也增强了代码的可读性。
- 将类的实例化(具体产品的创建)延迟到⼯⼚类的⼦类 (具体⼯⼚)中完成,即由⼦类来决定应该实例化哪⼀ 个类。
- 核⼼组成:
- IProduct:抽象产品接口,描述所有实例所共有的公共接⼝。
- Product:具体产品类,实现抽象产品类的接⼝,⼯⼚ 类创建对象,如果有多个需要定义多个。
- IFactory:抽象⼯⼚接口,描述具体⼯⼚的公共接⼝。
- Factory:具体⼯⼚类,实现创建产品类对象,实现抽 象⼯⼚类的接⼝,如果有多个需要定义多个。
要实现工厂方法模式,只需要在原来的简单工厂模式基础上,做出改进,而之前我们创建的IPay
抽象产品接口和AliPay
、WeChatPay
两个具体产品类不需要改动。
首先创建IPayFactory
抽象⼯⼚接口:
/**
* @Auther: csp1999
* @Date: 2020/11/07/15:08
* @Description: 抽象⼯⼚接口
*/
public interface IPayFactory {
IPay getPay();
}
然后创建AliPayFactory
和WeChatFactory
两个具体⼯⼚类:
/**
* @Auther: csp1999
* @Date: 2020/11/07/15:09
* @Description: 具体工厂类 AliPayFactory
*/
public class AliPayFactory implements IPayFactory{
@Override
public IPay getPay() {
return new AliPay();
}
}
/**
* @Auther: csp1999
* @Date: 2020/11/07/15:16
* @Description: 具体工厂类 WeChatFactory
*/
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();// 输出:微信支付统一下单...
}
工厂方法模式思路如下图:
工厂方法模式优点:
- 符合开闭原则,增加⼀个产品类,只需要实现其他具体的产品类和具体的⼯⼚类;
- 符合单⼀职责原则,每个⼯⼚只负责⽣产对应的产品;
工厂方法模式缺点:
- 增加⼀个产品,需要实现对应的具体⼯⼚类和具体产品类;
- 每个产品需要有对应的具体⼯⼚和具体产品类;
4、工厂模式——抽象工厂方法模式
抽象⼯⼚⽅法模式是简单工厂模式 和工厂方法模式的整合升级版。象⼯⼚模式在 Spring 中应⽤得最为⼴泛的⼀种设计模式。
背景:
- ⼯⼚⽅法模式引⼊⼯⼚等级结构,解决了简单⼯⼚模式中⼯⼚类职责过重的问题。
- 但⼯⼚⽅法模式中每个⼯⼚只创建⼀类具体类的对象, 后续发展可能会导致⼯⼚类过多,因此将⼀些相关的具体类组成⼀个“具体类族”,由同⼀个⼯⼚来统⼀⽣产, 强调的是⼀系列相关的产品对象!!!
实现步骤:
- 1、定义两个接⼝
IPay
(支付)、IRefund
(退款)。 - 2、创建具体的
Pay
产品、创建具体的Refund
产品。 - 3、创建抽象⼯⼚
IOrderFactory
接⼝ ⾥⾯两个⽅法createPay/createRefund。
- 4、创建⽀付宝产品族
AliOderFactory
,实现OrderFactory
抽象⼯⼚。 - 5、创建微信⽀付产品族
WechatOderFactory
,实现OrderFactory
抽象⼯⼚。 - 6、定义⼀个超级⼯⼚创造器
FactoryProducer
,通过传递参数获取对应的⼯⼚。
接下来我们就按照步骤使用一下抽象工厂方法模式:
1、定义两个接⼝ IPay(支付)、IRefund(退款):
/**
* @Auther: csp1999
* @Date: 2020/11/07/16:06
* @Description: 支付抽象接口
*/
public interface IPay {
/**
* 统一下单
*/
void unifiedOrder();
}
/**
* @Auther: csp1999
* @Date: 2020/11/07/16:07
* @Description: 退款抽象接口
*/
public interface IReFund {
/**
* 退款
*/
void refund();
}
2、创建具体的 Pay 产品、创建具体的 Refund 产品:
AliPay/WeChatPay:支付宝支付和微信支付
/**
* @Auther: csp1999
* @Date: 2020/11/07/11:29
* @Description: 支付宝支付具体实现类
*/
public class AliPay implements IPay {
@Override
public void unifiedOrder() {
System.out.println("支付宝支付 统一下单接口...");
}
}
/**
* @Auther: csp1999
* @Date: 2020/11/07/11:30
* @Description: 微信支付具体实现类
*/
public class WeChatPay implements IPay {
@Override
public void unifiedOrder() {
System.out.println("微信支付统一下单...");
}
}
AliRefund/WeChatFund:支付宝退款和微信退款
/**
* @Auther: csp1999
* @Date: 2020/11/07/16:35
* @Description:
*/
public class AliRefund implements IReFund {
@Override
public void refund() {
System.out.println("支付宝退款...");
}
}
/**
* @Auther: csp1999
* @Date: 2020/11/07/16:40
* @Description:
*/
public class WeChatRefund implements IReFund {
@Override
public void refund() {
System.out.println("微信支付退款...");
}
}
3、创建抽象⼯⼚ IOrderFactory 接⼝ ⾥⾯两个⽅法 createPay/createRefund:
/**
* @Auther: csp1999
* @Date: 2020/11/07/16:04
* @Description: 订单抽象工厂,一个超级工厂可以创建其他工厂(又被称为其他工厂的工厂)
*/
public interface IOrderFactory {
IPay createPay();
IReFund createRefund();
}
4、创建⽀付宝产品族AliOderFactory,实现OrderFactory 抽象⼯⼚:
/**
* @Auther: csp1999
* @Date: 2020/11/07/16:18
* @Description:
*/
public class AliOrderFactory implements IOrderFactory {
@Override
public IPay createPay() {
return new AliPay();
}
@Override
public IReFund createRefund() {
return new AliRefund();
}
}
5、创建微信⽀付产品族WechatOderFactory,实现 OrderFactory抽象⼯⼚
/**
* @Auther: csp1999
* @Date: 2020/11/07/16:39
* @Description:
*/
public class WeChatOrderFactory implements IOrderFactory {
@Override
public IPay createPay() {
return new WeChatPay();
}
@Override
public IReFund createRefund() {
return new WeChatRefund();
}
}
6、定义⼀个超级⼯⼚创造器FactoryProducer,通过传递参数获取对应的⼯⼚
/**
* @Auther: csp1999
* @Date: 2020/11/07/16:46
* @Description: 工厂创造器
*/
public class FactoryProducer {
public static IOrderFactory getFactory(String type){
if (type.equalsIgnoreCase("WECHAT")){
return new WeChatOrderFactory();
}else if (type.equalsIgnoreCase("ALI")){
return new AliOrderFactory();
}
return null;
}
}
最后我们来进行测试:
@Test
public void testAbstractMethodPayFactory(){
IOrderFactory wechatPayFactory = FactoryProducer.getFactory("WECHAT");
wechatPayFactory.createPay().unifiedOrder();
wechatPayFactory.createRefund().refund();
IOrderFactory aliPayFactory = FactoryProducer.getFactory("ALI");
aliPayFactory.createPay().unifiedOrder();
aliPayFactory.createRefund().refund();
}
结果如下:
微信支付统一下单...
微信支付退款...
支付宝支付 统一下单接口...
支付宝退款...
⼯⼚⽅法模式和抽象⼯⼚⽅法模式
- 从上面案例可以看出,抽象工厂模式是工厂方法模式和静态工厂模式的合并,即超级工厂使用的是简单工厂模式,受超级工厂管辖的工厂使用的是工厂方法模式。
- 如图:
总结的面试题也挺费时间的,文章会不定时更新,有时候一天多更新几篇,如果帮助您复习巩固了知识点,还请三连支持一下,后续会亿点点的更新!
为了帮助更多小白从零进阶 Java 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm
,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,有兴趣的小伙伴可以了解一下,当然,不管怎样博主的文章一直都是免费的~
以上是关于Java萌新面试常问设计模式——工厂模式的主要内容,如果未能解决你的问题,请参考以下文章