GoF之工廠模式
@
- GoF之工廠模式
- 每博一文案
- 1. 簡單說明“23種設計模式”
- 1.2 介紹工廠模式的三種形態
- 1.3 簡單工廠模式(靜態工廠模式)
- 1.3.1 簡單工廠模式的優缺點:
- 1.4 工廠方法模式
- 1.4.1 工廠方法模式的優缺點:
- 1.5 抽象工廠模式
- 1.6 抽象工廠模式的優缺點:
- 2. 總結:
- 3. 最後:
每博一文案
蘇東波蘇軾《望江南 超然臺作》
休對故人思故國,且將薪火試新茶,詩酒趁年華
休對故人思故國:它告訴我們應當忘記過去。
且將薪火試新茶:又告訴我們要活好當下。
詩酒趁年華:更告訴我們既要面對未來,又要及時努力
1. 簡單說明“23種設計模式”
設計模式:一種可以被重複利用的解決方案。
GoF(Gang of Four),中文名——四人組。
《Design Patterns: Elements of Reusable Object-Oriented Software》(即《設計模式》一書),1995年由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著。這幾位作者常被稱為"四人組(Gang of Four)"。
該書中描述了23種設計模式。我們平常所說的設計模式就是指這23種設計模式。
不過除了GoF23種設計模式之外,還有其它的設計模式,比如:JavaEE的設計模式(DAO模式、MVC模式等)。
GoF23種設計模式可分為三大類:
一. 建立型(5個):解決物件建立問題。
- 單例模式
- 工廠方法模式
- 抽象工廠模式
- 建造者模式
- 原型模式
二. 結構型 (7個):一些類或物件組合在一起的經典結構
- 代理模式
- 裝飾模式
- 介面卡模式
- 組合模式
- 享元模式
- 外觀模式
- 橋接模式
三. 行為型 (11個):解決類或物件之間的互動問題。
- 策略模式
- 模板方法模式
- 責任鏈模式
- 觀察者模式
- 迭代子模式
- 命令模式
- 備忘錄模式
- 狀態模式
- 訪問者模式
- 中介者模式
- 直譯器模式
而這裡我們該篇的主角“工廠模式” 是解決物件建立問題的,所以工廠模式可以歸屬於創造型設計模式當中。
這裡說明一下,為什麼我們學習 Spring框架,突然就跑過來學習,工廠模式呢?
原因是:在Spring框架底層當中,使用了大量的工廠模式,而學習工廠模式,有利於我們更加透徹的學習Spring,更加了解Spring框架的底層原理,從而肆無忌憚的使用Spring。
1.2 介紹工廠模式的三種形態
工廠模式通常有三種形態:
- 第一種:簡單工廠模式(Simple Factory):不屬於23種設計模式之一。簡單工廠模式又叫做:靜態工廠方法模式(原因是其中定義的是一個static 靜態的方法,進行工廠處理生產的)。簡單工廠模式是工廠方法模式的一種特殊實現。
- 第二種:工廠方法模式(Factory Method):是23種設計模式之一。
- 第三種:抽象工廠模式(Abstract Factory):是23種設計模式之一。
1.3 簡單工廠模式(靜態工廠模式)
簡單工廠模式的角色包括三個:
- 抽象產品角色
- 具體產品角色
- 工廠類角色
簡單工廠模式的程式碼如下:
抽象產品角色:
抽象產品角色:武器產品,抽象的,自然就是抽象類了 abstract
作用: 用來定義武器的統一的所具備的功能(這裡我們給予上)——攻擊(武器都具備攻擊的功能)
/**
* 抽象產品角色:武器產品
* 抽象的,自然就是 抽象類了 abstract
*/
public abstract class Weapon {
/**
* 所以的武器都要具備攻擊
*/
public abstract void attack();
}
具體產品角色:
這裡我們定義兩個具體產品角色(有關武器的具體產品:Dagger 匕首,Tank 坦克)
同時具體產品角色都 extends繼承了 抽象產品角色Weapon,統一上,武器都具備攻擊 attack()
/**
* 具體產品: 匕首
* 同樣繼承 Weapon 抽象武器產品,保持同性
*/
public class Dagger extends Weapon{
@Override
public void attack() {
System.out.println("Dagger 匕首攻擊");
}
}
/**
* 具體產品角色:坦克
* 同時具體產品要繼承 抽象武器產品,保持武器的同性
*/
public class Tank extends Weapon {
@Override
public void attack() {
System.out.println("Tank 坦克攻擊");
}
}
工廠類角色:
抽象工廠類角色:根據不同的武器型別生產武器
注意: 該獲取武器的方法是——靜態方法(static),要獲取什麼產品,就看你傳什麼引數,傳Tank獲取坦克,傳Dagger獲取匕首,
簡單工廠模式中是透過定義一個靜態方法——獲取的,所以又被稱為:靜態工廠方法模式。
/**
* 抽象工廠類角色:
* 根據不同的武器型別生產武器
*/
public class WeaponFactory {
/*
靜態方法,要獲取什麼產品,就看你傳什麼引數,傳Tank獲取坦克,傳Dagger獲取匕首,
簡單工廠模式中有一個靜態方法,所以被稱為:靜態工廠方法模式。
*/
public static Weapon get(String type) {
// 引數不對,返回 null
if (null == type || type.length() == 0) {
return null;
}
// 要匕首產品,給匕首
if ("DAGGER".equals(type)) {
return new Dagger();
} else if ("TANK".equals(type)) { // 要坦克給產品,給坦克
return new Tank();
} else {
throw new RuntimeException("暫時不支援該武器的生產製造");
}
}
}
測試程式(客戶端程式):
package com.rainbowsea.test;
import com.rainbowsea.bean.Weapon;
import com.rainbowsea.bean.WeaponFactory;
public class Test {
public static void main(String[] args) {
// 我們要坦克
Weapon tank = WeaponFactory.get("TANK");
tank.attack(); // 坦克攻擊
// 我們要匕首,拜託武器工廠給我們製造
Weapon dagger = WeaponFactory.get("DAGGER");
dagger.attack(); // 匕首攻擊
}
}
1.3.1 簡單工廠模式的優缺點:
簡單工廠模式的優點:
客戶端程式不需要關心物件的建立細節,需要哪個物件時,只需要向工廠索要即可,初步實現了責任的分離。客戶端只負責“消費”,工廠負責“生產”。生產和消費分離。
簡單工廠模式的缺點:
- 缺點1:工廠類集中了所有產品的創造邏輯,形成一個無所不知的全能類,有人把它叫做上帝類。顯然工廠類非常關鍵,不能出問題,一旦出問題,整個系統癱瘓。
- 缺點2:不符合OCP開閉原則,在進行系統擴充套件時,需要修改工廠類。
說明一下:Spring中的BeanFactory就使用了簡單工廠模式。關於這一點,我們後面會詳細說明
1.4 工廠方法模式
工廠方法模式既保留了簡單工廠模式的優點,同時又解決了簡單工廠模式的缺點。
工廠方法模式的角色包括:
- 抽象工廠角色
- 具體工廠角色
- 抽象產品角色
- 具體產品角色
簡單的說:就是一個產品,會對應著一個工廠。
首先,定義好,抽象產品角色,用於統一產品的功能。這裡我們還是以武器 為例子,武器統一都具有攻擊的作用。
/**
* 武器的抽象產品,
* 同樣是統一武器,武器都具備攻擊的功能
*/
public abstract class Weapon {
//
public abstract void attack();
}
定義具體產品角色,這裡,我們還是以有關武器的具體產品:Dagger 匕首,Tank 坦克)
同時具體產品角色都 extends繼承了 抽象產品角色Weapon,統一上,武器都具備攻擊 attack() 。這一點和簡單工廠模式一致。
/**
* 具體工廠角色(武器匕首)
*/
public class Dagger extends Weapon{
@Override
public void attack() {
System.out.println("Dagger 匕首攻擊");
}
}
/**
* 具體工廠角色(武器坦克)
*/
public class Tank extends Weapon{
@Override
public void attack() {
System.out.println("Tank 坦克攻擊");
}
}
抽象工廠角色
這裡我們定義一個介面,作為抽象工廠角色,用於約束,生產類的工廠的角色的。都要進行一個生產get()
。需要注意的是:這個方法不是靜態的,是例項方法,這是與簡單工廠模式的一大區別 。
/**
* 武器工廠的介面
* 統一武器工廠的生產
*/
public interface WeaponFactory {
/**
* 這個方法不是靜態的,是例項方法,
* 這裡是簡單工廠模式的最大的區別
*/
Weapon get();
}
具體工廠角色 :
這裡是兩個分別為了,生產(Dagger 匕首,Tank 坦克)的具體工廠
-
public class DaggerFactory implements WeaponFactory
-
public class TankFactory implements WeaponFactory
/**
* 具體工廠角色
* 生產匕首的工廠
*/
public class DaggerFactory implements WeaponFactory{
@Override
public Weapon get() {
return new Dagger();
}
}
/**
* 坦克的具體工廠角色:
* 專門生產坦克的工廠
*/
public class TankFactory implements WeaponFactory{
@Override
public Weapon get() {
return new Tank();
}
}
客戶端測試
package com.rainbowsea.test;
import com.rainbowsea.bean.DaggerFactory;
import com.rainbowsea.bean.TankFactory;
import com.rainbowsea.bean.Weapon;
import com.rainbowsea.bean.WeaponFactory;
public class Test {
public static void main(String[] args) {
// 我們需要匕首
// 呼叫匕首的工廠,進行生產
WeaponFactory weaponFactory = new DaggerFactory();
Weapon weapon = weaponFactory.get();
weapon.attack(); // 匕首攻擊
// 我們需要坦克
// 呼叫坦克的工廠,進行生產
WeaponFactory weaponFactory2 = new TankFactory();
Weapon weapon2 = weaponFactory2.get();
weapon2.attack(); // 坦克攻擊
}
}
這種簡單工廠模式,如果想擴充套件一個新的產品,只要新增一個產品類,再新增一個該產品對應的工廠即可,例如新增:戰鬥機
從中,我們可以看出,在進行功能擴充套件的時候,不需要修改之前的原始碼(僅僅是新增了,擴充套件的類,物件而已,並沒有修改),顯然工廠方法模式符合OCP原則(修改關閉,擴充套件開啟)。
1.4.1 工廠方法模式的優缺點:
工廠方法模式的優點:
- 一個呼叫者想建立一個物件,只要知道其名稱就可以了。
- 擴充套件性高,如果想要增加一個產品,只需要擴充套件該產品的一個工廠類就可以了
- 同時螢幕產品的具體實現(運用多型機理),呼叫者只關心產品的介面
工廠方法模式的缺點:
每次增加一個新產品時,都需要增加一個具體類和物件實現工廠,使得系統種類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事情。
1.5 抽象工廠模式
抽象工廠模式相對於工廠方法模式來說,就是工廠方法模式是針對一個產品系列的,而抽象工廠模式是針對多個產品系列的,即工廠方法模式是一個產品系列一個工廠類,而抽象工廠模式是多個產品系列一個工廠類。
抽象工廠模式特點:抽象工廠模式是所有形態的工廠模式中最為抽象和最具一般性的一種形態。抽象工廠模式是指當有多個抽象角色時,使用的一種工廠模式。抽象工廠模式可以向客戶端提供一個介面,使客戶端在不必指定產品的具體的情況下,建立多個產品族中的產品物件。它有多個抽象產品類,每個抽象產品類可以派生出多個具體產品類,一個抽象工廠類,可以派生出多個具體工廠類,每個具體工廠類可以建立多個具體產品類的例項。每一個模式都是針對一定問題的解決方案,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式針對的是多個產品等級結果。
抽象工廠中包含4個角色:
- 抽象工廠角色
- 具體工廠角色
- 抽象產品角色
- 具體產品角色
抽象工廠模式的類圖如下:
抽象工廠模式程式碼如下:
第一部分:武器產品族
package com.powernode.product;
/**
* 武器產品族
* @author 動力節點
* @version 1.0
* @className Weapon
* @since 1.0
**/
public abstract class Weapon {
public abstract void attack();
}
package com.powernode.product;
/**
* 武器產品族中的產品等級1
* @author 動力節點
* @version 1.0
* @className Gun
* @since 1.0
**/
public class Gun extends Weapon{
@Override
public void attack() {
System.out.println("開槍射擊!");
}
}
package com.powernode.product;
/**
* 武器產品族中的產品等級2
* @author 動力節點
* @version 1.0
* @className Dagger
* @since 1.0
**/
public class Dagger extends Weapon{
@Override
public void attack() {
System.out.println("砍丫的!");
}
}
第二部分:水果產品族
package com.powernode.product;
/**
* 水果產品族
* @author 動力節點
* @version 1.0
* @className Fruit
* @since 1.0
**/
public abstract class Fruit {
/**
* 所有果實都有一個成熟週期。
*/
public abstract void ripeCycle();
}
package com.powernode.product;
/**
* 水果產品族中的產品等級1
* @author 動力節點
* @version 1.0
* @className Orange
* @since 1.0
**/
public class Orange extends Fruit{
@Override
public void ripeCycle() {
System.out.println("橘子的成熟週期是10個月");
}
}
package com.powernode.product;
/**
* 水果產品族中的產品等級2
* @author 動力節點
* @version 1.0
* @className Apple
* @since 1.0
**/
public class Apple extends Fruit{
@Override
public void ripeCycle() {
System.out.println("蘋果的成熟週期是8個月");
}
}
第三部分:抽象工廠類
package com.powernode.factory;
import com.powernode.product.Fruit;
import com.powernode.product.Weapon;
/**
* 抽象工廠
* @author 動力節點
* @version 1.0
* @className AbstractFactory
* @since 1.0
**/
public abstract class AbstractFactory {
public abstract Weapon getWeapon(String type);
public abstract Fruit getFruit(String type);
}
第四部分:具體工廠類
package com.powernode.factory;
import com.powernode.product.Dagger;
import com.powernode.product.Fruit;
import com.powernode.product.Gun;
import com.powernode.product.Weapon;
/**
* 武器族工廠
* @author 動力節點
* @version 1.0
* @className WeaponFactory
* @since 1.0
**/
public class WeaponFactory extends AbstractFactory{
public Weapon getWeapon(String type){
if (type == null || type.trim().length() == 0) {
return null;
}
if ("Gun".equals(type)) {
return new Gun();
} else if ("Dagger".equals(type)) {
return new Dagger();
} else {
throw new RuntimeException("無法生產該武器");
}
}
@Override
public Fruit getFruit(String type) {
return null;
}
}
package com.powernode.factory;
import com.powernode.product.*;
/**
* 水果族工廠
* @author 動力節點
* @version 1.0
* @className FruitFactory
* @since 1.0
**/
public class FruitFactory extends AbstractFactory{
@Override
public Weapon getWeapon(String type) {
return null;
}
public Fruit getFruit(String type){
if (type == null || type.trim().length() == 0) {
return null;
}
if ("Orange".equals(type)) {
return new Orange();
} else if ("Apple".equals(type)) {
return new Apple();
} else {
throw new RuntimeException("我家果園不產這種水果");
}
}
}
第五部分:客戶端程式
package com.powernode.client;
import com.powernode.factory.AbstractFactory;
import com.powernode.factory.FruitFactory;
import com.powernode.factory.WeaponFactory;
import com.powernode.product.Fruit;
import com.powernode.product.Weapon;
/**
* @author 動力節點
* @version 1.0
* @className Client
* @since 1.0
**/
public class Client {
public static void main(String[] args) {
// 客戶端呼叫方法時只面向AbstractFactory呼叫方法。
AbstractFactory factory = new WeaponFactory(); // 注意:這裡的new WeaponFactory()可以採用 簡單工廠模式 進行隱藏。
Weapon gun = factory.getWeapon("Gun");
Weapon dagger = factory.getWeapon("Dagger");
gun.attack();
dagger.attack();
AbstractFactory factory1 = new FruitFactory(); // 注意:這裡的new FruitFactory()可以採用 簡單工廠模式 進行隱藏。
Fruit orange = factory1.getFruit("Orange");
Fruit apple = factory1.getFruit("Apple");
orange.ripeCycle();
apple.ripeCycle();
}
}
執行結果:
1.6 抽象工廠模式的優缺點:
- 優點: 當一個產品族中的多個物件被設計成一起工作時,它能保證客戶端始終只使用同一個產品族中的物件。
- 缺點: 產品族擴充套件非常困難,要增加一個系列的某一產品,既要在AbstractFactory里加程式碼,又要在具體的裡面加程式碼。
2. 總結:
23種設計模式:
- 建立型(5個):解決物件建立問題。
- 結構型 (7個):一些類或物件組合在一起的經典結構
- 行為型 (11個):解決類或物件之間的互動問題。
簡單工廠模式(靜態工廠模式),需要注意是:一個靜態方法,所以被稱之為”靜態工廠模式“,不屬於23種設計模式。同時需要注意:簡單工廠的一個上帝類和(違背了OCP原則(修改關閉,苦擴充套件開啟)) 的問題。注意其中的優點和缺點,靈活使用。
工廠方法模式既保留了簡單工廠模式的優點,同時又解決了簡單工廠模式的缺點(類爆炸問題)。工廠方法模式(不是靜態方法了,是例項方法)。同時其中的優點和缺點,靈活使用。
抽象工廠模式(解決了工廠方法模式的,類爆炸問題)抽象工廠模式相對於工廠方法模式來說,就是工廠方法模式是針對一個產品系列的,而抽象工廠模式是針對多個產品系列的,即工廠方法模式是一個產品系列一個工廠類,而抽象工廠模式是多個產品系列一個工廠類。其中的優點和缺點,靈活使用。
靈活使用工廠模式的三種形態,處理現實當中的業務需求。
3. 最後:
“在這個最後的篇章中,我要表達我對每一位讀者的感激之情。你們的關注和回覆是我創作的動力源泉,我從你們身上吸取了無盡的靈感與勇氣。我會將你們的鼓勵留在心底,繼續在其他的領域奮鬥。感謝你們,我們總會在某個時刻再次相遇。”