工廠模式:攝計魔士和他的煉丹密室

從前有一座山叫爪媧山,山峰上住著一個神龍見首不見尾的道士,江湖上的外號叫攝計魔士,他在密室里放著一座煉丹爐。(抱著娛樂的精神,類名方法名有一些用了中文命名,不規范。在Head First Design Pattern這本書里面,舉了一個Pizza店的例子,我覺得很不錯。我想了如下這個不同的例子,來說明同樣的問題。)本文系原創<br />

為什么需要工廠模式?使用工廠模式有什么好處?可以解決什么問題?

煉丹第一重境界:
攝計魔士剛入行時,涉世不深,只會煉一種丹,所以煉丹的代碼比較簡陋

public class 丹 {
    public static 丹 makeDan() {
        丹 dan = new 丹();
        dan.準備();
        dan.煉制();
        dan.收工();
        return dan;
    }

    private void 收工() {
        System.out.println("七七四十九天,九九八十一輪回,神丹已成,請您享用");
    }

    private void 煉制() {
        System.out.println("麻利麻利哄,煉制過程開始。。。");
    }

    private void 準備() {
        System.out.println("準備開始煉丹");
    }
}

測試丹.makeDan();,運行結果如下


煉丹第二重境界:(靜態工廠方法)
隨著煉丹水平的提升,攝計魔士學會了用不同的元素煉丹
丹.class

public class 丹 {
    static 丹 makeDan(String element) {
        丹 dan = null;
        if (element.equals("金")) {
            dan = new 金丹();
        } else if (element.equals("銀")) {
            dan = new 銀丹();
        } else if (element.equals("銅")) {
            dan = new 銅丹();
        } else if (element.equals("宋丹")) {
            dan = new 宋丹丹();
        }
        if (dan != null) {
            dan.準備();
            dan.煉制();
            dan.收工(element);
        }
        return dan;
    }

    public void 收工(String element) {
        System.out.println("七七四十九天,九九八十一輪回," + element + "丹已成,請您享用\n");
    }

    public void 煉制() {
        System.out.println("麻利麻利哄,煉制過程開始。。。");
    }

    public void 準備() {
        System.out.println("準備開始煉丹");
    }
}

DanTestDrive.class

public class DanTestDrive {
    public static void main(String[] args) {
        丹.makeDan("金");
        丹.makeDan("宋丹");
    }
}

運行結果:


煉丹第三重境界:(簡單工廠)
丹.class

interface 丹 {
    public void 收工();

    public void 煉制();

    public void 準備();
}

金丹.class

public class 金丹 implements 丹 {
    @Override
    public void 收工() {
        System.out.println("煉制完成,用上等的鎏金獸牙盒包裝\n");
    }

    @Override
    public void 煉制() {
        System.out.println("金丹煉制中...");
    }

    @Override
    public void 準備() {
        System.out.println("加入黃金二兩,秘方若干兩");
    }
}

銀丹.class

public class 銀丹 implements 丹 {
    @Override
    public void 收工() {
        System.out.println("煉制完成,用陳年黑檀木盒包裝\n");
    }

    @Override
    public void 煉制() {
        System.out.println("銀丹煉制中....");
    }

    @Override
    public void 準備() {
        System.out.println("加入白銀二兩,秘方若干兩");
    }
}

煉丹密室.class

public class 煉丹密室 {
    簡丹工廠 factory;

    public 煉丹密室(簡丹工廠 factory) {
        this.factory = factory;
    }

    public 丹 makeDan(String element) {
        丹 dan;
        dan = factory.createDan(element);
        dan.準備();
        dan.煉制();
        dan.收工();
        return dan;
    }
}

簡丹工廠.class

public class 簡丹工廠 {
    丹 createDan(String element) {
        丹 dan = null;
        if (element.equals("金")) {
            dan = new 金丹();
        } else if (element.equals("銀")) {
            dan = new 銀丹();
        } else if (element.equals("銅")) {
            dan = new 銅丹();
        } else if (element.equals("宋丹")) {
            dan = new 宋丹丹();
        }
        return dan;
    }
}

