設計模式系列2--三大工廠模式

image

今天學習下最常見的工廠模式片林,工廠模式細分下來有三大類:

 1. 簡單工廠
 2. 工廠模式
 3. 抽象工廠模式

他們的目標都是一樣的:封裝對象的創(chuàng)建。但是實現手段和使用場景卻是不相同。使用的時候三個模式也可以互相替換使用码荔,導致很容易混淆三者。

下面我們來具體看看三者的使用感挥。


簡單工廠模式

準確的說簡單工廠不是一個模式缩搅,而是一種編程習慣。但是平時使用的非常多触幼,我們就把他歸到模式一類了硼瓣。

1、定義

提供一個創(chuàng)建對象實例的功能置谦,而無需關心具體實現堂鲤。被創(chuàng)建的類型可以使接口亿傅、抽象類、具體類瘟栖。

2葵擎、UML結構圖及說明

image

obstractClass:可以實現為抽象類或者具體接口,看實際需要選擇慢宗,定義具體類需要實現的功能
concreteClass:實現抽象類所定義功能的具體類坪蚁,可能會有多個
simpleFactory:簡單工廠,選擇合適的具體類來創(chuàng)建對象返回
client:通過simplefactory來獲取具體的對象

如果對UML圖不了解镜沽,可以先看看這篇文章:UML類圖幾種關系的總結

3敏晤、實際場景運用

3.1、需求

假設我們要實現一個電腦組裝的功能缅茉,組裝電腦很重要的一個地方就是根據客戶指定的cpu類型來安裝嘴脾。假設我們有三種類型的cpu供客戶選擇:apple,intel蔬墩,AMD译打。

3.2、普通實現

在客戶端加入如下方法:

client.m文件
=====================

#import "simpleFactory.h"
#import "interCpu.h"
#import "appleCpu.h"
#import "AMDCpU.h"

@implementation client

-(Cpu *)selectCpuWithType:(NSString *)type{
    Cpu *cpu = nil;
    if ([type isEqualToString:@"intel"]) {
        cpu = [interCpu new];
        
    }else if([type isEqualToString:@"AMD"]){
        cpu = [AMDCpU new];
        
    }else{
        cpu = [appleCpu new];
        
    }
    return  cpu;
}

@end


比如像使用inter類型的cpu拇颅,只需要如下代碼:

[self selectCpuWithType@"interCpu"];

這里我只是展現了核心代碼奏司,忽略了其他代碼。你需要創(chuàng)建一個CPU的父類樟插,然后創(chuàng)建三個子類繼承它韵洋,分別是interCpu、AMDCpu黄锤、appleCpu搪缨。

上面的代碼可以完成功能,根據客戶傳入的type類型來創(chuàng)建相應的cpu具體對象鸵熟。

3.3副编、問題

雖然上述代碼可以完成功能,但是有如下問題:

1流强、如果要加入其他cpu類型痹届,或者更改cpu類型,那么必須修改客戶端代碼打月。違反了開閉原則(不了解的童鞋可以去看設計模式開篇漫談

2短纵、客戶端知道所有的具體cpu類,耦合度太高僵控∠愕剑客戶端必須知道所有具體的cpu類,那么任何一個類的改動都可能會影響到客戶端。

3.4悠就、解決問題

客戶端必須了解所有的具體cpu類才能創(chuàng)建對象千绪,但是這會導致上述一系列問題。那么解決辦法就是把這些對象的創(chuàng)建封裝起來梗脾,對客戶端不可見荸型,那么之后如何改動具體類都不會影響到客戶端。這可以通過簡單工廠來實現炸茧。

下面我們來看看使用簡單工廠重寫后的代碼

引入簡單工廠類:

simpleFactory.h文件

=======================

#import <Foundation/Foundation.h>
#import "Cpu.h"

@interface simpleFactory : NSObject
-(Cpu *)selectCpuWithType:(NSString *)type;

@end


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

simpleFactory.m文件

=======================


#import "simpleFactory.h"
#import "interCpu.h"
#import "appleCpu.h"
#import "AMDCpU.h"

@implementation simpleFactory

-(Cpu *)selectCpuWithType:(NSString *)type{
    Cpu *cpu = nil;
    if ([type isEqualToString:@"intel"]) {
        cpu = [interCpu new];
        
    }else if([type isEqualToString:@"AMD"]){
        cpu = [AMDCpU new];
        
    }else{
        cpu = [appleCpu new];
        
    }
    return  cpu;
}

@end

客戶端調用代碼:

#import <Foundation/Foundation.h>
#import "simpleFactory.h"
#import "Cpu.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        simpleFactory *factory = [simpleFactory new];
        Cpu *cpu = [factory selectCpuWithType:@"interCpu"];
        [cpu installCpu];
    }
    return 0;
}

