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

从前有一座山叫爪娲山,山峰上住着一个神龙见首不见尾的道士,江湖上的外号叫摄计魔士,他在密室里放着一座炼丹炉。(抱着娱乐的精神,类名方法名有一些用了中文命名,不规范。在Head First Design Pattern这本书里面,举了一个Pizza店的例子,我觉得很不错。我想了如下这个不同的例子,来说明同样的问题。)本文系原创

为什么需要工厂模式?使用工厂模式有什么好处?可以解决什么问题?

炼丹第一重境界:
摄计魔士刚入行时,涉世不深,只会炼一种丹,所以炼丹的代码比较简陋

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("宋丹");
    }
}

运行结果:

工厂模式:摄计魔士和他的炼丹密室_第1张图片

炼丹第三重境界:(简单工厂)
丹.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("银");
    }
}

运行结果:

工厂模式:摄计魔士和他的炼丹密室_第2张图片

结构图:


工厂模式:摄计魔士和他的炼丹密室_第3张图片

炼丹第四重境界1:(工厂模式)
摄计魔士带的几个徒弟都出师了,他们是思聪、御凤、强东、芙蓉四大炼丹术士,虽然师出同门,但他们每个人炼丹都有自己的风格,原来一间小小的炼丹密室已经不够用了。

原先是在炼丹密室的构造函数中,传入简丹工厂的引用,然后在炼丹密室的makeDan方法中,调用简丹工厂中的createDan()(dan = factory.createDan(element);),以得到不同种类的丹,现在因为徒弟出师了,师父必须在更高的层次上来设计炼丹的标准。createDan从简丹工厂中移到炼丹密室中,并使之成为抽象方法,从而炼丹密室成为一个抽象类

在此例中可见,“炼丹密室”这个类运用了工厂方法模式,它定义了一个创建对象的接口(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("金");
    }
}

输出结果:

工厂模式:摄计魔士和他的炼丹密室_第4张图片

炼丹第四重境界2:(工厂模式)
师父一看徒弟们炼的丹,完全随心所欲,他决定在质量上面增加一点点把控,于是他做了一些改变,首先就是把原先“丹”由接口变成抽象类
丹.class

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

    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("银");
    }
}

输出结果:

工厂模式:摄计魔士和他的炼丹密室_第5张图片

结构图


工厂模式:摄计魔士和他的炼丹密室_第6张图片

工厂模式:摄计魔士和他的炼丹密室_第7张图片

炼丹第五重境界:(抽象工厂模式)
一山不容二虎,徒儿们渐渐翅膀长硬了,于是都想着要各奔东西了。原本炼丹,第一味总是要先加入师父提供的秘方,才能够炼制成功。不管是金丹还是银丹,都需要加入它,这味秘方被称为“丹魂”(不是混蛋),即丹的灵魂的意思。摄计魔士知道徒儿们的心思,于是意味深长地和徒儿们道出了秘方:丹魂是由三种东西构成的:清晨的露水,白色的羽毛以及若干种兽骨,唯有丹魂,才有神丹。金丹的丹魂是清晨的露水、白色的羽毛及若干种兽骨 ,银丹的丹魂是清晨的露水和白色的羽毛,铜丹的丹魂只需要清晨的露水。

金丹 银丹 铜丹
清晨的露水
白色的羽毛
若干种兽骨

其中御凤去了亚美利加大洲,她炼制金丹时,用了美洲雕的羽毛,清晨中央公园嫩叶上的露水以及火鸡骨和北美的野牛骨作为丹魂。而思聪则留在了大陆,他炼制金丹时,用了兰博基尼上的新鲜露水、白凤凰的羽毛以及麒麟、獬豸和貔貅骨头作为丹魂。

丹魂工厂.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 additions = new ArrayList();

    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("金");
    }
}

运行结果:


工厂模式:摄计魔士和他的炼丹密室_第8张图片

有一天思聪心血来潮,想用一下御凤的丹魂配方,于是他只用改一个地方,即把思聪丹魂工厂改成御凤丹魂工厂。神奇的事情就发生了,只需要改动一处,他的配方就变成了御凤的配方。
思聪的炼丹密室.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;
    }
}

运行结果:


工厂模式:摄计魔士和他的炼丹密室_第9张图片

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

结构示意图


工厂模式:摄计魔士和他的炼丹密室_第10张图片

你可能感兴趣的:(工厂模式:摄计魔士和他的炼丹密室)