本文仅为设计模式学习笔记,并非原创总结
# 工厂模式【常用】
-
一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。不过,在 GoF 的《设计模式》一书中,它将简单工厂模式看作是工厂方法模式的一种特例,所以工厂模式只被分成了工厂方法和抽象工厂两类。实际上,前面一种分类方法更加常见,所以,在今天的讲解中,我们沿用第一种分类方法。
-
在这三种细分的工厂模式中,简单工厂、工厂方法原理比较简单,在实际的项目中也比较常用。而抽象工厂的原理稍微复杂点,在实际的项目中相对也不常用。所以,我们今天讲解的重点是前两种工厂模式。对于抽象工厂,稍微了解一下即可。
# 简单工厂(Simple Factory)
# 含义
我们把被创建的对象称为 “产品”,把创建产品的对象称为 “工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫 “简单工厂模式”。
在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。
简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式。简单工厂模式不在 GoF 23 种设计模式之列。
简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了 “开闭原则”。
# 解决的问题
将 “类实例化的操作” 与 “使用对象的操作” 分开,让使用者不用知道具体参数就可以实例化出所需要的 “产品” 类,从而避免了在客户端代码中显式指定,实现了解耦。
即使用者可直接消费产品而不需要知道其生产的细节
# 使用步骤
- 创建抽象产品类 & 定义具体产品的公共接口;
- 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
- 创建工厂类,通过创建静态方法根据传入不同参数从而创建不同具体产品类的实例;
- 外界通过调用工厂类的静态方法,传入不同参数从而创建不同具体产品类的实例
# 实例
下面我们使用手机生产来讲解该模式:
Phone 类:手机标准规范类 (AbstractProduct)
public interface Phone { | |
void make(); | |
} |
MiPhone 类:制造小米手机(Product1)
public class MiPhone implements Phone { | |
public MiPhone() { | |
this.make(); | |
} | |
@Override | |
public void make() { | |
// TODO Auto-generated method stub | |
System.out.println("make xiaomi phone!"); | |
} | |
} |
IPhone 类:制造苹果手机(Product2)
public class IPhone implements Phone { | |
public IPhone() { | |
this.make(); | |
} | |
@Override | |
public void make() { | |
// TODO Auto-generated method stub | |
System.out.println("make iphone!"); | |
} | |
} |
PhoneFactory 类:手机代工厂(Factory)
public class PhoneFactory { | |
public Phone makePhone(String phoneType) { | |
if(phoneType.equalsIgnoreCase("MiPhone")){ | |
return new MiPhone(); | |
} | |
else if(phoneType.equalsIgnoreCase("iPhone")) { | |
return new IPhone(); | |
} | |
return null; | |
} | |
} |
客户端演示:
public class Demo { | |
public static void main(String[] arg) { | |
PhoneFactory factory = new PhoneFactory(); | |
Phone miPhone = factory.makePhone("MiPhone"); // make xiaomi phone! | |
IPhone iPhone = (IPhone)factory.makePhone("iPhone"); // make iphone! | |
} | |
} |
# 优点
- 将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦;
- 把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。
# 缺点
- 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
- 违背 “开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
- 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
# 应用场景
- 客户如果只知道传入工厂类的参数,对于如何创建对象的逻辑不关心时;
- 当工厂类负责创建的对象(具体产品)比较少时。
# 工厂方法(Factory Method)
# 介绍
工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。
将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。
解决了简单工厂模式的缺点,即工厂一旦需要生产新产品就需要修改工厂类的方法逻辑,违背了 “开放 - 关闭原则
# 模式组成
组成(角色) | 关系 | 作用 |
---|---|---|
抽象产品(Product) | 具体产品的父类 | 描述具体产品的公共接口 |
具体产品(Concrete Product) | 抽象产品的子类;工厂类创建的目标类 | 描述生产的具体产品 |
抽象工厂(Creator) | 具体工厂的父类 | 描述具体工厂的公共接口 |
# 创建步骤
- 步骤 1: 创建抽象工厂类,定义具体工厂的公共接口;
- 步骤 2: 创建抽象产品类 ,定义具体产品的公共接口;
- 步骤 3: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
- 步骤 4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
- 步骤 5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
# 实例讲解
步骤 1: 创建抽象工厂类,定义具体工厂的公共接口
public interface Factory { | |
Phone makePhone(); | |
} |
步骤 2: 创建抽象产品类 ,定义具体产品的公共接口;
public interface Phone { | |
void make(); | |
} |
步骤 3: 创建具体产品类(继承抽象产品类), 定义生产的具体产品;
public class Iphone implements Phone{ | |
@Override | |
public void make() { | |
System.out.println("苹果手机制作"); | |
} | |
} | |
public class Miphone implements Phone{ | |
@Override | |
public void make() { | |
System.out.println("小米手机制造"); | |
} | |
} |
步骤 4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
public class MiFactory implements Factory{ | |
@Override | |
public Phone makePhone() { | |
return new Miphone(); | |
} | |
} |
步骤 5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
public class Main { | |
public static void main(String[] args) { | |
MiFactory miFactory = new MiFactory(); | |
IFactory iFactory = new IFactory(); | |
Phone phone1 = iFactory.makePhone(); | |
Phone phone = miFactory.makePhone(); | |
phone1.make(); | |
phone.make(); | |
} | |
} |
# 优点
更符合开 - 闭原则
新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
简单工厂模式需要修改工厂类的判断逻辑
符合单一职责原则
每个具体工厂类只负责创建对应的产品
简单工厂中的工厂类存在复杂的 switch 逻辑判断
不使用静态工厂方法,可以形成基于继承的等级结构。
简单工厂模式的工厂类使用静态工厂方法
总结:工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。
# 缺点
- 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到 DOM、反射等技术,增加了系统的实现难度。
- 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
- 一个具体工厂只能创建一种具体产品
# 应用场景
在了解了优缺点后,我总结了工厂方法模式的应用场景:
- 当一个类不知道它所需要的对象的类时
在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可; - 当一个类希望通过其子类来指定创建对象时
在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。 - 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
# 抽象工厂模式(Abstract Factory)
# 介绍
抽象工厂模式,即 Abstract Factory Pattern,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。
抽象工厂模式与工厂方法模式最大的区别:抽象工厂中每个工厂可以创建多种类的产品;而工厂方法每个工厂只能创建一类
# 解决的问题
每个工厂只能创建一类产品
即工厂方法模式的缺点
# 模式组成
组成(角色) | 关系 | 作用 |
---|---|---|
抽象产品族(AbstractProduct) | 抽象产品的父类 | 描述抽象产品的公共接口 |
抽象产品(Product) | 具体产品的父类 | 描述具体产品的公共接口 |
具体产品(Concrete Product) | 抽象产品的子类;工厂类创建的目标类 | 描述生产的具体产品 |
抽象工厂(Creator) | 具体工厂的父类 | 描述具体工厂的公共接口 |
具体工厂(Concrete Creator) | 抽象工厂的子类;被外界调用 | 描述具体工厂;实现 FactoryMethod 工厂方法创建产品的实例 |
# 使用步骤
步骤 1: 创建抽象工厂类,定义具体工厂的公共接口;
步骤 2: 创建抽象产品族类 ,定义抽象产品的公共接口;
步骤 3: 创建抽象产品类 (继承抽象产品族类),定义具体产品的公共接口;
步骤 4: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
步骤 5:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
步骤 6:客户端通过实例化具体的工厂类,并调用其创建不同目标产品的方法创建不同具体产品类的实例
# 实例
步骤 1: 创建抽象工厂类,定义具体工厂的公共接口
// 抽象工厂:农场类 | |
interface Farm { | |
public Animal newAnimal(); | |
public Plant newPlant(); | |
} |
步骤 2: 创建抽象产品类 ,定义具体产品的公共接口;
// 抽象产品:动物类 | |
interface Animal { | |
public void show(); | |
} | |
// 抽象产品:植物类 | |
interface Plant { | |
public void show(); | |
} |
步骤 3: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
class Horse implements Animal { | |
public Horse() { | |
} | |
public void show() { | |
} | |
} | |
class Cattle implements Animal { | |
} | |
class Fruitage implements Plant { | |
} |
步骤 5:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
class SGfarm implements Farm { | |
public Animal newAnimal() { | |
System.out.println("新牛出生!"); | |
return new Cattle(); | |
} | |
public Plant newPlant() { | |
System.out.println("蔬菜长成!"); | |
return new Vegetables(); | |
} | |
} | |
// 具体工厂:上饶农场类 | |
class SRfarm implements Farm { | |
public Animal newAnimal() { | |
System.out.println("新马出生!"); | |
return new Horse(); | |
} | |
public Plant newPlant() { | |
System.out.println("水果长成!"); | |
return new Fruitage(); | |
} | |
} |
步骤 6:客户端通过实例化具体的工厂类,并调用其创建不同目标产品的方法创建不同具体产品类的实例
# 总结
工厂方法模式或简单工厂关注的是单个产品对象的创建,比如创建 CPU 的工厂方法,它就只关心如何创建 CPU 的对象,而创建主板的工厂方法,就只关心如何创建主板对象。
这里要解决的问题是,要创建一系列的产品对象,而且这一系列对象是构建新的对象所需要的组成部分,也就是这一系列被创建的对象相互之间是有约束的。
解决这个问题的一个解决方案就是抽象工厂模式。在这个模式里面,会定义一个抽象工厂,在里面虚拟的创建客户端需要的这一系列对象,所谓虚拟的就是定义创建这些对象的抽象方法,并不去真的实现,然后由具体的抽象工厂的子类来提供这一系列对象的创建。这样一来可以为同一个抽象工厂提供很多不同的实现,那么创建的这一系列对象也就不一样了,也就是说,抽象工厂在这里起到一个约束的作用,并提供所有子类的一个统一外观,来让客户端使用。