測試類

public class DanTestDrive {
    public static void main(String[] args) {
        煉丹密室 煉丹密室 = new 煉丹密室(new 簡丹工廠());
        煉丹密室.makeDan("金");
        煉丹密室.makeDan("銀");
    }
}

運行結果:

結構圖:


煉丹第四重境界1:(工廠模式)
攝計魔士帶的幾個徒弟都出師了,他們是思聰、御鳳、強東、芙蓉四大煉丹術士,雖然師出同門,但他們每個人煉丹都有自己的風格,原來一間小小的煉丹密室已經不夠用了。<br />
原先是在煉丹密室的構造函數中,傳入簡丹工廠的引用,然后在煉丹密室的makeDan方法中,調用簡丹工廠中的createDan()(dan = factory.createDan(element);),以得到不同種類的丹,現在因為徒弟出師了,師父必須在更高的層次上來設計煉丹的標準。createDan從簡丹工廠中移到煉丹密室中,并使之成為抽象方法,從而煉丹密室成為一個抽象類<br />
在此例中可見,“煉丹密室”這個類運用了工廠方法模式,它定義了一個創建對象的接口(createDan),具體要實例化的類(丹類)是哪一個,是由繼承“煉丹密室”的子類所決定的。工廠方法讓類的實例化推遲到子類中。這樣,在煉丹密室這個父類中,所做的是制定流程的事,把不變的部分放在其中,比如將丹類的三個方法按順序排好,丹需要先準備、然后煉制,最后收工。但是具體的實現過程,因丹而異(比如強東的金丹,他要放入狗糧和奶茶粉的配料)。不能將變動的內容放入到煉丹密室中,每種具體的實現,都由每種不同的丹的子類去實現。

煉丹密室.class

public abstract class 煉丹密室 {
    public 丹 makeDan(String element) {
        丹 dan;
        dan = createDan(element);
        dan.準備();
        dan.煉制();
        dan.收工();
        return dan;
    }

    protected abstract 丹 createDan(String element);
}

思聰的煉丹密室.class

public class 思聰的煉丹密室 extends 煉丹密室 {
    @Override
    protected 丹 createDan(String element) {
        if (element.equals("金")) {
            return new 思聰的秘制金丹();
        } else if (element.equals("銀")) {
            return new 思聰的秘制銀丹();
        } else {
            return null;
        }
    }
}

思聰的秘制銀丹.class

public class 思聰的秘制銀丹 implements 丹 {
    @Override
    public void 收工() {
        System.out.println("銀丹煉制完成,用明成化斗彩雞缸杯來裝\n");
    }

    @Override
    public void 煉制() {
        System.out.println("銀丹煉制中...");
    }

    @Override
    public void 準備() {
        System.out.println("思聰秘制銀丹煉制準備。。。");
        System.out.println("放入銀兩,再加兩斤鉆石磨成粉");
    }
}

強東的煉丹密室.class

public class 強東的煉丹密室 extends 煉丹密室 {
    @Override
    protected 丹 createDan(String element) {
        if (element.equals("金")) {
            return new 強東的秘制金丹();
        } else if (element.equals("銀")) {
            return new 強東的秘制銀丹();
        } else {
            return null;
        }
    }
}

強東的秘制金丹.class

public class 強東的秘制金丹 implements 丹 {
    @Override
    public void 收工() {
        System.out.println("用上等的塑料袋包裝\n");
    }

    @Override
    public void 煉制() {
        System.out.println("金丹煉制中,等6月18日來拿...");
    }

    @Override
    public void 準備() {
        System.out.println("強東的金丹鋪子開張啦。。。");
        System.out.println("金丹準備,加一點點奶茶粉");
    }
}

測試類

