从现实产品生产理解创建型设计模式
文中图片来源互联网
在《设计模式》一书中,作者阐述了抽象工厂(abstract factory)、建造者(builder)、工厂方法(factory method)、原型(prototype)和单件(sigleton)等创建型设计模式,创建型模式解决了透明创建对象的诉求,就好比让使用者先吃上鸡蛋,而无需操心下蛋的母鸡是哪一只。由于笔者才疏学浅,对于上述设计模式的纸面描述一直处在懵懵懂懂的状态,所以找到一种方便记忆而又容易理解的方法来帮助笔者理解这些由无数软件工程师前人总结的心血成果,一直是笔者思索的问题。
一个汉堡的诞生
汉堡是一种常见的西式食物,既适合社会化大产生的流水线生产,也可以来自精工细作的家庭厨房,将制作汉堡的活动类比创建型设计模式就是两个字:完美!
设想一家超大规模的国际快餐连锁企业,旗下有多个品牌的连锁快餐,比如肯德基、麦当劳、汉堡王等,这些连锁快餐的主打食物是汉堡,出于规模化考虑,汉堡的制作已经实现了标准化和流程化,于是这家企业建立了一座(singleton)全球汉堡工厂来生产汉堡。这座工厂的建设规划有两种方案:
- 方案1 制定一套汉堡工厂的标准,然后分别设立肯德基汉堡工厂、麦当劳汉堡工厂和汉堡王汉堡工厂
- 方案2 建立一座汉堡工厂,然后设立三条流水线来分别生产肯德基、麦当劳和汉堡王汉堡
对于方案1,用代码表示如下:
抽象工厂与抽象产品
public class Burger {
// some method of burger
}
public interface class BurgerFactory {
public Burger createBurger();
}
具体工厂与具体产品
public class KFCBurger extends Burger {
// some method of burger
}
public class KFCBurgerFactory implements BurgerFactory {
public Burger createBurger() {
return new KFCBurger();
}
}
方案2的代码表示如下:
public class FoodFactory {
private BurgerPipeline burgerPipeline;
pulic void produce() {
burgerPipeline.createBurger();
}
}
public abstract class BurgerPipeline {
public Burger createBurger();
}
public class KFCBurgerPipeline extends BurgerPipeline {
public Burger createBurger() {
return new KFCBurger();
}
}
对比上述方案的代码实现,不难发现两种模式产生的代码非常相似,这是因为抽象工厂模式会必然地包含工厂方法模式,因为创建具体产品的工作会交给具体工厂去完成,这与工厂方法模式的思路是一致的。引用stackoverflow上的一段总结如下:
Factory - Creates objects without exposing the instantiation logic to the client and Refers to the newly created object through a common interface. Is a simplified version of Factory Method
Factory Method - Defines an interface for creating objects, but let subclasses to decide which class to instantiate and Refers to the newly created object through a common interface.
Abstract Factory - Offers the interface for creating a family of related objects, without explicitly specifying their classes.
为了简化描述,这里的工厂只包含了制作汉堡一个方法,实际上汉堡工厂的实现更应当是模板方法模式与工厂模式(抽象工厂或工厂方法)的混合体,因为工厂中的流程其实质投射到设计模式上就是模板。
讨论完工厂的规划,再来看看汉堡的制作,通常汉堡的组成会有面包、酱汁、馅料、蔬菜、芝士,不同组合会呈现出不同的风味,吃过肯德基、麦当劳和汉堡王的汉堡的人了解每家的汉堡风味是不太一样的,而且每家汉堡还有很多品种,也就是说汉堡其实是一个复杂对象,对能够呈现出不同表现的复杂对象,builder模式是合适的选择,用代码表示如下:
public class BurgerBuilder {
private Sauce sauce;
private Vegetable vegetable;
private Cheese cheese;
private Stuffed stuffed;
private Bread bread;
public BurgerBuilder withSauce(Sauce sauce) {
this.sauce = sauce;
return this;
}
public BurgerBuilder withVegetable(Vegetable vegetable) {
this.vegetable = vegetable;
return this;
}
public BurgerBuilder withCheese(Cheese cheese) {
this.cheese = cheese;
return this;
}
public BurgerBuilder withStuffed(Stuffed stuffed) {
this.stuffed = stuffed;
return this;
}
public BurgerBuilder withBread(Bread bread) {
this.bread = bread;
return this;
}
public Burger build() {
return new Burger(this);
}
}
使用builder模式分离了组成复杂对象的组件生产和复杂对象生产过程,从生产角度上看,这将极大地方便流水线转产,为实现了快速产生多样化产品提供可能。
通常在正常生产之外,企业还会有一个研发团队,这个团队的任务就是开发新口味的汉堡,以迎合客户尝新的需要,所以我们才会每隔一段时间在广告上看到新的汉堡品种,这个团队的工作不会存在上面的流水线,因为那样做太费时费力。最简单的研发方式就是复制现有的产品,然后对它进行修改,比如劲脆辣鸡堡,去掉酸奶油,加上辣椒酱和一块鸡块,就华丽变身为双层劲爆辣味鸡腿堡。显然这样的工作最适合的就原型模式,通过复制原型并对其进行修改来满足需要,也正好契合这个团队预研工作就是创造新的汉堡原型。用代码表示如下:
public class DoubleFriedChickenSpiceBurger extends Burger implements cloneable {
// some method of burger
@Override
public DoubleFriedChickenSpiceBurger clone() {
Burger burger = super.clone()
burger.replaceSauce(spiceSauce);
burger.addStuffed(friedChicken);
return (DoubleFriedChickenSpiceBurger)burger;
}
}
思考
上面用汉堡生产类比了创建型设计模式,实际上汉堡生产远比上面描述的过程复杂的多。比如规划工厂需要考虑的因素会更多更复杂,原料、土地、劳动力都需要被计算在内,这样会造成某一方案会比另一个更具有优势,但是对于设计模式而言,更多时候是可以相互替换的,因此对汉堡生产过程的关注点有所侧重的抽象,才最终会形成设计模式应用的场景。