1. 程式人生 > >工廠模式(工廠方法模式,抽象工廠模式) Java

工廠模式(工廠方法模式,抽象工廠模式) Java

工廠模式分為3類:
* 簡單工廠模式 Simple Factory
* 工廠方法模式 Factory Method
* 抽象工廠模式 Abstract Factory
thinking in Java 中工廠方法模式的例子:

interface Service{
    void method1();
    void method2();
}

interface ServiceFactory{
    Service getService();
}

class Implementation1 implements Service{
    Implementation1() {}
    public
void method1(){System.out.println("implementation1 method1");}; public void method2(){System.out.println("implementation1 method2");}; } class Implementation1Factory implements ServiceFactory{ public Service getService(){ return new Implementation1(); } } class Implementation2
implements Service{
Implementation2(){}; public void method1(){System.out.println("implementation2 method1");}; public void method2(){System.out.println("implementation2 method2");}; } class Implememntation2Factory implements ServiceFactory{ public Service getService(){ return
new Implementation2(); } } public class Factories { public static void serviceConsumer(ServiceFactory fact){ Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String[] args){ serviceConsumer(new Implementation1Factory()); serviceConsumer(new Implememntation2Factory()); } }

知乎上的一個問題
為什麼在最後Factories中的靜態方法serviceConsumer中,不直接用Service呢?
省掉工廠方法也可以實現多型,直接用Service例項作為引數,向上轉型成Service介面,去耦合。
如:

interface Service{
    void method1();
    void method2();
}
public static void serviceConsumer(Service s){
    s.method1();
    s.method2();
}

工廠模式真正的精髓並不在於多型去耦合,而是在於“工廠模式”,在Design Pattern中,工廠方法模式是和“虛擬工廠模式”,“生成器模式”,“單例模式”在一起,同屬於建立型模式。
建立型模型最大的共同特徵就是,把型別的定義過程和例項化過程分離開。即在類自身構造器之外,附加一個經常被誤認為沒什麼用的工廠類,如Thinking in Java裡的ServiceFactory:

class ServiceA{
    void method1(){};
    void method2(){};
    // 構造器
    ServiceA(){};
}
class ServiceFactory{
    Service getService(){};
}

實際中,工廠類作用巨大。
當一個類有很多不同的變種時,就需要工廠類來隔離例項化過程。

class ServiceFactory{
    Service getService(){
        if(a){
            ServiceA sA = new ServiceA();
        }else(b){
            ServiceB sB = new ServiceB();
        }else(c){
            ServiceC sC = new ServiceC();
        }
    }
}

這確實有用,但實際生活中,這並不是逼迫我們決定分離出工廠類的最主要的原因。真正的原因是:相比於繼承,我們更優先使用組合。
真正迫使我們決定分離出工廠類的原因,是實體類使用了組合,而且元件又非常的多,如果Service是由很多元件構成的龐大的傢伙,比如一個迷宮:假設一個迷宮是由三種不同的小單元組成,為房間,牆,門。

class Maze{
    Room r1;
    Room r2;
    ...
    Wall w1;
    Wall w2;
    ...
    Door d1;
    Door d2;
    ...
    //構造器
    Maze(){
        //按順序擺放成員欄位元件
        ...
    }
}

問題是,要生成一個迷宮,要成百上千個門,牆,房元件。而且還要遵循一定的邏輯規則,因此構造器就會很複雜,迷宮有無數種,如果給每個迷宮都建立一個實體類,再給出具體構造流程,那就累死了。

這種情況下,合理的辦法是寫一個隨機迷宮生成器,能根據具體要求不同生成無數種迷宮,這就是建立型模式的意義所在。無論是“虛擬工廠模式”,還是“生成器”模式,工廠模式,都是在具體生成的策略上做文章,但一個大前提不變:具體產出的產品Product都是由很多小的元件組合而成,而且組裝的時候靈活度非常高,甚至是runtime使用者定製的,或者是隨機的。這就導致組合出來的產品Product單獨作為一個類存在的意義很小,反而是構造器的作用被放大。索性把構造過程獨立出來成為一個方法,把這個方法用到的引數作為成員欄位一起封裝起來,再來個構造器只負責初始化這些引數,這種累就是“工廠類”。

class MazeFactory{
    //巨大迷宮生成演算法
    Maze mazeGenerator(int roomNum, int wallNum, int doorNum){
        ...
    }
    //構造器 初始化生成迷宮的引數
    MazeFactory(){
        roomNum = 100;
        wallNum = 1000;
        doorNum = 200;
    }
    //欄位:生成迷宮的引數
    int roomNum;
    int wallNum;
    int doorNum;
}

