工厂模式
工厂模式是开发中常用的一种设计模式,每一种设计模式都会极大的解决程序设计方面的问题,工厂模式也是一样,本文将会用通俗的语言来解释什么是工厂模式?工厂模式的种类、代码示例、每种工厂模式的优缺点和工厂模式适用的场景。
为什么要是使用工厂模式?
首先我们用一个生动故事来描述下什么是工厂模式,这会让你更快的理解工厂模式,为后面理解的工厂模式的几种实现方式打下基础。
假如,你需要让公司开一个收入证明为自己贷款买房提供收入证明,一般开收入证明的过程是:
- 打印收入证明;
- 在收入证明上盖上公司的章;
贯穿整个过程,可以知道,你需要创建一个收入证明,并使用收入证明为贷款买房提供资料。这个过程中,有两个关键的行为:创建收入证明,使用收入证明。创建收入证明的过程中又分为两步:打印、盖章。不熟悉这个流程的同事在创建收入证明的时候,往往会遇到很多麻烦导致开收入证明频频受阻,于是公司决定把员工开收入证明这件事做个优化,以前需要两步做的事,现在只需要发一份邮件给财务部门,财务部门就会在下班之前就会把盖好章的收入证明送到工位,这就是生活中遇到的工厂模式
。
说了这么多,工厂模式
究竟解决了生活中的哪些问题呢?
在这个生活案例中,它让员工创建收入证明这件事情变得更加简单,员工不需要知道收入证明是怎么创建的,只需要发一份邮件给财务部门,财务部门就会帮助员工打印并盖章。而且在后续的公司发展中,如果需要在收入证明中盖上更多的章,员工也不需要自己取熟悉整个流程,拿着收入证明跑断腿的到各个部门去盖章。
那么,在程序的世界中,如何使用代码演示员工为贷款买房,去公司开收入证明这个行为呢?又是如何用代码展示工厂模式的呢?
类比上面的例子,假如有个收入证明类
创建只需要new一下就行,入参是打印证明
和给证明盖章
,如果后续收入证明的盖章变了,需要修改入参。这就需要改大量调用创建收入证明类
的new代码。
于是这时工厂模式就可以使用了,工厂模式将类的创建和类的使用分离出来,当 Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。
那又有人说,也可以把这些创建过程的代码放到类的构造函数里,同样可以降低重复率,而且构造函数本身的作用也是初始化对象。针对这个观点,我们可以对比下工厂模式相较于构造函数的优点:
优点:
- 静态工厂方法有名字而构造函数没有,因为工厂方法可以有多个,通过名字来区别各个方法,但构造函数名字只能有一个,只能通过参数来区别,所以使用静态工厂方法更明了。
- 静态工厂方法支持条件性实例化,就是说你创建对象时,有时需要添加一些条件判断是否应该创建,如果满足条件则返回一个实例,不满足则返回NULL,比如单例模式。构造函数时做不到这样的,构造函数的功能需要保持单一,只为了构造而存在,而静态工厂方法可以很简单的做到。
- 方法能够返回返回类型的子类型
这就是工厂模式在创建对象上的一些优点,而工厂模式最核心的知识点是:将对象的创建和使用做分离。请默念三遍。
抓住了核心点,再去了解工厂模式的各种实现就简单的多了,工厂模式一般可以分为三种:
- 简单/静态工厂模式
- 工厂方法模式
- 抽象工厂模式
我们学习步骤按照:工厂方法模式 到 简单静态工厂模式 到 抽象工厂模式,示例都非常简单,一看就懂,文中的代码我放在github上感兴趣的同学可以下载:
github地址: java23种设计模式代码示例--工厂模式
我知道大家都是爱心人士,白嫖当然不是你们的习惯,欢迎大家来一波素质三连:关注、点赞、点star
工厂方法模式
工厂方法相对比较和简单静态工厂相对简单比较容易理解。
如果我们需要在工厂里造一台手机,那么先定义一个phone抽象类,手机必须要有打电话功能,加一个call抽象方法
代码语言:txt复制package com.shuai.design.factory.normals;
public abstract class Phone {
// 所有的手机必须要有打电话的功能
public abstract void call();
}
创建一个手机的工厂接口,里面有个createPhone方法,其他类型手机都要继承这个接口,创建手机必须实现这个方法
代码语言:txt复制package com.shuai.design.factory.normals;
import com.shuai.design.factory.simples.Phone;
public interface PhoneFactory {
Phone createPhone();
}
创建小米手机类。继承phone,实现call方法
代码语言:txt复制package com.shuai.design.factory.normals;
import com.shuai.design.factory.simples.Phone;
public class MiPhone extends Phone {
@Override
public void call() {
System.out.println("this is MI call");
}
}
创建小米手机工厂
代码语言:txt复制package com.shuai.design.factory.normals;
public class MiPhoneFactory implements PhoneFactory {
@Override
public MiPhone createPhone() {
return new MiPhone();
}
}
类似小米手机,实现华为手机类,继承phoen抽象类,实现call方法
代码语言:txt复制package com.shuai.design.factory.normals;
import com.shuai.design.factory.simples.Phone;
public class HuaWeiPhone extends Phone {
@Override
public void call() {
System.out.println("this is HuaWei Phone");
}
}
实现华为手机工厂
代码语言:txt复制package com.shuai.design.factory.normals;
public class HuaWeiPhoneFactory implements PhoneFactory{
@Override
public HuaWeiPhone createPhone() {
return new HuaWeiPhone();
}
}
简单静态工厂模式
静态工厂相对于工厂方法模式简单的多,首先创建phone的抽象类
代码语言:txt复制package com.shuai.design.factory.simples;
public abstract class Phone {
// 所有的手机必须要有打电话的功能
public abstract void call();
}
再创建phone的构建工厂类,根据传入的类型创建不同类型的手机
代码语言:txt复制package com.shuai.design.factory.simples;
public class PhoneFactory {
public static MiPhone createMiPhone() {
return new MiPhone();
}
public static HuaWeiPhone createHuaWeiPhone() {
return new HuaWeiPhone();
}
public static Phone createPhone(String type) {
if ("Mi".equals(type)) {
return new MiPhone();
} else {
return new HuaWeiPhone();
}
}
}
实现小米手机类
代码语言:txt复制package com.shuai.design.factory.simples;
public class MiPhone extends Phone {
@Override
public void call() {
System.out.println("this is MI call");
}
}
实现华为手机类
代码语言:txt复制package com.shuai.design.factory.simples;
public class HuaWeiPhone extends Phone {
@Override
public void call() {
System.out.println("this is HuaWei Phone");
}
}
抽象工厂模式
定义:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类
图为:
这里还是用创建手机的方式来实现抽象工厂,先创建一个phone的抽象类,里面不仅有call方法,还有手机的屏幕类型
代码语言:txt复制package com.shuai.design.factory.abstracts;
public abstract class Phone {
// 所有的手机必须要有打电话的功能
public abstract void call();
// 手机的屏幕类型
public abstract void screenType();
}
再创建一个手机构建工厂的父接口
代码语言:txt复制package com.shuai.design.factory.abstracts;
public interface PhoneFactory {
Phone createMiPhone();
Phone createHuaWeiPhone();
}
首先所有小米手机和华为手机都必须实现通话功能,重写call方法
代码语言:txt复制package com.shuai.design.factory.abstracts;
public abstract class MiPhone extends Phone {
@Override
public void call() {
System.out.println("this is MI call");
}
}
代码语言:txt复制package com.shuai.design.factory.abstracts;
public abstract class HuaWeiPhone extends Phone {
@Override
public void call() {
System.out.println("this is HuaWei Phone");
}
}
在根据手机的屏幕类型分为小米折叠屏手机:
代码语言:txt复制package com.shuai.design.factory.abstracts;
public class FoldingScreenMiPhone extends MiPhone {
@Override
public void screenType() {
System.out.println("this is 小米折叠屏手机");
}
}
在根据手机的屏幕类型分为华为折叠屏手机:
代码语言:txt复制package com.shuai.design.factory.abstracts;
public class FoldingScreenHuaWeiPhone extends HuaWeiPhone {
@Override
public void screenType() {
System.out.println("this is 华为折叠屏手机");
}
}
在根据手机的屏幕类型分为小米曲面屏屏手机:
代码语言:txt复制package com.shuai.design.factory.abstracts;
public class CurvedScreenMiPhone extends MiPhone {
@Override
public void screenType() {
System.out.println("this is 小米曲面屏手机");
}
}
在根据手机的屏幕类型分为华为曲面屏手机:
代码语言:txt复制package com.shuai.design.factory.abstracts;
public class CurvedScreenHuaWeiPhone extends HuaWeiPhone{
@Override
public void screenType(){
System.out.println("this is 华为曲面屏手机");
}
}
针对不同的屏幕类型手机,抽象出根据屏幕类型的构建工厂,有曲面屏手机工厂和折叠屏手机工厂:
代码语言:txt复制package com.shuai.design.factory.abstracts;
//曲面屏手机工厂
public class CurvedScreenPhoneFactory implements PhoneFactory {
@Override
public CurvedScreenMiPhone createMiPhone() {
return new CurvedScreenMiPhone();
}
@Override
public CurvedScreenHuaWeiPhone createHuaWeiPhone() {
return new CurvedScreenHuaWeiPhone();
}
}
折叠屏手机工厂
代码语言:txt复制package com.shuai.design.factory.abstracts;
//折叠屏手机工厂
public class FoldingScreenPhoneFactory implements PhoneFactory{
@Override
public FoldingScreenMiPhone createMiPhone() {
return new FoldingScreenMiPhone();
}
@Override
public FoldingScreenHuaWeiPhone createHuaWeiPhone() {
return new FoldingScreenHuaWeiPhone();
}
}
再写一个测试类测试一下结果:
代码语言:txt复制package com.shuai.design.factory.abstracts;
public class Test {
public static void main(String[] args) {
// 创建一个华为曲面屏手机
CurvedScreenHuaWeiPhone curvedScreenHuaWeiPhone = new CurvedScreenPhoneFactory().createHuaWeiPhone();
System.out.println("curvedScreenHuaWeiPhone 的手机类型为:");
curvedScreenHuaWeiPhone.screenType();
// 创建一个小米曲面屏手机
CurvedScreenMiPhone curvedScreenMiPhone = new CurvedScreenPhoneFactory().createMiPhone();
System.out.println("curvedScreenMiPhone 的手机类型为:");
curvedScreenMiPhone.screenType();
// 创建一个华为折叠屏手机
FoldingScreenHuaWeiPhone foldingScreenHuaWeiPhone = new FoldingScreenPhoneFactory().createHuaWeiPhone();
System.out.println("foldingScreenHuaWeiPhone 的手机类型为:");
foldingScreenHuaWeiPhone.screenType();
// 创建一个小米折叠屏手机
FoldingScreenMiPhone foldingScreenMiPhone = new FoldingScreenPhoneFactory().createMiPhone();
System.out.println("foldingScreenMiPhone 的手机类型为:");
foldingScreenMiPhone.screenType();
}
}
抽象工厂模式的使用场景:一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式。通过工厂类,只要知道工厂类是谁,我就能创建出一个需要的对象
缺点 :
扩展产品族困难。比如在phone中类型增加一个带手写笔类型的手机,那么每个已经实现的手机类就都需要实现这个方法。这严重违反了开闭原则。
优点:
增加等级简单。如果在折叠屏手机下增加一个双折叠屏和三折叠屏的手机这就比较简单,只需要在折叠屏手机构建工厂下面修改就行。
总结
实际上,一般开发过程中,我们使用简单工厂模式比较多,抽象工厂模式的话需要业务比较大的情况下才会用到。如果你有更好的观点,欢迎在评论区提出,互相学习。
参考资料:
- 《工厂模式理解了没有?(https://segmentfault.com/a/1190000014949595)》
- 《设计模式系列-Builder模式,工厂方法模式和抽象工厂模式》
- 《产品等级结构与产品族》