此時不管是增加還是減少或者修改cpu類型瑞妇,客戶端代碼都不用改動,降低了客戶端和具體cpu類的耦合梭冠,也遵循了開閉原則

4辕狰、反思

細心的一點的童鞋可能發(fā)現,你這不是逗我嗎控漠,僅僅是把本來客戶端的代碼移到了簡單工廠類而已蔓倍,有什么改變嗎?

理解這個問題的關鍵在于理解簡單工廠所在的位置盐捷。

前面我們把創(chuàng)建具體cpu對象的代碼放在客戶端偶翅,導致一系列問題。我們的目標就是讓客戶端從創(chuàng)建具體對象中解耦出來碉渡,讓客戶端不知道對象創(chuàng)建的具體過程聚谁。而簡單工廠就是和具體對象封裝在一起,算是一個封裝體內滞诺,所以簡單工廠知道具體的實現類是沒有關系的⌒蔚迹現在客戶端只要知道簡單工廠和一個抽象類cpu,就可以創(chuàng)建具體對象了铭段,實現了解耦骤宣。

5秦爆、改進

雖然上面使用簡單工廠后序愚,讓客戶端實現了解耦,但是如果實現類改變了等限,我們還是需要需改簡單工廠爸吮。有沒有什么辦法做到即使實現類改變也不需要改變簡單工廠的代碼呢?

在java中可以使用反射或者IoC/DI來實現望门,在iOS種我們有更簡單的方法形娇,一個方法足矣,具體見代碼

-(Cpu *)selectCpuWithType:(NSString *)type{
    Cpu *cpu = (Cpu *)[NSClassFromString(type)new];
    if ([cpu isKindOfClass:[Cpu class]] && cpu) {
        return  cpu;
    }else{
        return nil;
    }
}

客戶端代碼不需要改動筹误,是不是簡單了很多桐早?

6、簡單工廠優(yōu)缺點

  • 優(yōu)點
    1. 幫助封裝

      簡單工廠雖然簡單,但是幫我們實現了封裝對象創(chuàng)建的過程哄酝,讓我們可以實現面向接口編程友存。

    2. 解耦

      客戶端不需要知道具體實現類,也不需要知道創(chuàng)建過程陶衅。只需要知道簡單工廠類就可以創(chuàng)建具體對象屡立,實現了解耦

  • 缺點

    1.增加客戶端復雜度

    如果是通過參數來選擇創(chuàng)建具體的對象,那么客戶端就必須知道每個參數的含義搀军,也就暴露了內部實現

    2.不方便擴展

    如果實現類改變膨俐,那么還是需要修改簡單工廠,可以通過文中的方法來避免這個問題罩句》俅蹋或者使用下節(jié)我們講的工廠方法來解決

7、簡單工廠本質

簡單工廠的本質:選擇實現

簡單的工廠的本質在于選擇的止,而不是實現檩坚,實現是由具體類完成的,不要在簡單工廠完成诅福。簡單工廠的目的是讓客戶端通過自己這個中介者來選擇具體的實現匾委,從而讓客戶端和具體實現解耦,任何實現方面的變化都被簡單工廠屏蔽氓润,客戶端不會知道赂乐。