public class DanTestDrive {
    public static void main(String[] args) {
        煉丹密室 sicongRoom = new 思聰的煉丹密室();
        sicongRoom.makeDan("銀");
        煉丹密室 qiangdongRoom = new 強東的煉丹密室();
        qiangdongRoom.makeDan("金");
    }
}

輸出結果:


煉丹第四重境界2:(工廠模式)
師父一看徒弟們煉的丹,完全隨心所欲,他決定在質量上面增加一點點把控,于是他做了一些改變,首先就是把原先“丹”由接口變成抽象類
丹.class

public abstract class 丹 {
    String name;
    String roomName;
    String creatorName;
    String packageName;
    List<String> additions = new ArrayList<String>();

    public String getName() {
        return name;
    }

    public void 收工() {
        System.out.println("煉制完成," + name + "將由" + packageName + "包裝\n");
    }

    public void 煉制() {
        System.out.println(name + "正在煉制中。。。");
    }

    public void 準備() {
        System.out.println("爪媧山煉丹密室之" + roomName + "開始煉丹。。。");
        System.out.println("接下來將由" + creatorName + "大師開始煉制" + name);
        System.out.println("架爐生火");
        System.out.println("接下來放入" + roomName + "的秘制配方:");

        for (int i = 0; i < additions.size(); i++) {
            System.out.println("  " + (i+1) + ":" + additions.get(i));
        }
    }
}

強東的秘制銀丹.class

public class 強東的秘制銀丹 extends 丹 {
    public 強東的秘制銀丹() {
        name = "強東的秘制銀丹";
        roomName = "強東的煉丹密室";
        creatorName = "強東";
        packageName = "塑料袋";
        additions.add("奶茶粉");
        additions.add("狗糧");
    }
}

思聰的秘制銀丹.class

public class 思聰的秘制銀丹 extends 丹 {
    public 思聰的秘制銀丹() {
        name = "思聰的秘制銀丹";
        roomName = "思聰的煉丹密室";
        creatorName = "思聰";
        packageName = "斗彩雞缸杯";
        additions.add("鉆石粉");
        additions.add("一打iphone");
        additions.add("隕石");
    }
}

測試類

public class DanTestDrive {
    public static void main(String[] args) {
        煉丹密室 sicongRoom = new 思聰的煉丹密室();
        sicongRoom.makeDan("銀");
        煉丹密室 qiangdongRoom = new 強東的煉丹密室();
        qiangdongRoom.makeDan("銀");
    }
}

輸出結果:

結構圖



煉丹第五重境界:(抽象工廠模式)
一山不容二虎,徒兒們漸漸翅膀長硬了,于是都想著要各奔東西了。原本煉丹,第一味總是要先加入師父提供的秘方,才能夠煉制成功。不管是金丹還是銀丹,都需要加入它,這味秘方被稱為“丹魂”(不是混蛋),即丹的靈魂的意思。攝計魔士知道徒兒們的心思,于是意味深長地和徒兒們道出了秘方:丹魂是由三種東西構成的:清晨的露水,白色的羽毛以及若干種獸骨,唯有丹魂,才有神丹。金丹的丹魂是清晨的露水、白色的羽毛及若干種獸骨 ,銀丹的丹魂是清晨的露水和白色的羽毛,銅丹的丹魂只需要清晨的露水。

金丹 銀丹 銅丹
清晨的露水
白色的羽毛 <br />
若干種獸骨 <br /> <br />

其中御鳳去了亞美利加大洲,她煉制金丹時,用了美洲雕的羽毛,清晨中央公園嫩葉上的露水以及火雞骨和北美的野牛骨作為丹魂。而思聰則留在了大陸,他煉制金丹時,用了蘭博基尼上的新鮮露水、白鳳凰的羽毛以及麒麟、獬豸和貔貅骨頭作為丹魂。

丹魂工廠.class

public interface 丹魂工廠 {
    public 露水 createDew();

    public 羽毛 createFeather();

    public 獸骨[] createBones();

    public 金 createGold();

    public 銀 createSilver();

