写在前面

为方便读者,本文已添加至索引

在上篇笔记Builder设计模式中,时の魔导士祭出了自己的WorldCreator。尽管它因此能创造出一个有山有树有房子的世界,但是白雪公主的生活似乎并不太快乐。啊,她当然需要填饱肚子。“来点可口的意式甜点,还是独特的法式面包呢?”魔导士心想。顺便说一下,白雪公主是德国人。“那就德式烤肠怎么样?……总之,我们需要点食品加工厂~尝尝来自世界各地的美味吧!”

来自不同地域的食品加工厂各自有着独特的工艺水平,生产出来的食物口感味道上都各有千秋。但它们生产出来的都是给公主吃的食物,要怎么样去设计这个食物的供给呢?我们当然可以采用Abstract Factory(抽象工厂)模式啦!

要点梳理

  • 目的分类

    • 对象创建型模式
  • 范围准则
    • 对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
  • 主要功能
    • 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
  • 适用情况
    • 一个系统要独立于它的产品的创建、组合和表示时
    • 一个系统要由多个产品系列中的一个来配置时
    • 当我们要强调一系列相关的产品对象的设计以便进行联合使用时
    • 当我们提供一个产品类库,而只想显示它们的接口而不是实现时
  • 参与部分
    • AbstractFactory:声明一个创建抽象产品对象的操作接口
    • ConcreteFactory:实现创建具体产品对象的操作
    • AbstractProduct:为一类产品对象声明一个接口
    • ConcreteProduct:定义一个将被相应的具体工厂创建的产品对象;实现AbstractProduct接口
    • Client:仅使用由AbstractFactory和AbstractProduct类声明的接口
  • 协作过程
    • 通常在运行时刻创建一个ConcreteFactory类的实例。这一具体的工厂创建具有特定实现的产品对象。为创建不同的产品对象,客户应使用不同的具体工厂
    • AbstractFactory将产品对象的创建延迟到它的ConcreteFactory

示例分析 - 美味的食品工厂

时の魔导士合上了他心爱的魔法书。为了让白雪公主能随时随地吃上美味的食物,他在公主的城堡外建造了一个充满魔法的FoodFactory(魔法总是虚幻的,可以认为它只是一个Abstract Factory),它能创造食物Food和饮料Drink,请看示例:

 class FoodFactory {
public:
FoodFactory(); virtual Dinner* makeDinner() const { return new Dinner(); }
virtual Food* makeFood(int f) const { return new Food(f); }
virtual Drink* makeDrink(int d) const { return new Drink(d); }
}

他当然还教会7个霍比特人中的一个生产食物的咒语(createFood),以使得他荣幸地成为白雪公主的第一任美味厨师长。createFood方法以FoodFactory为参数,这样小霍比特人就能指定要制作的食物和饮料了:

 Dinner* Hobbit::createFood (FoodFactory& factory) {
Dinner* dinner = factory.makeDinnner();
Food* food = factory.makeFood();
Drink* drink = factory.makeDrink(); dinner->addFoodAndDrink(food, drink); return dinner;
}

等等,怎么是9份?啊,白雪公主要吃霍比特人2倍的量哦。另外,时の魔导士悄悄地告诉小霍比特人,其实FoodFactory只是一个统一的魔法平台而已,想要让白雪公主尝到世界各地的美味,他还需要建造相应的具体工厂,比如说“PizzaHut”:

 class PizzaHut : public FoodFactory {
public:
PizzaHut(); virtual Food* makeFood(int f) const { return new Pizza(f, addCheese()); }
virtual Drink* makeDrink(int d) const { return new Coffee(d); }
protected:
Cheese* addCheese() const;
}

嗯……更多芝士,更多美味~~“当然,你甚至可以来点中餐!”时の魔导士兴奋地说道:

class ChineseRestaurant : public FoodFactory {
public:
ChineseRestaurant(); virtual Food* makeFood(int f) const { return new ChineseFood(f, "Egg Fried Rice"); }
virtual Drink* makeDrink(int d) const { return new Maotai(d, ); }
}

现在,我们可以看到Abstract Factory的好处了,如果白雪公主想吃意式菜:

 Hobbit theCook;
PizzaHut factory;
Dinner* dinner = theCook.createFood(factory);

那如果她突然想吃中餐,要做的仅仅是:

 Hobbit theCook;
ChineseRestaurant factory;
Dinner* dinner = theCook.createFood(factory);

总而言之,现在他们可以享受到美味的食物啦。

特点总结

趁着白雪公主她们一行人吃饭的时间,我们来回顾一下抽象工厂的特点和实用性吧。在上面的例子中,客户即是霍比特人厨师,工厂则是FoodFactory, PizzaHut, ChineseRestaurant,产品是Dinner(Food和Drink的合集)。注意到FoodFactory仅是工厂方法的一个集合。这是最通常的实现Abstract Factory模式的方式。同时注意FoodFactory不是一个抽象类;因此它既作为AbstractFactory也作为ConcreteFactory。这是Abstract Factory模式的简单应用的另一个通常的实现。因为FoodFactory是一个完全由工厂方法组成的具体类,通过生成一个子类并重定义需要改变的操作,它很容易生成一个新的FoodFactory。我们可以看到:

  1. 它分离了具体的类。Abstract Factory模式帮助我们控制一个应用创建的对象的类。因为一个工厂封装创建产品对象的责任和过程,它将客户与类的实现分离。客户通过它们的抽象接口操纵实例。产品的类名也在具体工厂的实现中被分离;它们不出现在客户代码中。
  2. 它使得易于交换产品系列。一个具体工厂类在一个应用中仅出现一次—即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变。上文的例子已经提到。
  3. 它有利于产品的一致性。当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要。而Abstract Factory可以很容易实现这一点。
  4. 难以支持新种类的产品。难以扩展抽象工厂以生产新种类的产品。这是因为AbstractFactory接口确定了可以被创建的产品集合。支持新种类的产品就需要扩展该工厂接口,这将涉及AbstractFactory类及其所有子类的改变。比方说上面的例子,Dinner中除了Food和Drink,我还想要点Dessert……