簡單工廠的實現難點在于如何“選擇實現”,前面講到的是靜態(tài)傳遞參數咖气。其實還可以在運行過程中從內存或者數據庫動態(tài)選擇參數來實現挨措,具體代碼就不演示了,只是讀取參數的方式不同崩溪,其他都一樣浅役。

8、何時使用簡單工廠

  1. 想完全封裝隔離具體實現

讓外部只能通過抽象類或者接口來操作伶唯,上面的例子中觉既,就是只能操作抽象類cpu,而不能操作具體類乳幸。此時可以使用簡單工廠瞪讼,讓客戶端通過簡單工廠來選擇創(chuàng)建具體的類,不需要創(chuàng)建的具體過程粹断。

  1. 想把創(chuàng)建對象的職責集中管理起來

一個簡單工廠可以創(chuàng)建許多相關或者不相關的對象符欠,所以可以把對象的創(chuàng)建集中到簡單工廠來集中管理。

完整代碼見文末瓶埋。


工廠模式

1希柿、問題

讓我們回到最原始的代碼:

client.m文件
=====================

#import "simpleFactory.h"
#import "interCpu1179.h"
#import "appleCpu1179.h"
#import "AMDCpU1179.h"

@implementation client

-(Cpu *)selectCpuWithType:(NSString *)type{
    Cpu *cpu = nil;
    if ([type isEqualToString:@"intel1179"]) {
        cpu = [interCpu1179 new];
        
    }else if([type isEqualToString:@"intel753"]){
        cpu = [interCpu753 new];
        
    }else if([type isEqualToString:@"AMD1179"]){
        cpu = [AMDCpU1179 new];
        
    }else if([type isEqualToString:@"AMD753"]){
        cpu = [AMDCpu753 new];
        
    }else if([type isEqualToString:@"apple1179"]){
        cpu = [appleCpu1179 new];
        
    }else if([type isEqualToString:@"apple753"]){
        cpu = [appleCpu753 new];
        
    }else{
        return nil;
    }return  cpu;
}

@end

仔細看這段代碼诊沪,就會發(fā)現一個問題:依賴于具體類。因為必須在這里完成對象創(chuàng)建曾撤,所以不得不依賴于具體類:interCpu娄徊、appleCpu、AMDCpu盾戴。

這會導致什么問題呢寄锐?簡單來說就是違反了依賴倒置原則,讓高層組件client依賴于底層組件cpu尖啡。違反這個原則的后果就是一旦底層組件改動橄仆,那么高層組件也就必須改動,違反了開閉原則衅斩。聯(lián)系到上面的這個例子就是如果增加或者修改一個cpu子類盆顾,那么就必須改動上面的代碼,即使使用了簡單工廠模式畏梆,還是要修改簡單工廠的代碼您宪。

我們先來看看什么是依賴倒置原則:

定義:

要依賴抽象,不要依賴具體

展開來說就是:不能讓高層組件依賴低層組件奠涌,而且不管高層還是低層組件宪巨,都應該依賴于抽象。

那么如何才能避免違反這一原則呢溜畅?下面有三條建議可以參考下:

  • 變量不可以持有具體類的引用捏卓,比如new一個對象
  • 不要讓類派生自具體類,不然就會依賴于具體類慈格,最好派生自抽象類
  • 不要覆蓋基類中已經實現的方法怠晴,如果覆蓋了基類方法,就說明該類不適合做基類浴捆,基類方法應該是被子類共享而不是覆蓋蒜田。

但是要完全遵守上面三條,那就沒法寫代碼了选泻。所以合適變通才是冲粤,而工廠模式就是為了遵循依賴倒置原則而生的。

下面就來看看使用工廠模式如何解決這個問題滔金。


