工厂模式:摄计魔士和他的炼丹密室

从前有一座山叫爪娲山,山峰上住着一个神龙见首不见尾的道士,江湖上的外号叫摄计魔士,他在密室里放着一座炼丹炉。(抱着娱乐的精神,类名方法名有一些用了中文命名,不规范。在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;
    }
}

运行结果:


丹魂工厂这个类便是运用了抽象工厂模式,它以接口的形式提供出了一份创建对象的协议,其中是丹魂的配方,包含露水、白色羽毛和兽骨组合。它只是说丹魂需要这三种成份,具体是用纽约中央公园的新鲜露水还是兰博基尼上面的新鲜露水,它不需要管,由其子类来具体实现,比如御凤丹魂工厂、思聪丹魂工厂。

结构示意图


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容