抽象工廠模式
用途
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
From: Design Patterns: Elements of Reusable Object-Oriented Software
提供一個(gè)接口,使得當(dāng)創(chuàng)建一系列的相關(guān)或者相互依賴的對(duì)象時(shí),不需要一一指定它們具體的類。
例子
下面是一個(gè)圖形界面工具包的例子。像窗口、滾動(dòng)條等組件,支持不同的樣式。樣式可以由界面的不同主題決定。這樣,如果在整個(gè)應(yīng)用中寫死不同主題的不同按鈕樣式,非常不好維護(hù)。
為了解決這個(gè)問題,使應(yīng)用便于在各個(gè)主題之間切換移植。我們可以定義一個(gè)抽象的WidgetFactory類,在其中定義創(chuàng)建每一種組件的接口。同樣地,對(duì)于每一種組件,我們也有一個(gè)抽象類,每一種具體主題下的組件都是這個(gè)類的子類。WidgetFactory類中創(chuàng)建組件的接口就是創(chuàng)建一個(gè)新的組件實(shí)例。
這樣,客戶端調(diào)用WidgetFactory類的接口來得到各種組件的實(shí)例,但是,客戶端并不知道獲得的具體組件類是哪個(gè)。因此,就實(shí)現(xiàn)了調(diào)用方和具體主題的解耦。如下圖。

從這里可以看出,抽象工廠類主要用于產(chǎn)生一系列相關(guān)的類或者一類對(duì)象。使用了抽象工廠之后,如果想要切換獲得對(duì)象的類別或者系列,只需要改動(dòng)抽象工廠的實(shí)例即可。通過這樣,就改變了所有獲得對(duì)象的主題,從而實(shí)現(xiàn)了調(diào)用方和具體對(duì)象的解耦。
說明
AbstractFactory defers creation of product objects to its ConcreteFactory subclass.
From: Design Patterns: Elements of Reusable Object-Oriented Software
抽象工廠將產(chǎn)品對(duì)象的創(chuàng)建下放到了具體的工廠子類中。
脫離了具體的場(chǎng)景之后,抽象工廠類的示意圖如下:

使用抽象工廠,可以用來控制產(chǎn)品的一致性。當(dāng)多個(gè)產(chǎn)品被設(shè)置為一個(gè)系列之后,應(yīng)用使用同一個(gè)系列內(nèi)的產(chǎn)品是很重要的。抽象工廠模式使這種控制變得容易。
使用抽象工廠的壞處是,支持新的產(chǎn)品比較麻煩。因?yàn)樵诔橄蠊S的接口中已經(jīng)固定了支持哪些產(chǎn)品,新增支持新的產(chǎn)品就需要修改這個(gè)接口。
例子
AbstractFactory.Cpu:
package AbstractFactory;
/**
* Created by chengxia on 2019/8/16.
*/
public abstract class Cpu {
private String name;
public Cpu(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
AbstractFactory.PhoneCpu:
package AbstractFactory;
/**
* Created by chengxia on 2019/8/16.
*/
public class PhoneCpu extends Cpu {
public PhoneCpu(String name) {
super(name);
}
}
AbstractFactory.ComputerCpu:
package AbstractFactory;
/**
* Created by chengxia on 2019/8/16.
*/
public class ComputerCpu extends Cpu {
public ComputerCpu(String name) {
super(name);
}
}
AbstractFactory.Screen:
package AbstractFactory;
/**
* Created by chengxia on 2019/8/16.
*/
public abstract class Screen {
private String name;
public Screen(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
AbstractFactory.PhoneScreen:
package AbstractFactory;
/**
* Created by chengxia on 2019/8/16.
*/
public class PhoneScreen extends Screen {
public PhoneScreen(String name) {
super(name);
}
}
AbstractFactory.ComputerScreen:
package AbstractFactory;
/**
* Created by chengxia on 2019/8/16.
*/
public class ComputerScreen extends Screen {
public ComputerScreen(String name) {
super(name);
}
}
AbstractFactory.AbstractComponentFactory:
package AbstractFactory;
/**
* Created by chengxia on 2019/8/16.
*/
public class AbstractComponentFactory {
public String getCPU(){
return "Abstract CPU.";
}
public String getScreen(){
return "Abstract Screen.";
}
}
AbstractFactory.PhoneComponentFactory:
package AbstractFactory;
/**
* Created by chengxia on 2019/8/16.
*/
public class PhoneComponentFactory extends AbstractComponentFactory{
@Override
public String getCPU(){
return "Qualcomm Snapdragon CPU: 820, 821, 845, 855 ...";
}
@Override
public String getScreen(){
return "Computer Screen: JDI LED, BOE OLED, Samsung AMOLED ...";
}
}
AbstractFactory.ComputerComponentFactory:
package AbstractFactory;
/**
* Created by chengxia on 2019/8/16.
*/
public class ComputerComponentFactory extends AbstractComponentFactory{
@Override
public String getCPU(){
return "Computer CPU: i3, i5, i7 ...";
}
@Override
public String getScreen(){
return "Computer Screen: Samsung LCD ...";
}
}
類圖如下:

下面如果有手機(jī)或者電腦廠商調(diào)用上面的類的時(shí)候,可以如下:
AManufacturer.Xiaomi:
package AManufacturer;
import AbstractFactory.AbstractComponentFactory;
import AbstractFactory.PhoneComponentFactory;
/**
* Created by chengxia on 2019/8/19.
*/
public class Xiaomi {
public static void main(String []args){
AbstractComponentFactory ab = new PhoneComponentFactory();
System.out.println(ab.getCPU());
System.out.println(ab.getScreen());
}
}
AManufacturer.Lenovo:
package AManufacturer;
import AbstractFactory.AbstractComponentFactory;
import AbstractFactory.ComputerComponentFactory;
/**
* Created by chengxia on 2019/8/19.
*/
public class Lenovo {
public static void main(String []args){
AbstractComponentFactory ab = new ComputerComponentFactory();
System.out.println(ab.getCPU());
System.out.println(ab.getScreen());
}
}
Builder模式
用途
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
From: Design Patterns: Elements of Reusable Object-Oriented Software
將復(fù)雜對(duì)象的創(chuàng)建,從其表示中剝離。同樣的創(chuàng)建過程,會(huì)創(chuàng)建出不同的對(duì)象。
例子
一個(gè)RTF富文本閱讀器,應(yīng)該能夠?qū)⒏晃谋靖袷睫D(zhuǎn)化為各種文本格式,如ASCII、純文本等。所以,目標(biāo)格式多種多樣,而且不確定。所以,設(shè)計(jì)上,應(yīng)該讓添加一種格式轉(zhuǎn)化非常容易,最好不用修改reader本身的代碼。
一種解決辦法是,讓RTFReader類調(diào)用TextConverter對(duì)象(該對(duì)象負(fù)責(zé)將RTF轉(zhuǎn)化為另一種文本表示)。每當(dāng)RTFReader認(rèn)出一個(gè)RTF標(biāo)記符時(shí),它向TextConverter發(fā)起調(diào)用來轉(zhuǎn)化這個(gè)標(biāo)記。TextConverter對(duì)象既負(fù)責(zé)格式轉(zhuǎn)化,也負(fù)責(zé)將標(biāo)記表示成特定的格式。各種具體格式的轉(zhuǎn)化和表示,通過TextConverter的子類來實(shí)現(xiàn)。如下圖:

從上圖可以看出,每一個(gè)Converter子類,實(shí)現(xiàn)了具體的復(fù)雜對(duì)象的創(chuàng)建和組裝,將這些復(fù)雜的邏輯隱藏在了抽象接口之后,和調(diào)用方reader隔離開來。這樣,reader只負(fù)責(zé)解析RTF文檔就可以了。
這里每一個(gè)converter類稱為builder,而reader稱為director。這個(gè)例子中,Builder模式實(shí)現(xiàn)了RTF解析算法和RTF表示格式創(chuàng)建的分離。這讓我們只需要配置不同的TextConverter子類就能夠復(fù)用RTFReader的解析邏輯。
說明
Builder模式適用于:
- the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled.(創(chuàng)建復(fù)雜對(duì)象的算法應(yīng)該和對(duì)象組裝表示的過程分離。)
- the construction process must allow different representations for the object that's constructed.(創(chuàng)建過程必須兼容對(duì)象的不同的表示形式,也就是不同的子類。)
拋除具體場(chǎng)景之后,示意圖如下:

進(jìn)一步說明
上面關(guān)于Builder模式的說明是來自《Design Patterns: Elements of Reusable Object-Oriented Software》。里面提到的例子不是特別好理解。可以參考維基百科的說明。
Builder模式主要解決如下問題:
- How can a class (the same construction process) create different representations of a complex object?(一個(gè)類如何創(chuàng)建不同表示的復(fù)雜對(duì)象。)
- How can a class that includes creating a complex object be simplified?(如何讓一個(gè)創(chuàng)建復(fù)雜對(duì)象的類變得簡(jiǎn)單?)
直接在類內(nèi)部創(chuàng)建和組裝一個(gè)復(fù)雜對(duì)象是非常不靈活的,這樣會(huì)導(dǎo)致類內(nèi)部有大量創(chuàng)建復(fù)雜對(duì)象表示的代碼,不修改類無法修改對(duì)象的表示。
Builder模式如下解決這個(gè)問題:
- Encapsulate creating and assembling the parts of a complex object in a separate object.(將復(fù)雜對(duì)象的創(chuàng)建和組裝封裝在一個(gè)單獨(dú)的對(duì)象中)
- A class delegates object creation to a Builder object instead of creating the objects directly.(一個(gè)類將對(duì)象創(chuàng)建的過程委托給一個(gè)單獨(dú)的Builder對(duì)象,而不是自己直接創(chuàng)建。)
Builder模式的初衷就是將復(fù)雜對(duì)象的創(chuàng)建和表示分離開來,通過這樣,可以用同一個(gè)創(chuàng)建過程創(chuàng)建成不同表示的對(duì)象。(The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.)
Builder模式的優(yōu)勢(shì):
- Allows you to vary a product’s internal representation.(允許你創(chuàng)建一個(gè)產(chǎn)品的不同內(nèi)部表示)
- Encapsulates code for construction and representation.(實(shí)現(xiàn)將創(chuàng)建對(duì)象和表示對(duì)象的代碼封裝)
- Provides control over steps of construction process.(實(shí)現(xiàn)對(duì)象構(gòu)件過程的逐步控制)
Builder模式的劣勢(shì):
- Requires creating a separate ConcreteBuilder for each different type of product.(對(duì)于每一種不同的對(duì)象,都需要一個(gè)單獨(dú)的Builder。)
- Requires the builder classes to be mutable.(需要Builder類是可變的)。
- Data members of class aren't guaranteed to be initialized.(類的數(shù)據(jù)成員不保證都被初始化)
- Dependency injection may be less supported.(對(duì)于依賴注入的支持不好。)
維基百科給出了如下例子:
WikiBuilder.Car:
package WikiBuilder;
/**
* Created by chengxia on 2019/8/21.
*/
public class Car {
int wheel;
String color;
public Car(int wheel, String color) {
this.wheel=wheel;
this.color=color;
}
@Override
public String toString() {
return "Car [wheels = " + wheel+ ", color = " + color + "]";
}
}
WikiBuilder.CarBuilder:
package WikiBuilder;
/**
* The builder abstraction.
* */
public class CarBuilder {
int wheel;
String color;
public CarBuilder setWheel(int wheel){
this.wheel=wheel;
return this;
}
public CarBuilder setColor(String color) {
this.color=color;
return this;
}
public Car getCar() {
return new Car(wheel,color);
}
}
WikiBuilder.Client:
package WikiBuilder;
/**
* Created by chengxia on 2019/8/21.
*/
public class Client {
public static void main(String[] arg) {
Car c= new CarBuilder().setWheel(4).setColor("Black").getCar();
System.out.println(c);
}
}
運(yùn)行上面的測(cè)試類,輸出如下:
Car [wheels = 4, color = Black]
Process finished with exit code 0
Builder模式和靜態(tài)內(nèi)部類
builder模式的作用將一個(gè)復(fù)雜對(duì)象的構(gòu)建與他的表示分離,使用者可以一步一步的構(gòu)建一個(gè)比較復(fù)雜的對(duì)象。因此,在好多時(shí)候,我們可以通過創(chuàng)建一個(gè)靜態(tài)內(nèi)部類(區(qū)別于一般的內(nèi)部類,靜態(tài)內(nèi)部類屬于所嵌入的外部類,而不是所嵌入外部類的對(duì)象,可以直接通過外部類訪問實(shí)例化,不需要依賴于外部對(duì)象),來實(shí)現(xiàn)Builder模式。如下:
StaticInnerBuilder.Product:
package StaticInnerBuilder;
/**
* Created by chengxia on 2019/8/22.
*/
public class Product {
private final int id;
private final String name;
private final int type;
private final float price;
private Product(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.type = builder.type;
this.price = builder.price;
}
@Override
public String toString(){
return "Builder{id:"+id+", name:"+name+", type:"+type+", price:"+price+"}";
}
public static class Builder {
private int id;
private String name;
private int type;
private float price;
public Builder id(int id) {
this.id = id;
return this;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder type(int type) {
this.type = type;
return this;
}
public Builder price(float price) {
this.price = price;
return this;
}
public Product build() {
return new Product(this);
}
}
}
StaticInnerBuilder.Test:
package StaticInnerBuilder;
/**
* Created by chengxia on 2019/8/22.
*/
public class Test {
public static void main(String []args){
Product p = new Product.Builder().id(1).name("Test").type(3).price(3.14f).build();
System.out.println(p);
}
}
從這個(gè)例子中,可以看出,這樣寫的時(shí)候,可以在構(gòu)造對(duì)象的時(shí)候采用鏈?zhǔn)綄懛?,比較有意思。運(yùn)行前面的測(cè)試類輸出如下:
Builder{id:1, name:Test, type:3, price:3.14}
Process finished with exit code 0
工廠方法模式
用途
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
From: Design Patterns: Elements of Reusable Object-Oriented Software
定義一個(gè)創(chuàng)建對(duì)象的接口,但是讓子類去決定實(shí)例化哪個(gè)具體類。工廠方法讓一個(gè)類將實(shí)例化的過程下放到子類中。
例子
以一個(gè)能夠開發(fā)給用戶展示多種Document的應(yīng)用框架為例。這個(gè)框架的兩個(gè)重要的類是Application和Document,這兩個(gè)類都是抽象的,使用時(shí)必須要根據(jù)具體的應(yīng)用場(chǎng)景做具體的實(shí)現(xiàn)。當(dāng)我們開發(fā)一個(gè)繪圖應(yīng)用時(shí),我們要自己實(shí)現(xiàn)DrawingApplication和DrawingDocument。這個(gè)Application類負(fù)責(zé)管理Document,比如當(dāng)用戶點(diǎn)擊菜單新建時(shí),需要?jiǎng)?chuàng)建一個(gè)新的Document類。
由于具體的要實(shí)例化哪個(gè)Application子類,是由具體要實(shí)現(xiàn)的應(yīng)用確定的。Application類并不知道要實(shí)例化哪個(gè)Document子類,他只知道什么時(shí)候創(chuàng)建,僅此而已。這就進(jìn)退兩難了:框架需要實(shí)例化類,但是它只知道抽象類,但是這個(gè)類不能被實(shí)例化。
這時(shí)候,工廠方法提供了一種解決方案:它對(duì)創(chuàng)建哪個(gè)Document類的子類做了封裝,然后將這一過程移到框架外。(It encapsulates the knowledge of which Document subclass to create and moves this knowledge out of the framework.)如下圖。

Application subclasses redefine an abstract CreateDocument operation on Application to return the appropriate Document subclass. Once an Application subclass is instantiated, it can then instantiate application-specific Documents
without knowing their class. We call CreateDocument a factory method because it's
responsible for "manufacturing" an object.
Application類的子類重新寫了一個(gè)Application的CreateDocument方法,用來返回一個(gè)合適的Document子類。一旦這個(gè)Application類的子類實(shí)例化完成,它就能夠?qū)嵗虾踹@個(gè)Application子類的Document類,而不用知道具體的它們(Document類)具體的類。
我們之所以叫CreateDocument工廠方法,是因?yàn)樗?fù)責(zé)“生產(chǎn)”對(duì)象。
說明
工廠方法模式適用于:
- 一個(gè)類不能預(yù)測(cè)它將要?jiǎng)?chuàng)建的類對(duì)象時(shí)。
- 一個(gè)類想讓它的子類確定具體實(shí)例化哪個(gè)類。
- classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate.(這句話翻不了,直接拿過來吧)
拋除具體場(chǎng)景后,類圖如下:

工廠方法使得無需在代碼中綁定具體應(yīng)用的類,在代碼中,只需和Product接口打交道。因此,能夠適用于任何用戶自定義的具體ConcreteProduct類。
工廠方法一個(gè)不好的地方在于,用戶必須要?jiǎng)?chuàng)建一個(gè)Creator類的子類,來創(chuàng)建一個(gè)具體的ConcreteProduct對(duì)象。如果本來就有這樣的繼承層次還好,否則有些麻煩。
前面的例子中,工廠方法只通過Creator調(diào)用。但是,有時(shí)候,會(huì)出現(xiàn)類的平行繼承關(guān)系。如下:

在這個(gè)例子中,通過工廠方法,實(shí)際上連接了兩個(gè)平行的繼承層次:Figure和Manipulator。工廠方法定義了兩個(gè)類層次之間的聯(lián)系,通過它,實(shí)際上實(shí)現(xiàn)了類之間聯(lián)系的內(nèi)聚。
例子
代碼如下:
FactoryMethod.Shape:
package FactoryMethod;
/**
* Created by chengxia on 2019/8/19.
*/
public abstract class Shape {
public String name;
public Shape(String aName){
name = aName;
}
//畫shape
public abstract void draw();
//擦去shape
public abstract void erase();
}
FactoryMethod.Square:
package FactoryMethod;
/**
* Created by chengxia on 2019/8/19.
*/
//方形子類
public class Square extends Shape {
//構(gòu)造函數(shù)
public Square(String aName){
super(aName);
}
public void draw() {
System.out.println("It will draw a Square.");
}
public void erase() {
System.out.println("It will erase a Square.");
}
}
FactoryMethod.Circle:
package FactoryMethod;
/**
* Created by chengxia on 2019/8/19.
*/
// 圓形子類
public class Circle extends Shape {
// 構(gòu)造函數(shù)
public Circle(String aName){
super(aName);
}
public void draw() {
System.out.println("It will draw a Circle.");
}
public void erase() {
System.out.println("It will erase a Circle.");
}
}
FactoryMethod.ShapeFactory:
package FactoryMethod;
/**
* Created by chengxia on 2019/8/19.
*/
public abstract class ShapeFactory {
protected abstract Shape factoryMethod(String aName);
//在operOnShape中定義對(duì)Shape的一系列使用操作
public void operOnShape(String aName){
Shape s = factoryMethod(aName);
System.out.println("The current using shape is: " + s.name);
s.draw();
s.erase();
}
}
FactoryMethod.SquareFactory:
package FactoryMethod;
/**
* Created by chengxia on 2019/8/19.
*/
//定義返回Square實(shí)例的SquareFactory
public class SquareFactory extends ShapeFactory {
//重載factoryMethod方法,返回Circle對(duì)象
protected Shape factoryMethod(String aName) {
return new Square(aName + " (created by SquareFactory)");
}
}
FactoryMethod.CircleFactory:
package FactoryMethod;
/**
* Created by chengxia on 2019/8/19.
*/
//定義返回Circle 實(shí)例的CircleFactory
public class CircleFactory extends ShapeFactory {
//重載factoryMethod方法,返回Circle對(duì)象
protected Shape factoryMethod(String aName) {
return new Circle(aName + " (created by CircleFactory)");
}
}
類圖如下:
[圖片上傳失敗...(image-7f4400-1566435759539)]
接下來寫一個(gè)測(cè)試類,來調(diào)動(dòng)上面的程序結(jié)構(gòu),如下。
FactoryMethod.TestFactoryMethod:
package FactoryMethod;
/**
* Created by chengxia on 2019/8/19.
*/
public class TestFactoryMethod {
public static void main(String[] args){
ShapeFactory sf1 = new SquareFactory();
ShapeFactory sf2 = new CircleFactory();
sf1.operOnShape("Shape one");
sf2.operOnShape("Shape two");
}
}
從這個(gè)例子可以看出,ShapeFactory實(shí)際上將實(shí)例化Shape的職能下放到了子類SquareFactory和CircleFactory。這樣,它就只需要關(guān)注具體Shape實(shí)例的行為,關(guān)注對(duì)于Shape實(shí)例的操作,而無需關(guān)注該實(shí)例是如何實(shí)例化的。
運(yùn)行上面的測(cè)試?yán)樱Y(jié)果如下:
The current using shape is: Shape one (created by SquareFactory)
It will draw a Square.
It will erase a Square.
The current using shape is: Shape two (created by CircleFactory)
It will draw a Circle.
It will erase a Circle.
Process finished with exit code 0
參考資料
- Design Patterns: Elements of Reusable Object-Oriented Software
- 在 Java 中應(yīng)用設(shè)計(jì)模式 - Factory Method
- Builder pattern - Wikipedia
- Java設(shè)計(jì)模式之builder模式