2色解、定義

定義了一個創(chuàng)建對象的接口茂嗓,由子類決定實例化哪一個類餐茵,讓類的實例化延遲到子類執(zhí)行。

3述吸、UML結構圖及說明

image

先記住工廠模式實現了依賴倒置原則忿族,至于如何實現的锣笨,暫且按下不表,我們先來看代碼

4道批、實際場景運用

還是和簡單工廠的同樣的需求错英,但是我們根據cpu的針腳個數增加了cpu的分類,比如intelCpu1179隆豹、intelCpu753椭岩。另外兩個類型的cpu也是如此,分為1179和753兩個類型的cpu璃赡。但是這次我們用工廠模式來實現判哥。

定義一個工廠基類,定義一個工廠方法

#import <Foundation/Foundation.h>
#import "Cpu.h"

@interface factory : NSObject
-(Cpu*)createCpuWithType:(NSInteger)type;

@end


=============================
#import "factory.h"

@implementation factory
-(Cpu *)createCpuWithType:(NSInteger)type{
    @throw ([NSException exceptionWithName:@"繼承錯誤" reason:@"子類必須重寫該方法" userInfo:nil]);
    return nil;
}
@end


下面是具體工廠碉考,繼承自工廠基類塌计,實現工廠方法來創(chuàng)建具體的cpu對象

#import <Foundation/Foundation.h>
#import "factory.h"

@interface intelFactory : factory

@end

===========================

#import "intelFactory.h"
#import "interCpu753.h"
#import "interCpu1179.h"
#import "Cpu.h"

@implementation intelFactory
-(Cpu *)createCpuWithType:(NSInteger)type{
    Cpu *cpu = nil;
    if (type == 753) {
        cpu = [interCpu753 new];
    }else{
        cpu = [interCpu1179 new];
    }
    return cpu;
}
@end


上面演示的是intelCpu工廠,另外的AMD和apple的cpu具體工廠類類似侯谁,就不貼代碼了锌仅。

客戶端調用:


#import <Foundation/Foundation.h>
#import "factory.h"
#import "Cpu.h"
#import "intelFactory.h"
#import "appleFactory.h"
#import "AMDFactory.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        factory *factory = nil;
        factory = [intelFactory new];
        Cpu *cpu1 = [factory createCpuWithType:753];
        [cpu1 installCpu];
        Cpu *cpu2 = [factory createCpuWithType:1179];
        [cpu2 installCpu];
        
        factory = [AMDFactory new];
        Cpu *cpu3 = [factory createCpuWithType:753];
        [cpu3 installCpu];
        Cpu *cpu4 = [factory createCpuWithType:1179];
        [cpu4 installCpu];

        
    }
    return 0;
}

如果此時又多了一個cpu類型,比如高通的cpu墙贱,那么只需要新建一個高通cpu的工廠類热芹,繼承自factory類,然后實現工廠方法惨撇,就可以了剿吻。客戶端也可以根據自己的需要選擇使用哪個工廠串纺,不用修改原有代碼丽旅。符合開閉原則:對修改關閉,對擴展開放纺棺。

5榄笙、如何遵循依賴倒置原則

我們先來看看沒有使用工廠方法,各個類之間的依賴關系

image

可以看到高層組件client依賴于具體的低層組件cpu類祷蝌,違反了依賴倒置原則茅撞。一般我們把功能的使用者歸到高層組件,把功能的提供者歸到低層組件巨朦。

再來看看使用工廠方法后各個類之間的依賴關系

image

可以看到高層組件client依賴于抽象類cpu米丘,低層組件也就是各種cpu具體類也依賴于抽象類factory,符合依賴倒置原則糊啡。其實說白了拄查,就是要針對接口編程,而不是針對實現編程棚蓄。

那么倒置在哪里呢堕扶?

對比兩個圖碍脏,就會發(fā)現具體cpu類的箭頭從原來向下變成了向上,也就是說依賴關系發(fā)生了倒置稍算。我們來看看為什么會這樣典尾。

