工廠模式理解了沒有?

前言

只有光頭才能變強html

回顧前面:設計模式

  • 給女友講解什麼是代理模式
  • 包裝模式就是這麼簡單啦
  • 單例模式你會幾種寫法?
    昨天寫了單例模式了,今天是時候寫工廠模式啦~

工廠模式我我的認爲其實比較難理解的,若是有接觸過|聽過|見過該模式的同窗極可能就會想:我本身new一個對象出來就行了,簡單快捷。用得着你這個工廠模式嗎?搞一個工廠出來還要寫一大堆的代碼呢~微信

網上的不少資料都是在闡述着:工廠模式的好處就是解耦。相信你們對解耦這個詞也不陌生,那解耦究竟有什麼好處呢?iphone

本文章試圖去解釋爲何要用工廠模式,用了工廠模式的好處是什麼,以及工廠模式衍生出的三種形式究竟有什麼區別~~ide

那麼接下來就開始吧,若是有錯的地方但願能多多包涵,並不吝在評論區指正!post

1、工廠模式概述

在《設計模式之禪》這本書中分了兩章節講工廠模式:.net

  • 工廠方法模式
  • (ps:其中裏面講到了簡單工廠模式)
  • 抽象工廠模式
    網上的大部分資料都是將工廠模式分紅三種:設計

  • 簡單/靜態工廠模式
  • 工廠方法模式
  • 抽象工廠模式
    看完上面的敘述是否是想打死我,什麼鳥玩意?不急哈,下面我會一一講到~~

1.1爲何要用工廠模式?

想一想咱們爲何要用工廠模式?下面我就簡單舉例子:代理

文件IO的操做咱們會常常用獲得吧,因此BufferedReader對象常常要建立的:code

// 建立一個BufferedReader對象
    BufferedReader bf = new BufferedReader(new FileReader(new File("aa.txt")));

你說麻煩嗎?其實也不麻煩,就一行代碼嘛,哪裏麻煩了~若是不太熟悉IO流的同窗就沒有那麼機靈了,建立一個BufferedReader可能就是如下的代碼了:

File file = new File("aa.txt");
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);

你說麻煩嗎?其實也不麻煩,不就是三行代碼嘛,哪裏麻煩了~若是這個應用不少的類上都用到了BufferedReader對象的話,那每一個類都寫上這三行代碼了。那你說麻煩嗎?那確定麻煩啊,還用想啊….

能夠看出來,建立一個BufferReader對象裏面須要一個FileReader對象,而FileReader對象又要File對象。那建立這個BufferReader對象仍是比較麻煩的(代碼上看不麻煩,從構造上看仍是挺麻煩的)!

雖然比較麻煩,但咱們還能用,能用就行!因而乎,咱們就去寫代碼了,如今有三個類都要進行文件的讀寫操做,因而他們就有這樣的代碼:

public class FileOperateA {

    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("aa.txt");
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);

        // 讀寫文件....
    }
}

工廠模式理解了沒有?
此時:上頭說,我要換成LineNumberReader來讀寫,有這個需求!那咱們做爲一個寫代碼的,能怎麼辦?很絕望也須要去完成呀。

  • 不熟悉IDE的小夥子就一個一個將BufferedReader改爲LineNumberReader,如今就3個類用到了BufferedReader,也就改6次而已。(ps:那若是不少地方都用到了呢?)
  • 熟悉IDE的小夥子就全局替換重構,妥妥的!
    哎,寫個代碼屁事真多…那有沒有一種方法可以讓建立對象變得簡單並且修改對象時能很方便呢?

  • 哎,工廠模式就好了。
    再說從面向對象的角度來看:我一個操做文件的類還要我會建立BufferReader是否是有點過度了?(職責沒有分工好)

  • 交給工廠來建立對象這就很面向對象了!

    1.2體驗工廠模式

何爲工廠?將咱們的產品都交由工廠來生產!我如今用的iphone5s,從哪來?從富士康組裝而來,富士康是工廠。我用得着知道iphone5s在富士康是怎麼組裝起來的嗎?不須要。

