【阿里大神讲设计模式】2.小光热干面提供饮料了---简单工厂
前情提要
本意是想像美剧的previously那样, 不知道怎么翻译好, 求翻译达人赐教…
上集讲到, 小光辞了工作, 开起了热干面的店子, 用Builder模式改造了热干面的构建过程, 是日渐稳定有效起来, 生意也是越来越好.
但是小光是善于观察的同学啊, 他发现热干面真的好干啊(好像一般人也都能发现, 鬼脸~). 心想, 解决用户痛点才产品的存在根本啊, 是时候推出新东西了.
于是他决定跟推出自己的光氏饮料产品.
饮料的制作
经过一番调查和走访, 小光发现几个特点:
大家对这个饮料要求不高(码农的屌丝特性啊) , 解渴为主.
因为赶着上班, 一般要求要快, 最好可以直接拿走.
品种要丰富, 大家口味不一啊.
于是, 小光选用了最新的XX牌 (有广告商找我吗? 哈哈.) 速溶饮料. 制作饮料的过程很简单, 很快, 小光做出了第一杯饮料—-橙汁:
public class OrangeJuice {
public void make() {
// 1. 拿出一次性饮料杯
System.out.println("拿出一次性饮料杯");
// 2. 加入速溶橙汁粉
System.out.println("加入速溶橙汁粉");
// 3. 加水冲兑
System.out.println("加水");
// 4. 加盖, 打包
System.out.println("加盖, 打包");
}
}
小光的思考
看起来, 程序似乎是可以运转了, 让我们投入生产吧…
然而, 小光何许人啊, 毕竟是在码农界混了好些年的同志啊. 还没有投入使用, 就发现了”问题”:
饮料有很多种, 过程都类似, 只是放的速溶包不一样. 如果我每个都这么写, 改天真有人找我打广告, 杯子上印个二维码什么的, 我不是每个饮料的制作过程都要改啊
灵光一现, 小光想起了面向对象思想中提到的抽象, 封装, 于是乎, 小光改写了自己的程序:
他抽象出来一个”Drink”类:
public abstract class Drink {
public void make() {
// 1. 拿出一次性饮料杯
System.out.println("拿出一次性饮料杯");
// 2. 加入速溶橙汁粉
System.out.println("加入" + getInstantPackage());
// 3. 加水冲兑
System.out.println("加水");
// 4. 加盖, 打包
System.out.println("加盖, 打包");
}
abstract String getInstantPackage();
}
可乐, 酸梅汤, 橙汁皆继承了Drink:
public class Coke extends Drink {
@Override
String getInstantPackage() {
return "速溶可乐粉";
}
}
public class PlumJuice extends Drink {
@Override
String getInstantPackage() {
return "速溶酸梅粉";
}
}
public class OrangeJuice extends Drink {
@Override
String getInstantPackage() {
return "速溶橙汁粉";
}
}
开卖
制作完饮料, 准备开卖了, 是这样的:
public class XiaoGuang {
public static void main(String[] args) {
OrangeJuice orangeJuice = new OrangeJuice();
orangeJuice.make();
}
}
第二天, 生意实在太好, 小光请了表妹临时来帮忙泡制饮料. 而且, 小光还发现自己也无需关注用户要什么饮料了, 直接让用户告诉表妹, 表妹直接制作饮料给用户就行~
表妹负责根据用户要求生产饮料:
public class Cousins {
public static Drink create(String drinkType) {
// Java7开始, switch支持String
switch (drinkType) {
case "橙汁":
return new OrangeJuice();
case "酸梅汤":
return new PlumJuice();
case "可乐":
return new Coke();
default:
return new OrangeJuice();
}
}
}
小光不再关注饮料具体种类:
public class XiaoGuang {
public static void main(String[] args) {
Drink drink = Cousins.create("可乐");
drink.make();
}
}
加上饮料套餐之后, 生意果然越来越好, 估计也有美女助阵的加成, 哈哈…
故事之后
可能有些同学已经看出一点熟悉感了, 我们把这些关系一个UML表示下:
~
如此, 应该就比较清晰了, 这个就是我们今天的主题—-简单工厂.
严格来说, 简单工厂并非一种设计模式, 可以说是一种编码习惯.
那么我们为什么要使用简单工厂, 或者说这么做有什么好处呢?
从我们这个例子中, 可以看到, 小光(Client类)不需要去关注饮料泡制(Drink make)这个过程了, 有更多的时间去接待客户, 只需从表妹(工厂类)那拿到相应的饮料给客户即可.
这个其实是蕴含了单一职责的编程思想:
小光的职责是接待客户
表妹的职责是泡制饮料
简单工厂一般来说, 使用一个静态方法来生产产品, 故而有时也称之为静态工厂方法模式.
扩展阅读
在这个故事的过程中, 我们提到了面向对象的抽象, 封装特性. 实际上我们所有的OOD原则, 设计模式, 都是基于面向对象的思想的, 离不开抽象, 封装, 继承, 多态几大特性. 随着对这些设计模式的分析, 也可以让我们加深面向对象的编程思想.
以Glide为例, 其中的DefaultConnectivityMonitorFactory.java就是一个类似的简单工厂实现:
public class DefaultConnectivityMonitorFactory implements ConnectivityMonitorFactory {
@NonNull
public ConnectivityMonitor build(
@NonNull
Context context,
@NonNull
ConnectivityMonitor.ConnectivityListener listener) {
final int res = context.checkCallingOrSelfPermission("android.permission.ACCESS_NETWORK_STATE");
final boolean hasPermission = res == PackageManager.PERMISSION_GRANTED;
if (hasPermission) {
return new DefaultConnectivityMonitor(context, listener);
} else {
return new NullConnectivityMonitor();
}
}
}
与表妹Cousins(工厂)的实现很类似, 只是这个Factory具体创建什么产品不是由传入的参数决定的, 是在内部的逻辑决定的.
转化为类图关系, 更加清晰:
一直在说的话: 所谓架构, 设计模式都是一种思想, 没有固定的招式, 所有的这些招式都是让我们入门, 了解面向对象的基础思想, 然后能运用无形. 共勉.
留个尾子
大家可能有发现, 简单工厂的弊端也是很多的, 表妹(工厂)的责任太重, 包含(UML依赖关系)了所有的具体产品的实现. 另外, 如果需要修改或增加产品, 我们就得改变工厂类的实现. 这显然违反了开闭原则.
故而, 简单工厂, 一般来说, 适用于产品类别较少, 且固定的场景.