当然我们可以定义一个可扩展的工厂,来解决4中所提的难处。比如给创建对象的操作增加一个参数;该参数指定了将被创建的对象的种类。不过,这是一种更灵活但不太安全的设计。

写在最后

今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!

[学习笔记]设计模式之Abstract Factory的更多相关文章

  1. 设计模式——(Abstract Factory)抽象工厂

    设计模式——(Abstract Factory)抽象工厂 设计面向对象软件比较困难,而设计可复用的面向对象软件就更加困难.你必须设计相关类,并设计类的接口和继承之间的关系.设计必须可以解决当前问题,同 ...

  2. [学习笔记]设计模式之Factory Method

    写在前面 为方便读者,本文已添加至索引: 设计模式 魔法手札索引 在上篇笔记Abstract Factory设计模式中,时の魔导士创建了一系列的FoodFactory,并教会了其中一名霍比特人theC ...

  3. [学习笔记]设计模式之Prototype

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在笔记Builder模式中,我们曾见到了最初用于创建平行世界的函数createWorld,并且它是Mage类的成员函数(毕竟是专属于魔 ...

  4. [学习笔记]设计模式之Chain of Responsibility

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 最近时间比较紧,所以发文的速度相对较慢了.但是看到园子里有很多朋友对设计模式感兴趣,我感觉很高兴,能够和大家一起学习这些知识. 之前的 ...

  5. [学习笔记]设计模式之Singleton

    写在前面 为方便读者,本文已添加至索引: 设计模式 魔法手札索引 在前几篇笔记中,我们有了解了部分对象创建型模式,包括Builder(建造者).Abstract Factory(抽象工厂)和Facto ...

  6. [学习笔记]设计模式之Bridge

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 “魔镜啊魔镜,谁是这个世界上最美丽的人?”月光中,一个低沉的声音回荡在女王的卧室.“是美丽的白雪公主,她正和小霍比特人们幸福快乐地生活 ...

  7. [学习笔记]设计模式之Command

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式.今天,我们继续这一主题,来学习 ...

  8. [学习笔记]设计模式之Facade

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Facade(外观)模式定义了一个高层接口,它能为子系统中的一组接口提供一个一致的界面,从而使得这一子系统更加容易使用.欢迎回到时の魔 ...

  9. 设计模式——(Abstract Factory)抽象工厂“改正为简单工厂”

    设计面向对象软件比较困难,而设计可复用的面向对象软件就更加困难.你必须设计相关类,并设计类的接口和继承之间的关系.设计必须可以解决当前问题,同时必须对将来可能发生的问题和需求也有足够的针对性.掌握面向 ...

随机推荐

  1. [DevExpress]DxValidationProvider分享

    前些日子从研究所临时调回公司,帮忙做另外一个项目的控件验证工作,其实内容非常的简单,就是将用户即将提交至服务器的数据先做一个本地验证,以达到减少服务器压力.提高用户体验的目的. 附上一张图片 这是官方 ...

  2. 新开窗口不被拦截的方法-window.open和表单提交form

    $("#btn").click(function() { var w = window.open(); setTimeout(function() { w.location = & ...

  3. js json和对象互相转换

    http://www.jb51.net/article/44562.htm obj = JSON.parse(string) | obj = jQuery.parseJSON(str) 将JSON字符 ...

  4. JavaScript实现url地址自动检测并添加URL链接示例代码

    写一个简单的聊天系统,发出Htpp的Url实现跳转加上a标签,下面是具体的实现,感兴趣的朋友不要错过 背景:写一个简单的聊天系统,发出Htpp的Url实现跳转加上a标签.  实现代码: 复制代码代码如 ...

  5. citrix xen server 虚拟机无法关闭的问题

    悲剧的一台windows的虚拟机无法重启无法关机.如下图,一直卡住不动. 首先找到这台机器: [root@xenserver- xen]# xe vm-list name-label=-vss\ se ...

  6. 分页加查询的sql语句

    "SELECT TOP(@pagesize) * FROM T_News WHERE(NewsTitle LIKE @newskey OR NewsContent LIKE @newskey ...

  7. xmpp 配置数据库 服务器

    一.了解XMPP 协议(标准) XMPP 即时通讯协议 SGIP 短信网关协议 这手机发短信 移动支付和网页支付 0x23232[0,1] 0x23232 0x23232 0x23232 只有协议,必 ...

  8. 7个热门开源PHP框架

    PHP(Hypertext Preprocessor)是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点.虽然有很多其它可供选择的Web开发语言,像:ASP 和Ruby,但是PHP是目 ...

  9. C语言利用va_list、va_start、va_end、va_arg宏定义可变参数的函数

    在定义可变参数的函数之前,先来理解一下函数参数的传递原理: 1.函数参数是以栈这种数据结构来存取的,在函数参数列表中,从右至左依次入栈. 2.参数的内存存放格式:参数的内存地址存放在内存的堆栈段中,在 ...

  10. PHP 切割字符串 点号 不用双斜杠

    $name = "tupian.png"; $nameArr = explode(".", $name); 习惯了Java的程序员容易写成 $nameArr = ...