來,咱們來改造一下上面的例子。首先咱們建立一個工廠類,它能夠生產Reader對象!

// 建立Reader對象的工廠
public class ReaderFactory {
    public static Reader getReader() throws FileNotFoundException {
        File file = new File("aa.txt");
        FileReader fileReader = new FileReader(file);
        BufferedReader reader = new BufferedReader(fileReader);
        return reader;
    }
}

那麼咱們要獲得BufferReader對象就賊簡單了:

public class FileOperateA {

    public static void main(String[] args) throws FileNotFoundException {

        //-------我有工廠了,還用本身搞嗎?不用了!
        //File file = new File("aa.txt");
        //FileReader fileReader = new FileReader(file);
        //BufferedReader bufferedReader = new BufferedReader(fileReader);
        //-------我有工廠了,還用本身搞嗎?不用了!

        // 用工廠來建立出對象
        Reader reader = ReaderFactory.getReader();

        // 讀寫文件....
    }
}

工廠將咱們建立的對象過程給屏蔽了!

此時我要改爲LineNumberReader怎麼玩?在工廠上改一下就行了:

工廠模式理解了沒有?
咱們的調用方FileOperateA|FileOperateB|FileOperateC這些類徹底就不用變!

1.3使用工廠方法的好處

從上面的工廠模式體驗咱們就能夠看到:

  • 咱們修改了具體的實現類,對客戶端(調用方)而言是徹底不用修改的。
  • 若是咱們使用new的方式來建立對象的話,那麼咱們就說:new出來的這個對象和當前客戶端(調用方)耦合了!
  • 也就是,當前客戶端(調用方)依賴着這個new出來的對象!
    這就是解耦的好處!

我再放下我以前練習的時候寫過的代碼吧:

我有一個DaoFactory,邏輯很簡單就是專門建立Dao對象的~

工廠模式理解了沒有?
那麼在Service層就可使用工廠將想要的Dao對象初始化了~

工廠模式理解了沒有?
此時咱們的Service與Dao的對象低耦合的~

  • 你們可能看不出有什麼好處,還弄了一大堆的字符串啥的~~
    在Service與Controller層我也弄了一個ServiceFactory,根據當時業務的須要(添加權限),我建立Service時就很是靈活了:

工廠模式理解了沒有?

2、如何使用工廠模式

在一開始我就說了,工廠模式能夠分紅三類:

  • 簡單/靜態工廠模式
  • 工廠方法模式
  • 抽象工廠模式
    下面我就逐一來介紹一下每一種工廠模式有什麼不同~

三種模式都以:Java3y要買寵物的例子來說解~

2.1工廠方法模式

不少博客都是以簡單/靜態工廠模式,工廠方法模式,抽象工廠模式這個順序來說解工廠模式的。我認爲按書上的順序比較好理解~由於簡單/靜態工廠模式是在工廠方法模式上縮減,抽象工廠模式是在工廠方法模式上再加強。

  • 因此我就先講工廠方法模式了。
    Java3y天天寫代碼很無聊,想要買只寵物來陪陪本身。因而乎就去寵物店看寵物啦~~~

做爲一間寵物店,號稱什麼寵物都有!因而乎,店主宣傳的時候就說:個人寵物店什麼寵物都有!

因而構建寵物的工廠就誕生了~

// 號稱什麼寵物都有
public interface AnimalFactory {

    // 能夠獲取任何的寵物
    Animal createAnimal();
}

固然了,主流的寵物得進貨一些先放在店裏充充門面,一些特殊的寵物就告訴顧客要時間進貨~

  • 因此,咱們就有了構建貓和狗的工廠(繼承着全部寵物的工廠)
    貓工廠:
// 繼承着寵物工廠
public class CatFactory implements AnimalFactory {
    @Override
    // 建立貓
    public Animal createAnimal() {
        return new Cat();
    }

}

狗工廠也是同樣的:

// 繼承着寵物工廠
public class DogFactory implements AnimalFactory {

    // 建立狗
    @Override
    public Animal createAnimal() {
        return new Dog();
    }

}