原來的那個迷宮類,本身的構造器不承擔任何功能了。

class Maze{
    void play(){...}

    Maze(int roomNum, int wallNum, int doorNum){
        Room[] roomSet = new Room[roomNum];
        Wall[] wallSet = new Wall[wallNum];
        Door[] doorSet = new Door[doorSet];
    }

    Room[] roomSet;
    Wall[] wallSet;
    Door[] doorSet;

}

所以再回到工廠方法,書裡說的所有的東西,都是基於這個前提,也就是我們說好了啊,迷宮這東西的類檔案裡是沒有具體構造方法的,都是要用工廠類MazeFactory來生成的。至於後來,我們加入了方迷宮,和方迷宮生成器。又有了圓迷宮和圓迷宮生成器。有了一堆生成器複雜了之後,又想到用多型來解耦,這都是後話,是在確定了使用工廠類的前提下,利用多型解耦的優化方案。所以才有了最初的這段程式碼:

//兩個介面
interface Maze{
    void play();
}
interface MazeFactory{
    Maze mazeGenerator();   
}

//各自繼承類
CircularMaze implements Maze{
    ...
}
SquareMaze implements Maze{
    ...
}

CircularFactory implements MazeFactory{
    ...
}
SquareMazeFactory implements MazeFactory{
    ...
}

//多型,面向介面
public static void mazeGame(MazeDactory fact){
    Maze z =  fact.mazeGenerator();
    z.play();
}

再來,如果程式為:
public static void serviceConsumer(Service s){
    s.method1();
    s.method2();
}

Service從哪來呢?當然是從其他地方new了傳進來:

Service s = new ServiceA();
serviceConsumer(s);

Service為什麼是一個介面?

ServiceA s = new ServiceA();
s.method1();
s.method2();

如果有一天,領導要求將資料庫從MYSQL換成ACCESS,因為只有一個ServiceA所以只能把所有用到ServiceA的地方都替換成ServiceB:

//!ServiceA s = new ServiceB();
ServiceB s = new ServiceB();
s.method1();
s.method2();

如果是個小專案,沒關係,如果是一個龐大的團隊專案,,,根據設計原則中的依賴倒置原則,設計一個介面,然後擴充套件一個類,這樣就能靈活應對boss的需求了:

Service s;
if (Config.DB == SQL)
    s = new ServiceA();
else if (Config.DB == ACCESS)
    s = new ServiceB();
s.method1();
s.method2();

又過了幾天,用到的資料庫越來越多,if else也越來越多,我麼你決定隔離這些變化,將Service例項化過程放在一個地方,簡單工廠誕生:

Service s = ServiceFactory.getService(Config.DB);
s.method1();
s.method2();

public class ServiceFactory{
    public static Service getService(DB db){
        if(db == SQL)
            return new SerivceA();
        else if(db == ACCESS)
            return new ServiceB();
        else
            return null;
    }
}

這樣,即時資料庫不斷變化,我也只需要擴充套件一個新的類,並且修改工廠這個類就行了。但是ServiceFactory是一個介面,這就是抽象工廠

另一位回答者接下去回答:
如果只有一個工廠,但同一個Service在不同環境下會有不同的實現,如果環境很多,這個ServiceFactory會變成什麼樣呢:

class ServiceFactory{
    Environment env;
    public static Service getService(){
        if(env == MYSQL){
            return new ServiceA(arg1, arg2,...)
        }else if (env == ORACLE){
            return new ServiceB(arg1, arg2,...);
        }else if (env == BIG_TABLE){
            return new ServiceC(arg1, arg2,...);
        }
    }
}

那麼if else越來越多,每次修改都需要重新編譯整個Factory,而且,一般情況下,實現這些Service的往往是不同組的人,每個組都往這個類里加引數/狀態,合併程式碼時會出現無數個衝突,還可能互相用錯引數從而影響別人程式碼,抽象工廠大家的實現分開到不同的類裡面,讓大家各改各的檔案,互不影響:

//小組1的檔案:
class MySqlFactory implements ServiceFactory{
    public static Service getService(){
        //計算 arg1, arg2,...
        return new ServiceA(arg1, arg2);
    }
}

//小組2的檔案
class OrcaleFactory implements ServiceFactory{
    public static Service getService(){
        //計算arg1, arg2, ...
        return new ServiceB(arg1, arg2);
    }
}
...

這些工廠不需要使用時才new,只要事先new好,存入一個Map,根據具體環境隨時呼叫:

Map<Environment, ServiceFactory> factoryMap = new HashMap<>();
factoryMap.put(MYSQL, new MySqlFactory());
factoryMap.put(ORACLE, new OracleFactory());
...

然後呼叫serviceConsumer的那個地方只需要:

Environment env = ...//獲得環境變數env
ServiceFactory factory = factoryMap.get(env);
serviceSonsumer(factory);

以後如果有新的資料庫,重新寫一個ServiceFactory的實現類,編譯一下放一塊就行,在factoryMap裡放入例項就好了,別的程式碼完全不需要動,甚至不需要重新編譯。
可不可以不要Factory,直接用ServiceMap呢?

Map<Environment, Service> serviceMap = new HashMap<>();
serviceMap.put(MYSQL, new ServiceA(..));
serviceMap.put(ORACLE, new ServiceB(..));

基本是不行的,首先Service的初始化一般需要很多引數,不可能在程式剛載入時就存在,另外Service的例項會new很多個,也不滿足一一對應的關係。但Factory的建構函式一般不需要引數。相比於Service,Factory內部儲存的僅僅是一些引數,佔用記憶體小得多,長時間駐留在記憶體中也沒有太大的損害。

相關推薦

設計模式學習之責任鏈模式Chain of Responsibility行為型模式22

分析 一定的 之間 ash 我們 set 抽象 request 發現 參考:http://www.cnblogs.com/zhili/p/ChainOfResponsibity.html 一、引言   在現實生活中,有很多請求並不是一個人說了就算的,例如面試時的工資,低

Spring 原始碼分析-----委派模式不關心過程只關心結果

兩個角色,受託人、委託人(社會上是平等關係) 公司裡面:專案經理,普通員工(法律上是平等的,工作的關係,各自的職責會不一樣) 幹活是我的,功勞是你的(最重要的特點) 專案經理(委託人):主要職責是安排任

設計模式-裝飾者模式問題比較大需要重新處理

設計原則:類應該對擴充套件開放,對修改關閉。在不修改現有程式碼的情況下,拓展,搭配新的行為。設計更加彈性應對改變,可以接受新的功能應對改變的需求。 遵循開放關閉原則會引入程式碼抽象層次,增加程式碼的複雜度。我們的注意力是在於設計中最有可能發生改變的地方,然後應用開放關閉原

iOS經常使用設計模式——工廠方法簡單工廠模式工廠方法模式 抽象工廠模式

csdn bst 設計 cto mod 基類 load 引用 角色 1. 簡單工廠模式 怎樣理解簡單工廠,工廠方法。 抽象工廠三種設計模式? 簡單工廠的生活場景。賣早點的小攤販。他給你提供包子,饅頭,地溝油烙的煎餅等,小販是一個工廠。它生產包子,饅頭,地溝油烙的

設計模式2——建立型——工廠相關:簡單工廠Simple factory工廠方法Factory method抽象工廠Abstract factory

概要 這裡試圖描述23個設計模式中的兩個工廠(Factory)相關的設計模式:工廠方法(Factorymethod),抽象工廠(Abstract factory)。 注意點: 這兩個都屬於建立型設計模式。 由於這兩個設計模式都

【設計模式-4】工廠模式簡單工廠工廠方法抽象工廠

簡單工廠模式(Simple Factory)   簡單工廠模式(Simple Factory Pattern):又稱為靜態工廠方法(Static Factory Method)模式。在簡單工廠模式中,可以根據引數的不同返回不同類的例項。簡單工廠模式專門定義一個類來負責建立其

java設計模式 建立型模式 工廠模式 (簡單工廠工廠方法抽象工廠)

1.簡單工廠    定義:簡單工廠模式又 叫靜態工廠方法模式(Static FactoryMethod Pattern),是通過專門定義一個類來負責建立其他類的例項,被建立的例項通常都具有共同的父類。   2.工廠方法    定義:定

iOS常用設計模式——工廠方法簡單工廠模式工廠方法模式 抽象工廠模式

1. 簡單工廠模式 如何理解簡單工廠,工廠方法, 抽象工廠三種設計模式? 簡單工廠的生活場景,賣早點的小攤販,他給你提供包子,饅頭,地溝油烙的煎餅等,小販是一個工廠,它生產包子,饅頭,地溝油烙的煎餅。該場景對應的UML圖如下所示: 圖1:簡單工廠模式UML圖 簡單

工廠模式簡單工廠工廠方法抽象工廠