第一個圖里面,因為我們直接在client里面去初始化各個cpu類糊探,倒置client就必須依賴這些具體類钾埂,依賴關系向下。

第二個圖里面科平,每個cpu具體類勃教,都繼承自抽象cpu類,并且實現了抽象cpu的方法installCpu匠抗,此時具體cpu類就依賴于抽象cpu類故源,依賴關系向上。

現在明白為什么叫做依賴倒置了吧汞贸?這一切都是工廠方法的功勞绳军。

有人要說,這個用簡單工廠也可以實現的呀矢腻。是的沒錯门驾,簡單工廠也能實現,其實如果直接在工廠方法的抽象cpu類里面實現對象的創(chuàng)建多柑,那么此時工廠模式就是簡單工廠奶是。但是工廠模式有一個簡單工廠模式沒有的功能:遵循開閉原則。如果此時要增加或者修改一個cpu具體類竣灌,那么簡單工廠的代碼就必須修改聂沙,而工廠方法只需要擴展就行了,不用修改原有代碼初嘹。

6及汉、工廠模式優(yōu)缺點

  • 優(yōu)點
    1. 可以在不知道具體實現的情況下編程

      工廠模式可以讓你在實現功能時候,不需要關心具體對象屯烦,只需要使用對象的抽象接口即可坷随,上面例子中client使用的就是cpu抽 象類,而不是具體的cpu類驻龟。

    2. 更容易擴展新版本

      如果需要加入新的實現温眉,只需要擴展一個新類,然后繼承抽象接口實現工廠方法即可翁狐。遵循了開閉原則类溢。

  • 缺點

    具體產品和工廠方法耦合,因為在工廠方法中需要創(chuàng)建具體實例谴蔑,所以它們會耦合

7豌骏、何時使用工廠模式

通過工廠模式定義我們知道,工廠模式主要是把對象的創(chuàng)建延遲到子類執(zhí)行隐锭。如何實現的呢窃躲?

拿上面的例子來說,當我們調用抽象類factory的方法createCpuWithType的時候钦睡,真正執(zhí)行的是factory的子類蒂窒,比如intelFactory。做到這點是面向對象語言的基本特征之一:多態(tài)荞怒,它可以實現父類的同一個方法在不同的子類中有不同的表現洒琢。

了解了工廠模式的本質,我們就知道在上面情況下可以使用它了

  • 一個類不想知道它所需要創(chuàng)建的對象所屬的類褐桌,比如client不需要知道intelCpu1179這個具體類
  • 一個類希望由他的子類來指定它所創(chuàng)建的對象衰抑,比如factory希望IntelFactory創(chuàng)建具體cpu對象

抽象工廠

1、業(yè)務場景

假設我們寫了一套系統(tǒng)荧嵌,底層使用了兩套數據庫:sqlserver和access數據庫呛踊。但是針對業(yè)務邏輯的代碼不可能寫兩套,這樣非常麻煩啦撮,也不方便擴展新的數據庫谭网。我們需要提供一個統(tǒng)一的接口給業(yè)務層操作,切換數據庫也不需要修改業(yè)務層邏輯赃春。

簡化下需求愉择,假設我們每個數據庫都有user和department兩張表,業(yè)務邏輯代碼如下:


        //業(yè)務邏輯
        [user insert:@"張三"];
        [user getUser];
        [deparment insert:@"財務"];
        [deparment getDepartment];


下面我們就來看看如何使用抽象工廠來實現這個需求

2织中、需求實現

2.1锥涕、創(chuàng)建抽象工廠接口

我們先創(chuàng)建一個抽象接口,在iOS里面我們使用協(xié)議實現狭吼。

IFactory.h文件
========================
@class IUser;
@class IDepartment;

@protocol IFactory <NSObject>
@required
-(IUser*)createUser;
-(IDepartment *)createDepartment;