    public 銅 createBronze();

}

思聰丹魂工廠.class

public class 思聰丹魂工廠 implements 丹魂工廠 {
    @Override
    public 露水 createDew() {
        return new 蘭博基尼上的新鮮露水();
    }

    @Override
    public 羽毛 createFeather() {
        return new 白鳳凰羽();
    }

    @Override
    public 獸骨[] createBones() {
        獸骨[] bones = {new 麒麟骨(), new 獬豸骨(), new 貔貅骨()};
        return bones;
    }

    @Override
    public 金 createGold() {
        return new 金();
    }

    @Override
    public 銀 createSilver() {
        return new 銀();
    }

    @Override
    public 銅 createBronze() {
        return new 銅();
    }
}

御鳳丹魂工廠.class

public class 御鳳丹魂工廠 implements 丹魂工廠 {
    @Override
    public 露水 createDew() {
        return new 中央公園新鮮露水();
    }

    @Override
    public 羽毛 createFeather() {
        return new 美洲雕白羽();
    }

    @Override
    public 獸骨[] createBones() {
        獸骨[] bones = {new 火雞骨(), new 北美野牛骨()};
        return bones;
    }

    @Override
    public 金 createGold() {
        return new 金();
    }

    @Override
    public 銀 createSilver() {
        return new 銀();
    }

    @Override
    public 銅 createBronze() {
        return new 銅();
    }

}

丹.class

該類與之前的區別就在于,將“準備()”變成了一個抽象方法,由子類去實現

public abstract class 丹 {
    String name;
    String roomName;
    String creatorName;
    String packageName;
    露水 dew;
    羽毛 feather;
    獸骨[] bones;
    金 gold;
    銀 silver;
    銅 bronze;
    List<String> additions = new ArrayList<String>();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void 收工() {
        System.out.println("煉制完成," + name + "將由" + packageName + "包裝\n");
    }

    public void 煉制() {
        System.out.println(name + "正在煉制中。。。");
    }

    abstract void 準備();
}

金丹.class

public class 金丹 extends 丹 {
    丹魂工廠 ingredientFactory;

    public 金丹(丹魂工廠 ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }


    @Override
    void 準備() {
        System.out.println("開始煉制" + name);
        dew = ingredientFactory.createDew();
        feather = ingredientFactory.createFeather();
        bones = ingredientFactory.createBones();
        gold = ingredientFactory.createGold();
        System.out.println("構成金丹的丹魂是:" +
                        dew.getClass().getSuperclass().getSimpleName() + "、" +
                        feather.getClass().getSuperclass().getSimpleName() + "和獸骨"
        );
        System.out.println(name + "的丹魂配方由"
                        + dew.getClass().getSimpleName() + "、"
                        + feather.getClass().getSimpleName() + "以及若干獸骨所組成"
        );
        System.out.println("其中獸骨的配方包含:");
        for (int i = 0; i < bones.length; i++) {
            System.out.println("  " + (i + 1) + "." + bones[i].getName());
        }
    }
}

銀丹.class

public class 銀丹 extends 丹 {
    丹魂工廠 ingredientFactory;

    public 銀丹(丹魂工廠 ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    @Override
    public void 準備() {
        System.out.println("開始煉制" + name);
        dew = ingredientFactory.createDew();
        feather = ingredientFactory.createFeather();
        silver = ingredientFactory.createSilver();
        System.out.println("構成銀丹的丹魂是:"
                + dew.getClass().getSuperclass().getSimpleName() + "和"
                + feather.getClass().getSuperclass().getSimpleName());
        System.out.println(name + "的丹魂配方由"
                        + dew.getClass().getSimpleName() + "、"
                        + feather.getClass().getSimpleName() + "所組成"
        );
    }
}

思聰的秘制銀丹.class

public class 思聰的秘制銀丹 extends 銀丹 {
    public 思聰的秘制銀丹(丹魂工廠 ingredientFactory) {
        super(ingredientFactory);
        packageName = "斗彩雞缸杯";
    }
}

思聰的秘制金丹.class

public class 思聰的秘制金丹 extends 金丹 {