1.簡單工廠模式:      簡單工廠模式又稱靜態工廠方法模式。從命名上就可以看出這個模式一定很簡單。它存 在的目的很簡單:定義一個用於建立物件的介面。 先來看看它的組成: 1) 工廠類角色:這是本模式的核心,含有一定的商業邏輯和判斷邏輯。在java中它往往由 一個

工廠模式工廠方法模式抽象工廠模式 Java

工廠模式分為3類: * 簡單工廠模式 Simple Factory * 工廠方法模式 Factory Method * 抽象工廠模式 Abstract Factory thinking in Java 中工廠方法模式的例子: interface Ser

2抽象工廠模式Abstract Factory Pattern 抽象工廠可以一下生產一個產品族裏面有很多產品組成

creat name hba abstract 模式 存在 names cto 園區 備註  工廠模式:要麽生產香蕉、要麽生產蘋果、要麽生產西紅柿;但是不能同時生產一個產品組。     抽象工廠:能同時生產一個產品族。===》抽象工廠存在原因 解釋 : 具體工廠

簡單工廠模式工廠方法模式抽象工廠模式

個人認為比起文字解釋,用類圖、程式碼和執行結果更能瞭解和感受設計模式的思想。 簡單工廠模式 public interface Shape { void draw(); } public class Triangle implements Shape {

設計模式之簡單工廠工廠方法抽象工廠模式

目錄 1.簡單工廠模式 1)最基本的實現         簡單工廠常用的方法就是一個工廠類,裡面包含很多if else結構 或者switch case 、如下程式碼ProductA和ProductB是分別的兩個不同的類: public cl

工廠方法模式抽象工廠模式簡單例項分析

為了更好地理解這兩個模式,自己假設了如下場景,可能有些牽強,但應該還好。嘿嘿。 Acer,Lenovo兩家本本製造商,假設原來有一OEM兩個牌子的本本都做,這時,無論你想買那種牌子的,都可以直接向OEM購買。 可是後來該OEM商發現,如果一次同時做很多個牌子的本本,

工廠模式工廠方法模式抽象工廠模式 詳解

1. 工廠模式1.1 描述簡單工廠模式是由一個工廠物件根據收到的訊息決定要建立哪一個類的物件例項。1.2 使用場景工廠類負責建立的物件比較少,客戶只需要傳入工廠類引數,對於如何建立物件(邏輯)不關心。簡單工廠模式很容易違反高內聚低耦合的原則,因此一般只在很簡單的情況下使用。1

設計模式之簡單工廠模式工廠方法模式抽象工廠模式

在面向物件程式設計中, 最通常的方法是一個new操作符產生一個物件例項,new操作符就是用來構造物件例項的。但是在一些情況下, new操作符直接生成物件會帶來一些問題。舉例來說, 許多型別物件的創造需要一系列的步驟: 你可能需要計算或取得物件的初始設定; 選擇生

淺談 簡單工廠模式工廠方法模式抽象工廠模式的優點和缺點

1.簡單工廠模式: 簡單工廠模式的實質是由一個工廠類根據傳入的引數 動態決定應該創建出哪一個產品類的例項 工廠類角色,抽象產品角色,具體產品角色 嚴格說這並不是一個設計模式,簡單工廠沒有抽象類,只有一個工廠類,這個類有個工廠方法是專門返回一個具體產品類,具體

工廠模式簡單工廠模式抽象工廠模式

說到這幾個工廠模式有很多相似之處又有不同。最重要的是掌握這種思想,在以後搭建專案架構或寫一些功能,應用這些思想,讓自己的程式更健壯,或者說當你看到別人寫的程式應用到了這種思想能夠快速理解。話不多說,咱們先從入門級的小案例講起。 一.簡單工廠模式 基本概念:簡單工廠模式是由一個工廠類根據接受到的訊息決定要建

工廠模式簡單、普通、抽象

概述 屬於建立型設計模式,需要生成的物件叫做產品 ,生成物件的地方叫做工廠 。 使用場景:   1、在任何需要生成複雜物件的地方,都可以使用工廠方法模式。   2、直接用new可以完成的不需要用工廠模式 一、簡單(靜態)工廠  我喜歡吃粉,抽象一個粉基類(或者介面),

設計模式-建立型模式-工廠模式抽象工廠模式

簡單工廠模式 簡單工廠模式不是 23 種裡的一種,簡而言之,就是有一個專門生產某個產品的類。 它只算工廠模式的一個特殊實現。簡單工廠模式在實際中的應用相對於其他2個工廠模式用的還是相對少得多,因為它只適應很多簡單的情況。 簡單工廠例項 1)建立Shape介面 publ