@end

2.2站楚、創(chuàng)建具體工廠

下面我們來創(chuàng)建兩個具體的工廠,分別針對兩個數據庫搏嗡,實現抽象工廠的方法窿春,來創(chuàng)建具體的表對象

#import <Foundation/Foundation.h>
#import "IFactory.h"
#import "IUser.h"

@interface SqlServerFactory : NSObject<IFactory>

@end

======================

#import "SqlServerFactory.h"
#import "SqlServerUser.h"
#import "SqlServerDepartment.h"

@implementation SqlServerFactory

-(IUser *)createUser{
    return [SqlServerUser new];
}

-(IDepartment *)createDepartment{
    return [SqlServerDepartment new];
}
@end


AccessFactory類創(chuàng)建方法類似。

2.3采盒、創(chuàng)建產品

現在我們需要創(chuàng)建具體工廠需要的產品旧乞,這里是兩張表:user和department。但是這兩張表有分為兩個體系磅氨,sqlserver的user和department表尺栖,access的user和department表。

我們把user表抽象為基類烦租,下面分別實現sqlserver和access的子類user表延赌。department表同理除盏,不再貼代碼了。

抽象產品類


#import <Foundation/Foundation.h>

@interface IUser : NSObject
-(void)insert:(NSString *)user;
-(void)getUser;
@end

=======================
#import "IUser.h"

@implementation IUser
-(void)insert:(NSString *)user{
    @throw ([NSException exceptionWithName:@"繼承錯誤" reason:@"子類沒有實現父類方法" userInfo:nil]);
}

-(void)getUser{
    @throw ([NSException exceptionWithName:@"繼承錯誤" reason:@"子類沒有實現父類方法" userInfo:nil]);
}
@end


具體產品類

#import <Foundation/Foundation.h>
#import "IUser.h"

@interface SqlServerUser : IUser

@end

==================


#import "SqlServerUser.h"

@implementation SqlServerUser
-(void)insert:(NSString *)user{
    NSLog(@"向sqlserver數據庫插入用戶:%@", user);
}

-(void)getUser{
    NSLog(@"從sqlserver數據庫獲取到一條用戶數據");
}
@end


#import <Foundation/Foundation.h>
#import "IUser.h"

@interface AccessUser : IUser

@end

=========================

#import "AccessUser.h"

@implementation AccessUser
-(void)insert:(NSString *)user{
    NSLog(@"向access數據庫插入用戶:%@", user);
}

-(void)getUser{
    NSLog(@"從access數據庫獲取到一條用戶數據");
}

@end

2.4挫以、客戶端調用

#import <Foundation/Foundation.h>
#import "IFactory.h"
#import "IUser.h"
#import "IDepartment.h"
#import "SqlServerFactory.h"
#import "AccessFactory.h"


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        id<IFactory> DBFactory = [AccessFactory new];
        IUser *user = [DBFactory createUser];
        IDepartment *deparment = [DBFactory createDepartment];
        
        //業(yè)務邏輯
        [user insert:@"張三"];
        [user getUser];
        [deparment insert:@"財務"];
        [deparment getDepartment];
        
    }
    return 0;
}

輸出:

2016-11-22 17:38:30.667 抽象工廠模式[56330:792839] 向access數據庫插入用戶:張三
2016-11-22 17:38:30.668 抽象工廠模式[56330:792839] 從access數據庫獲取到一條用戶數據
2016-11-22 17:38:30.668 抽象工廠模式[56330:792839] 向access數據庫插入部門:財務
2016-11-22 17:38:30.668 抽象工廠模式[56330:792839] 從access數據庫獲取到一條部門數據

此時如果需要切換到sqlserver數據庫者蠕,只需要更改如下代碼

id<IFactory> DBFactory = [AccessFactory new];
改為:
id<IFactory> DBFactory = [SqlServerFactory new];