    public 思聰的秘制金丹(丹魂工廠 ingredientFactory) {
        super(ingredientFactory);
        packageName = "斗彩雞缸杯";
    }
}

御鳳的秘制金丹.class

public class 御鳳的秘制金丹 extends 金丹 {

    public 御鳳的秘制金丹(丹魂工廠 ingredientFactory) {
        super(ingredientFactory);
        packageName = "首飾盒";
    }
}

煉丹密室.class

public abstract class 煉丹密室 {
    public 丹 makeDan(String element) {
        丹 dan;
        dan = createDan(element);
        dan.準備();
        dan.煉制();
        dan.收工();
        return dan;
    }

    protected abstract 丹 createDan(String element);
}

思聰的煉丹密室.class

public class 思聰的煉丹密室 extends 煉丹密室 {
    @Override
    protected 丹 createDan(String element) {
        丹 dan = null;
        丹魂工廠 ingredientFactory = new 思聰丹魂工廠();
        if (element.equals("金")) {
            dan = new 思聰的秘制金丹(ingredientFactory);
            dan.setName("思聰金丹");
        } else if (element.equals("銀")) {
            dan = new 思聰的秘制銀丹(ingredientFactory);
            dan.setName("思聰銀丹");
        } else if (element.equals("銅")) {
            dan = new 銅丹(ingredientFactory);
            dan.setName("思聰銅丹");
        }
        return dan;
    }
}

御鳳的煉丹密室.class

public class 御鳳的煉丹密室 extends 煉丹密室 {
    @Override
    protected 丹 createDan(String element) {
        丹 dan = null;
        丹魂工廠 ingredientFactory = new 御鳳丹魂工廠();
        if (element.equals("金")) {
            dan = new 御鳳的秘制金丹(ingredientFactory);
            dan.setName("御鳳金丹");
        } else if (element.equals("銀")) {
            dan = new 御鳳的秘制銀丹(ingredientFactory);
            dan.setName("御鳳銀丹");
        }
        return dan;
    }
}

測試類

public class DanTestDrive {
    public static void main(String[] args) {
        煉丹密室 sicongRoom = new 思聰的煉丹密室();
        sicongRoom.makeDan("金");
        sicongRoom.makeDan("銀");
        煉丹密室 yufengRoom = new 御鳳的煉丹密室();
        yufengRoom.makeDan("金");
    }
}

運行結果:


有一天思聰心血來潮,想用一下御鳳的丹魂配方,于是他只用改一個地方,即把思聰丹魂工廠改成御鳳丹魂工廠。神奇的事情就發生了,只需要改動一處,他的配方就變成了御鳳的配方。
思聰的煉丹密室.class

public class 思聰的煉丹密室 extends 煉丹密室 {
    @Override
    protected 丹 createDan(String element) {
        丹 dan = null;
        丹魂工廠 ingredientFactory = new 御鳳丹魂工廠();
        if (element.equals("金")) {
            dan = new 思聰的秘制金丹(ingredientFactory);
            dan.setName("思聰金丹");
        } else if (element.equals("銀")) {
            dan = new 思聰的秘制銀丹(ingredientFactory);
            dan.setName("思聰銀丹");
        } else if (element.equals("銅")) {
            dan = new 銅丹(ingredientFactory);
            dan.setName("思聰銅丹");
        }
        return dan;
    }
}

運行結果:


丹魂工廠這個類便是運用了抽象工廠模式,它以接口的形式提供出了一份創建對象的協議,其中是丹魂的配方,包含露水、白色羽毛和獸骨組合。它只是說丹魂需要這三種成份,具體是用紐約中央公園的新鮮露水還是蘭博基尼上面的新鮮露水,它不需要管,由其子類來具體實現,比如御鳳丹魂工廠、思聰丹魂工廠。

結構示意圖


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容