3.2、工厂模式
(1)简介
- 定义:
工厂模式大体分为简单工厂、工厂方法、抽象工厂等三种模式。工厂方法模式也可称为工厂模式,与抽象模式都是属于GOF23种设计模式中的一员;简单工厂模式不属于23 种经典设计模式,它的缺点是增加新产品时会违背“开闭原则”。可以大概理解为:简单工厂进阶变成了工厂方法,然后再进阶成了抽象工厂。难度逐步增加,也越来越抽象。下面按先易到难逐个分析。 - 优点:
1、将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,明确了职责。
2、把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则,面向接口编程,而不是面向实现编程。 - 缺点:
1、由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
2、要新增产品类的时候,就要修改工厂类的代码,违反了开放封闭原则(对扩展的开放,对修改的关闭)。
3、简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
3.2.1、简单工厂模式
属于创建型模式,又叫做静态工厂方法模式,不属于23种GOF设计模式之一。是由一个工厂对象决定创建出哪一种产品类的实例。实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。
作用:将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。
主要角色
工厂:负责实现创建所有实例的内部逻辑,并提供一个外界调用的方法,创建所需的产品对象。
抽象产品:负责描述产品的公共接口
具体产品:描述生产的具体产品。
例子:
“假设”有一台饮料机(工厂),可以调出各种口味的饮料(抽象产品),有三个按钮(参数)对应这三种饮料(具体产品)。这时候你可以根据点击按钮来选择你喜欢的饮料。
/**
* @ Product.java
* 抽象产品
* 描述产品的公共接口
*/
abstract class Product {
//产品介绍
abstract void intro();
}
/**
* @ AProduct.java
* 具体产品A
* (可以看成是一种饮料:可乐)
*/
public class AProduct extends Product{
@Override
void intro() {
System.out.println("可乐");
}
}
/**
* @ BProduct.java
* @具体产品B
* @(可以看成是一种饮料:奶茶)
*/
public class BProduct extends Product{
@Override
void intro() {
System.out.println("奶茶");
}
}
/**
* @ CProduct.java
* 具体产品C
* (可以看成是一种饮料:咖啡)
*/
public class CProduct extends Product{
@Override
void intro() {
System.out.println("咖啡");
}
}
Factory.java
/**
* 工厂
* 负责实现创建所有实例的内部逻辑,并提供一个外界调用的方法,创建所需的产品对象。
*/
public class Factory {
/**
* 供外界调用的方法
* (可以看成是对外提供的三种按钮)
* @param type
* @return 产品实例
*/
public static Product getProduct(String type) {
switch (type) {
case "A":
return new AProduct();
case "B":
return new BProduct();
case "C":
return new CProduct();
default:
return null;
}
}
}
测试
public class Test {
public static void main(String[] args) {
//创建具体的工厂
Factory factory = new Factory();
//根据传入的参数生产不同的产品实例
//(按下不同的按钮,获取饮料)
Product A = Factory.getProduct("A");
A.intro();
Product B = Factory.getProduct("B");
B.intro();
Product C = Factory.getProduct("C");
C.intro();
}
}
运行结果:可乐 奶茶 咖啡
根据例子可以描述为:一个抽象产品类,可以派生出多个具体产品类。一个具体工厂类,通过往此工厂的static方法中传入不同参数,产出不同的具体产品类实例。
- 优点:将创建使用工作分开,不必关心类对象如何创建,实现了解耦;
- 缺点:违背“开放 -关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
3.2.2、工厂方法模式
又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。一种常用的对象创建型设计模式,此模式的核心精神是封装 类中不变的部分。
作用:将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。
主要角色
抽象工厂:描述具体工厂的公共接口
具体工厂:描述具体工厂,创建产品的实例,供外界调用
抽象产品:负责描述产品的公共接口
具体产品:描述生产的具体产品
例子:
“假设”有各类的饮料机(抽象工厂),可以调出各种的饮料(抽象产品)。但是一类饮料机(具体工厂),只能生产一种饮料(具体产品)。如果你需要喝可乐,就需要买可乐饮料机。
产品:Product.java 、ProductA.java 、ProductB.java
/**
* @ Product.java
* 抽象产品
*/
abstract class Product {
//产品介绍
abstract void intro();
}
/**
* @ ProductA.java
* 具体产品A
*/
public class ProductA extends Product{
@Override
void intro() {
System.out.println("饮料A");
}
}
/**
* @ ProductB.java
* 具体产品B
*/
public class ProductB extends Product{
@Override
void intro() {
System.out.println("饮料B");
}
}
工厂:Factory.java、FactoryA.java 、FactoryB.java
/**
* @ Factory.java
* 抽象工厂
*/
abstract class Factory {
//生产产品
abstract Product getProduct();
}
/**
* @ FactoryA.java
* 具体工厂A
* 负责具体的产品A生产
*/
public class FactoryA extends Factory{
@Override
Product getProduct() {
return new ProductA();
}
}
/**
* @ FactoryB.java
* @具体工厂B
* 负责具体的产品B生产
*/
public class FactoryB extends Factory{
@Override
Product getProduct() {
return new ProductB();
}
}
测试:Test.java
public class Test {
public static void main(String[] args) {
//创建具体的工厂
FactoryA factoryA = new FactoryA();
//生产相对应的产品
factoryA.getProduct().intro();
FactoryB factoryB = new FactoryB();
factoryB.getProduct().intro();
}
}
根据例子可以描述为:一个抽象产品类,可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。每个具体工厂类只能创建一个具体产品类的实例。
优点:
符合开-闭原则:新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
符合单一职责原则:每个具体工厂类只负责创建对应的产品
缺点:
增加了系统的复杂度:类的个数将成对增加
增加了系统的抽象性和理解难度
一个具体工厂只能创建一种具体产品
补充:
- a)适用场景特点:客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。 客户不关心创建产品的细节,只关心产品的品牌。
总的来说,凡是出现了大量的对象需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在大多数情况下,我们会选用第三种——静态工厂方法模式(将工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可)。 - b)扩展:当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式
3.2.3、抽象工厂模式
- 定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。
- 解决的问题:每个工厂只能创建一类产品(工厂方法模式)
- 抽象工厂模式与工厂方法模式最大的区别:抽象工厂中每个工厂可以创建多种类的产品;而工厂方法每个工厂只能创建一类
主要对象
抽象工厂:描述具体工厂的公共接口
具体工厂:描述具体工厂,创建产品的实例,供外界调用
抽象产品族:描述抽象产品的公共接口
抽象产品:描述具体产品的公共接口
具体产品:具体产品
举个简单易懂的例子:(找了个不怎么好的比喻,看不懂得可以看相关推荐链接)
“假设”有各类的自动售卖机(抽象工厂),可以出售各类食品(抽象产品族)。
有饮料、零食(抽象产品),比如常见的零食售卖机(具体工厂),出售矿泉水与面包(具体产品)。
产品:Product、ProductA、ProductB、ProductAa、ProductBb
/**
* @ Product.java
* 抽象产品族 (食品)
*/
abstract class Product {
//产品介绍
abstract void intro();
}
/**
* @ ProductA.java
* 抽象产品 (饮料)
*/
abstract class ProductA extends Product{
@Override
abstract void intro();
}
/**
* @ ProductB.java
* 抽象产品 (零食)
*/
abstract class ProductB extends Product{
@Override
abstract void intro();
}
/**
* @ ProductAa.java
* 具体产品 (矿泉水)
*/
public class ProductAa extends ProductA{
@Override
void intro() {
System.out.println("矿泉水");
}
}
/**
* @ ProductBb.java
* 抽象产品 (面包)
*/
public class ProductBb extends ProductB{
@Override
void intro() {
System.out.println("面包");
}
}
工厂:Factory.java、FactoryA.java
/**
* @ Factory.java
* 抽象工厂
*/
abstract class Factory {
//生产饮料
abstract Product getProductA();
//生产零食
abstract Product getProductB();
}
/**
* @ FactoryA.java
* 具体工厂A
* 负责具体的A类产品生产
*/
public class FactoryA extends Factory{
@Override
Product getProductA() {
//生产矿泉水
return new ProductAa();
}
@Override
Product getProductB() {
//生产面包
return new ProductBb();
}
}
测试:Test.java
public class Test {
public static void main(String[] args) {
//创建零食售卖机(具体工厂),
FactoryA factoryA = new FactoryA();
//获取矿泉水与面包(具体产品)
factoryA.getProductA().intro();
factoryA.getProductB().intro();
}
}
输出:矿泉水 面包
根据实例可以描述为: 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类可以创建多个具体产品类的实例。.
优点:
降低耦合
符合开-闭原则
符合单一职责原则
不使用静态工厂方法,可以形成基于继承的等级结构。
缺点:难以扩展新种类产品
总的来说,前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机、计算机软件学院只培养计算机软件专业的学生等。
同种类称为同等级,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等,这就是抽象工厂模式考虑的。
补充:
a)适用场景特点
当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
b)扩展
抽象工厂模式的扩展有一定的“开闭原则”倾斜性:
当增加一个新的产品族时只需增加一个新的具体工厂,不需要修改原代码,满足开闭原则。
当产品族中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则。
另一方面,当系统中只存在一个等级结构的产品时,抽象工厂模式将退化到工厂方法模式。
3.2.4、总结
角色不同:
1、简单工厂:具体工厂、抽象产品、具体产品
2、工厂方法:抽象工厂、具体工厂、抽象产品、具体产品
3、抽象工厂:抽象工厂、具体工厂、抽象产品族、抽象产品、具体产品
定义:
1、简单工厂:由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(继承自一个父类或接口)的实例。
2、工厂方法:定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象
3、抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。
对比:
1、工厂方法模式解决了简单工厂模式的“开放 - 关闭原则
2、抽象工厂模式解决了工厂方法模式一个具体工厂只能创建一类产品