但是抽象工廠有個缺點:你想下,如果此時我想增加一張工資表掐松,那么就必須修改抽象工廠接口類IFactory和每個具體工廠類SqlServerFactory踱侣、AccessFactory,違反了開閉原則大磺。但是總體來瑕不掩瑜抡句。

3、 實現原理分析

通過上面的例子杠愧,我想大家已經認識到抽象工廠的優(yōu)雅之處待榔,那么它是如何完成的呢?

我們來把上面的例子做成UML圖流济,這樣看的更加清晰究抓。

image

可以看到我們創(chuàng)建了兩個具體工廠,分別是sqlserverFactory和AccessFactory袭灯。我們的產品有兩個user和department刺下,每個產品也分為兩個體系:sqlserver的access的。

如果選擇sqlserverFactory稽荧,那么對應的兩個工廠方法就生成sqlserver的user和department表橘茉。選擇accessFactory也是如此。

所以我們可以很方便在兩個數據庫之間切換姨丈,而不影響業(yè)務邏輯畅卓,因為業(yè)務邏輯都是面向抽象編程。再看下業(yè)務邏輯的代碼

        id<IFactory> DBFactory = [AccessFactory new];
        IUser *user = [DBFactory createUser];
        IDepartment *deparment = [DBFactory createDepartment];
        
        //業(yè)務邏輯
        [user insert:@"張三"];
        [user getUser];
        [deparment insert:@"財務"];
        [deparment getDepartment];

可以看到業(yè)務邏輯都是針對抽象類IUesr和IDepartment編程蟋恬,所以他們的子類如何變化翁潘,不會影響到業(yè)務邏輯。

4歼争、 抽象工廠定義

提供一個創(chuàng)建一系列相關或者相互依賴的接口拜马,而無需依賴具體類。

好好分析這句話沐绒,關鍵的地方就是:一系列相關或者相互依賴的接口俩莽。這決定了我們使用抽象工廠的初衷,抽象工廠定義了一系列接口乔遮,這些接口必須是相互依賴或者相關的扮超,而不是把一堆沒有什么關聯(lián)的接口放到一起。

回頭看看我們上面的抽象工廠類IFactory定義的接口,是用來創(chuàng)建兩張表出刷,這兩張表是屬于同一個數據庫的璧疗,他們之間是相互關聯(lián)和依賴的。

后面一句“無需依賴具體類”是怎么做到的呢馁龟?

可以看到抽象工廠類只是定義了接口崩侠,而真正去實現這些接口產生具體對象的是具體工廠∑ò兀客戶端面向的也是抽象工廠類編程啦膜,所以無需依賴具體類有送。

我們可以把抽象工廠的定義的方法看做工廠方法淌喻,然后具體工廠去實現這些工廠方法,這不就是工廠模式嗎雀摘?
所以說抽象工廠包含了具體工廠裸删。

5、思考

工廠模式和抽象工廠模式最大的區(qū)別在于阵赠,后者的一系列工廠方法是相互依賴或者相關的涯塔,而工廠模式雖然也可以定義一些列工廠方法,但是他們之間是沒有關聯(lián)的清蚀。這是區(qū)分他們的重要依據匕荸。

其實如果抽象工廠里面只定義一個工廠方法,也就是只實現一個產品枷邪,那么久退換為工廠方法了榛搔。

記住:

工廠模式創(chuàng)建一種類型的產品东揣,抽象工廠創(chuàng)建一些列相關的產品家族践惑。

6、何時使用抽象工廠

  • 客戶端只希望知道抽象接口嘶卧,而不關心具體產品的實現的時候
  • 一個系統(tǒng)需要有多個產品系列中的一個來配置的時候尔觉。也就是說可以動態(tài)切換產品系列狞洋,比如上面的切換兩個數據庫
  • 需要強調一系列產品的接口有關聯(lián)的時候衷蜓,以便聯(lián)合使用它們们颜。