嗯,還有咱們的實體類:貓、狗、動物(多態:貓和狗都是動物,能夠直接用動物來表示了)

動物實體類:

public abstract class Animal {

    // 全部的動物都會吃東西
    public abstract void eat();
}

貓實體類:

public class Cat extends Animal {

    // 貓喜歡吃魚
    @Override
    public void eat() {
        System.out.println("貓吃魚");
    }

}

狗實體類:

public class Dog extends Animal {

    // 狗喜歡吃肉
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }

}

那麼如今Java3y想要一隻狗,跟了寵物店老闆說,寵物店老闆就去找狗回來了:

// 去找狗工廠拿一隻狗過來
AnimalFactory f = new DogFactory();

// 店主就拿到了一隻狗給Java3y
Animal a = f.createAnimal();
a.eat();
System.out.println("關注公衆號:Java3y");

那麼如今Java3y想要一隻貓,跟了寵物店老闆說,寵物店老闆就去找貓回來了:

// 去找貓工廠拿一隻貓過來
    AnimalFactory ff = new CatFactory();

    // 店主就拿到了一隻貓給Java3y
    Animal aa = ff.createAnimal();
    aa.eat();
    System.out.println("關注公衆號:Java3y");

若是這個時候Java3y說想要一隻蜥蜴怎麼辦啊?沒問題啊,店主搞個蜥蜴工廠就行了~~

// 要買蜥蜴..
        AnimalFactory fff = new LizardFactory();
        Animal aaa = ff.createAnimal();
        aaa.eat();

優勢:

  • 1:客戶端不須要在負責對象的建立,明確了各個類的職責
  • 2:若是有新的對象增長,只須要增長一個具體的類和具體的工廠類便可
  • 3:不會影響已有的代碼,後期維護容易,加強系統的擴展性
    缺點:

  • 1:須要額外的編寫代碼,增長了工做量
    工廠方法類圖:

工廠模式理解了沒有?

2.2簡單/靜態工廠模式

如今寵物店生意很差作啊,號稱「什麼寵物都有",這吹過頭了~~因而店主只賣兩種常見的寵物了。

  • 既然就只有兩種寵物的話,那就不必有」貓廠「、」狗廠「了,一個貓狗廠就好了!
    因此咱們的工廠是這樣子的:
public class AnimalFactory {
    public static Dog createDog() {
        return new Dog();
    }

    public static Cat createCat() {
        return new Cat();
    }

    // 外界想要貓要狗,這裏建立就行了
    public static Animal createAnimal(String type) {
        if ("dog".equals(type)) {
            return new Dog();
        } else if ("cat".equals(type)) {
            return new Cat();
        } else {
            return null;
        }
    }
}

三個實體仍是沒變(動物、貓、狗)….

那麼Java3y去寵物店買貓狗的時候,告訴老闆我要貓、我要狗:

// 拿到狗
        Animal A = AnimalFactory.createAnimal("dog");
        A.eat();

        // 拿到貓
        Animal C = AnimalFactory.createAnimal("cat");
        C.eat();

如今問題來了:

  • 1:我想要一個豬,但是個人工廠類沒有豬
  • 2:我就去改代碼,寫能夠建立豬對象的
  • 3:接着,我又要其餘的動物
  • 4:我仍是得改代碼
  • 5……………….
  • 6:這就是簡單工廠類的缺點:當需求改變了,我就要改代碼.
    簡單工廠類的優勢也很明顯:我就一個具體的工廠來建立對象,代碼量少。

2.3抽象工廠模式

抽象工廠模式就比較複雜了,咱們通常的應用都寫不到。我首先來簡述一下需求吧:

  • 如今很是流行在貓狗屆也吹起了一股「性別風」
  • 有的喜歡公的
  • 有的喜歡母的
    那咱們的貓和狗都是有性別的,不是公的就是母的~~

  • 咱們以前在工廠方法模式下是每一個動物都開一個工廠,若是動物過多的話,那麼就有不少的工廠~
  • 那如今咱們能夠抽取出來:每一個動物不是公的就是母的~
  • 因此咱們有兩個工廠就足夠了!
    具體的代碼是這樣的:

咱們的最大工廠仍是定義了建立什麼動物

public interface AnimalFactory {
    Animal createDog();
    Animal createCat();
}

建立母貓和母狗的工廠:

public class FemaleAnimalFactory implements AnimalFactory {

    // 生產母狗和母貓
    @Override
    public Animal createDog() {
        return  new FemaleDog();
    }

    @Override
    public Animal createCat() {
        return new FemaleCat();
    }

}

建立公貓和公狗的工廠:

public class MaleAnimalFactory implements AnimalFactory {

    // 生產公狗和公貓

    @Override
    public Animal createDog() {
        return new MaleDog();
    }

    @Override
    public Animal createCat() {
        return new MaleCat();
    }

}

這是全部動物都擁有的廣泛行爲:

public abstract class Animal {

    // 全部的動物都會吃東西
    public abstract void eat();

    // 全部的動物都有性別
    public abstract void gender();
}

這是貓都擁有的廣泛行爲:

public abstract class Cat extends Animal {
    // 貓喜歡吃魚
    @Override
    public void eat() {
        System.out.println("貓吃魚");
    }
}

這是狗都擁有的廣泛行爲:

public abstract class Dog extends Animal {

    // 狗喜歡吃肉
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }

}

貓分爲公貓、母貓。狗分爲公狗和母狗:

public class FemaleCat extends Cat {

    public void gender() {
        System.out.println("I am a female Cat");
    }

}
…..

工廠模式理解了沒有?
簡單來講:工廠方法模式的工廠是建立出一種產品,而抽象工廠是建立出一類產品。

  • 一類的產品咱們稱之爲產品族。
  • 貓是一類的,狗也是一類的。因此AnimalFactory定義了兩類產品--->Animal createDog();和Animal createCat();
  • 產品的繼承結構稱之爲產品等級。
  • 全部的狗都是會吃肉的,因此Dog實現了eat()方法
  • 狗又分紅了公狗和母狗,因此定義了兩個類FemaleDog和MaleDog繼承了Dog,實現了gender()方法
  • 全部的貓都是會吃魚的,因此Cat實現了eat()方法
  • 貓又分紅了公貓和母貓,因此定義了兩個類FemaleCat和MaleCat繼承了Cat,實現了gender()方法
  • 具體的工廠是面向多個產品等級結構進行生產。
  • 因此FemaleAnimalFactory定義了createDog()和createCat()生產母狗和母貓
  • 因此MaleAnimalFactory定義了createDog()和createCat()生產公狗和共貓
  • 找到母工廠就能夠建立母貓和母狗,找到公工廠就能夠建立公貓和公狗
    工廠模式理解了沒有?
public static void main(String[] args) {

        // 須要性別爲母的就去找母工廠
        AnimalFactory af = new FemaleAnimalFactory();

        // 須要一隻母貓
        af.createCat().gender();

        // 須要一隻母狗
        af.createDog().gender();

        System.out.println("-------------關注公衆號:Java3y-------------------------");

        // 須要性別爲公的就去找公工廠
        AnimalFactory aff = new MaleAnimalFactory();

        // 須要一隻公狗
        aff.createDog().gender();

        // 須要一隻公貓
        aff.createCat().gender();

    }

效果:

工廠模式理解了沒有?
這是抽象工廠模式的類圖:

工廠模式理解了沒有?
抽象工廠模式說到底就是多了一層抽象,減小了工廠的數量。

抽象工廠缺點也很明顯:

  • 難以擴展產品族--->若是我再要寵物豬的話
  • 那我要修改AnimalFactory、FemaleAnimalFactory、MaleAnimalFactory這些類了~

    3、總結

總的來講咱們用簡單工廠模式比較多,工廠方式模式的話代碼量會比較大,抽象工廠模式的話須要業務比較大的狀況下才會用到(若是有更好的理解方式不妨在評論區留言,一塊兒交流交流漲漲見識~~)

文章的目錄導航:

相關文章
相關標籤/搜索