从前有一座山叫爪娲山,山峰上住着一个神龙见首不见尾的道士,江湖上的外号叫摄计魔士,他在密室里放着一座炼丹炉。(抱着娱乐的精神,类名方法名有一些用了中文命名,不规范。在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("宋丹");
}
}
运行结果:
炼丹第三重境界:(简单工厂)
丹.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:(工厂模式)
摄计魔士带的几个徒弟都出师了,他们是思聪、御凤、强东、芙蓉四大炼丹术士,虽然师出同门,但他们每个人炼丹都有自己的风格,原来一间小小的炼丹密室已经不够用了。
原先是在炼丹密室的构造函数中,传入简丹工厂的引用,然后在炼丹密室的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("金");
}
}
输出结果:
炼丹第四重境界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("银");
}
}
输出结果:
结构图
炼丹第五重境界:(抽象工厂模式)
一山不容二虎,徒儿们渐渐翅膀长硬了,于是都想着要各奔东西了。原本炼丹,第一味总是要先加入师父提供的秘方,才能够炼制成功。不管是金丹还是银丹,都需要加入它,这味秘方被称为“丹魂”(不是混蛋),即丹的灵魂的意思。摄计魔士知道徒儿们的心思,于是意味深长地和徒儿们道出了秘方:丹魂是由三种东西构成的:清晨的露水,白色的羽毛以及若干种兽骨,唯有丹魂,才有神丹。金丹的丹魂是清晨的露水、白色的羽毛及若干种兽骨 ,银丹的丹魂是清晨的露水和白色的羽毛,铜丹的丹魂只需要清晨的露水。
金丹 | 银丹 | 铜丹 | |
---|---|---|---|
清晨的露水 | √ | √ | √ |
白色的羽毛 | √ | √ | |
若干种兽骨 | √ |
其中御凤去了亚美利加大洲,她炼制金丹时,用了美洲雕的羽毛,清晨中央公园嫩叶上的露水以及火鸡骨和北美的野牛骨作为丹魂。而思聪则留在了大陆,他炼制金丹时,用了兰博基尼上的新鲜露水、白凤凰的羽毛以及麒麟、獬豸和貔貅骨头作为丹魂。
丹魂工厂.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("金");
}
}
运行结果:
有一天思聪心血来潮,想用一下御凤的丹魂配方,于是他只用改一个地方,即把思聪丹魂工厂改成御凤丹魂工厂。神奇的事情就发生了,只需要改动一处,他的配方就变成了御凤的配方。
思聪的炼丹密室.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;
}
}
运行结果:
丹魂工厂这个类便是运用了抽象工厂模式,它以接口的形式提供出了一份创建对象的协议,其中是丹魂的配方,包含露水、白色羽毛和兽骨组合。它只是说丹魂需要这三种成份,具体是用纽约中央公园的新鲜露水还是兰博基尼上面的新鲜露水,它不需要管,由其子类来具体实现,比如御凤丹魂工厂、思聪丹魂工厂。
结构示意图