前言:
为什么之前写过Golang 版的设计模式,还在重新写Java 版?
答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。
为什么类图要附上uml
因为很多人学习有做笔记的习惯,如果单纯的只是放一张图片,那么学习者也只能复制一张图片,可复用性较低,附上uml,方便有新理解时,快速出新图。
工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题。但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是本章将要学习的抽象工厂模式的基本思想。
为了更好地理解抽象工厂模式,这里先引入如下两个概念:
(1)产品等级结构。产品等级结构即产品的继承结构,例如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
(2)产品族。在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。例如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。
抽象工厂模式为创建一组对象提供了一种解决方案。与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品。
抽象工厂模式定义如下:抽象工厂模式(Abstract Factory Pattern
):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。
@startuml abstract class 抽象产品族1海尔{} class 产品1电视机 extends 抽象产品族1海尔 {} class 产品2洗衣机 extends 抽象产品族1海尔 {} abstract class 抽象产品族2{} abstract class 抽象工厂 { + 生产抽象产品族1():抽象产品族1海尔 + 生产抽象产品族2():抽象产品族2 } class 具体工厂1 extends 抽象产品族1海尔 implements 抽象工厂 { + 生产产品1():抽象产品族1海尔 + 生产产品2():抽象产品族1海尔 } 具体工厂1 -up-> 产品1电视机 具体工厂1 -up-> 产品2洗衣机 @enduml
在抽象工厂模式结构图中包含以下4个角色。
(1)AbstractFactory
(抽象工厂):它声明了一组用于创建一族产品的方法,每个方法对应一种产品。
(2)ConcreteFactory
(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每种产品都位于某个产品等级结构中。
(3)AbstractProduct
(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
(4)ConcreteProduct
(具体产品):它定义具体工厂生产的具体产品对象,实现在抽象产品接口中声明的业务方法。
代码实现
public class AbstractFactoryDemo { // 抽象产品族1 static abstract class AbstractProductFamily1 {} // 产品1 static class Product1 extends AbstractProductFamily1 {} // 产品2 static class Product2 extends AbstractProductFamily1 {} // 抽象产品族2 static abstract class AbstractProductFamily2 {} // 抽象工厂 interface AbstractFactory { AbstractProductFamily1 createProductFamily1(); AbstractProductFamily2 createProductFamily2(); } // 具体工厂1 static class ConcreteFactory1 implements AbstractFactory { @Override public AbstractProductFamily1 createProductFamily1() { return new Product1(); } @Override public AbstractProductFamily2 createProductFamily2() { // 在具体工厂1中创建抽象产品族2的实例 return null; // 根据具体情况进行实现 } } public static void main(String[] args) { // 创建具体工厂1的实例 AbstractFactory factory = new ConcreteFactory1(); // 使用具体工厂1创建抽象产品族1的实例 AbstractProductFamily1 product1 = factory.createProductFamily1(); AbstractProductFamily1 product2 = factory.createProductFamily1(); System.out.println(product1); System.out.println(product2); // 使用具体工厂1创建抽象产品族2的实例 AbstractProductFamily2 product3 = factory.createProductFamily2(); System.out.println(product3); } }
抽象工厂总结
抽象工厂模式的主要优点如下:
(1)抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了在抽象工厂中声明的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
(2)当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
(3)增加新的产品族很方便,无须修改已有系统,符合开闭原则。
抽象工厂模式的主要缺点是:
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则。
适用场景
在以下情况下可以考虑使用抽象工厂模式:
(1)一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
(2)系统中有多于一个的产品族,而每次只使用其中某一个产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
(3)属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束。例如同一操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统的,此时具有一个共同的约束条件:操作系统的类 型。
(4)产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。