三個模式對比

  • 抽象工廠模式和工廠模式

    工廠模式針對單獨產品的創(chuàng)建椅寺,而抽象工廠注重一個產品系列的創(chuàng)建浆竭。如果產品系列只有一個產品的 話胁出,那么抽象工廠就退換到工廠模式了迫悠。在抽象工廠中使用工廠方法來提供具體實現谆趾,這個時候他們聯(lián) 合使用携添。

  • 工廠模式和簡單工廠

    兩者非常類似嫁盲,都是用來做選擇實現的。不同的地方在于簡單工廠在自身就做了選擇實現。而工廠模式 則是把實現延遲到子類執(zhí)行羞秤。如果把工廠方法的選擇實現直接在父類實現缸托,那么此時就退化為簡單工廠 模式了。

  • 簡單工廠和抽象工廠
    簡單工廠用于做選擇實現瘾蛋,每個產品的實現之間沒有依賴關系俐镐。而抽象工廠實現的一個產品系列,相互 之間有關聯(lián)哺哼。這是他們的區(qū)別


Demo下載地址

簡單工廠

工廠模式

抽象工廠模式

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末佩抹,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子取董,更是在濱河造成了極大的恐慌棍苹,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茵汰,死亡現場離奇詭異枢里,居然都是意外死亡,警方通過查閱死者的電腦和手機蹂午,發(fā)現死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門栏豺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人豆胸,你說我怎么就攤上這事奥洼。” “怎么了晚胡?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵灵奖,是天一觀的道長。 經常有香客問我搬泥,道長桑寨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任忿檩,我火速辦了婚禮尉尾,結果婚禮上,老公的妹妹穿的比我還像新娘燥透。我一直安慰自己沙咏,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布班套。 她就那樣靜靜地躺著肢藐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吱韭。 梳的紋絲不亂的頭發(fā)上吆豹,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音,去河邊找鬼痘煤。 笑死凑阶,一個胖子當著我的面吹牛,可吹牛的內容都是我干的衷快。 我是一名探鬼主播宙橱,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蘸拔!你這毒婦竟也來了师郑?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤调窍,失蹤者是張志新(化名)和其女友劉穎宝冕,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體陨晶,經...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡猬仁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年帝璧,在試婚紗的時候發(fā)現自己被綠了先誉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡的烁,死狀恐怖褐耳,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情渴庆,我是刑警寧澤铃芦,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站襟雷,受9級特大地震影響刃滓,放射性物質發(fā)生泄漏。R本人自食惡果不足惜耸弄,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一咧虎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧计呈,春花似錦砰诵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扶歪,卻和暖如春理肺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工妹萨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贪薪,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓眠副,卻偏偏與公主長得像画切,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子囱怕,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內容

  • 1 場景問題# 1.1 選擇組裝電腦的配件## 舉個生活中常見的例子——組裝電腦霍弹,我們在組裝電腦的時候,通常需要選...
    七寸知架構閱讀 4,345評論 6 67
  • 設計模式匯總 一娃弓、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用典格、多...
    MinoyJet閱讀 3,948評論 1 15
  • 一、設計模式的分類 總體來說設計模式分為三大類: 創(chuàng)建型模式台丛,共五種:工廠方法模式耍缴、抽象工廠模式、單例模式挽霉、建造者...
    RamboLI閱讀 752評論 0 1
  • 簡單工廠模式雖然簡單防嗡,但存在一個很嚴重的問題。當系統(tǒng)中需要引入新產品時侠坎,由于靜態(tài)工廠方法通過所傳入參數的不同來創(chuàng)建...
    justCode_閱讀 1,187評論 1 9
  • 一. 昨晚我沒有做推送蚁趁,因為我偷懶了,和球隊的人去了逛校道实胸,一圈一圈地走著他嫡,聊著生活的瑣事,看著校道橘黃色的燈光庐完,...
    陸木魚啊閱讀 412評論 1 1