写在前面

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

作为一个新入职的魔导士呢,哦不,是程序员,我以为并没有太多机会去设计项目的软件架构。但是,工作一段时间之后,自己渐渐意识到,哪怕是自己要去做的小feature,也是需要去好好设计。我就应该做好它的架构师。看过一些牛人的文章后,总算意识到自己应该好好学习下设计模式。尽管并不是为了把设计模式作为教条式的方案套用到现实的工作中去,起码我也要做到心中有佛吧,...是心中有数,再在日后工作中积累经验,达到融汇贯通的境地。设计模式是前人总结出的相当重要的经验,也是针对特定问题的简洁而优雅的解决方案。我想,它们不仅在面向对象(OO)设计中极具参考价值,更能推广到日常生活方方面面。因此,我还是好好地学习下设计模式,在此记录自己的学习笔记。废话不多说,开始第一篇:Builder模式。

要点梳理

  • 目的分类

    • 对象创建型模式
  • 范围准则
    • 对象
  • 主要功能
    • 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  • 适用情况
    • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时;
    • 当构造过程必须允许被构造的对象有不同的表示时;
  • 参与部分
    • Builder:为创建一个Product对象的各个部件指定抽象接口
    • ConcreteBuilder:具体实现Builder的接口以构造和装配该产品的各个部件的生成器;定义并明确它所创建的表示;提供一个返回检索其Product的接口
    • Director:使用Builder接口的对象,称为导向器
    • Product:它表示被构造的复杂对象;包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
  • 协作过程
    • 我们首先创建Director对象,并用它所想要的Builder对象进行配置
    • 一旦Product部件被生成,导向器就会通知生成器
    • 生成器处理导向器的请求,并将部件添加到该Product中
    • 我们可以通过生成器检索最后成品

示例分析 - 世界创造器

总觉得书上讲的例子会否太过严肃,我在这里还是祭出时の魔导士(Mage)的大杀器,神奇的“世界创造器”--WorldCreator吧!平行世界的概念想必大家都懂,现在让我们来看看,Mage如果要用咒语createWorld来生成一个平行世界,让可爱的白雪公主和7个霍比特人幸福快乐地生活,我们从哪开始呢?(貌似混进了奇怪的事情。)那就让我们从WorldCreator类开始吧,它定义了以下接口来创造世界:

 class WorldCreator {
public:
virtual void createWorld() {}
virtual void createMountains(int m) {}
virtual void createPlants(int p) {}
virtual void createBuildings(int b) {}
virtual World* getWorld() { return ; }
protected:
WorldCreator();
};

首先建立一个平行世界,然后造山,造树,造房子,嗯......咦,怎么都是virtual?没错!WorldCreator就是前文所提的Builder,它自己并不实际用来创造世界,而是由它的子类来完成;它的主要目的仅仅是为创造世界提供接口,并且,这些接口缺省操作虽然是空,什么也不做,但也不定义成纯虚函数。因为这样便于子类只重定义它们所感兴趣的那些方法。比方说,我突然又想创造一个许多元素生物存在的世界,那里压根没有人工建筑Buildings,那么我就不用重写createBuildings方法了。后面会继续讲到。

有了WorldCreator,我们来看看Mage的createWorld咒语(圈内术语应该叫Mage类的createWorld成员函数)到底是什么样的吧:

 World* Mage::createWorld(WorldCreator& builder) {
builder.createWorld();
builder.createMountains();
builder.createPlants();
builder.createBuildings(); return builder.getWorld();
}

像其他创建型模式一样,Builder模式封装了对象是如何被创建的,在这个例子中是通过WorldCreator所定义的接口来封装的。这就意味着我们的Mage可以重用WorldCreator来创造不同种类的世界,比方说,前面提到的没有人工建筑的世界:

 World* Mage::createEarthElementalWorld(WorldCreator& builder) {
builder.createWorld();
builder.createMountains(); return builder.getWorld();
}

什么?连树都没有,这地方白雪公主可不想去。

前面说过WorldCreator本身并不能创造世界,因为它是虚的,它的子类才做了实际的工作。来看一个简单的子类LovelyWorldCreator实现:

 class LovelyWorldCreator : public WorldCreator {
public:
LovelyWorldCreator() { _currentWorld = ; } virtual void createWorld();
virtual void createMountains(int);
virtual void createPlants(int);
virtual void createBuildings(int); virtual World* getWorld() { return _currentWorld; }
private:
World* _currentWorld;
}; void LovelyWorldCreator::createWorld() {
_currentWorld = new World();
} void LovelyWorldCreator::createMountains(int m) {
if ( !_currentWorld->hasMountains(m) ) {
Mountains* mountains = new Mountains(m);
_currentWorld->addMountains(mountains);
}
} void LovelyWorldCreator::createPlants(int p) {
if ( !_currentWorld->hasPlants(p) ) {
Plants* plants = new Plants(p);
_currentWorld->addPlants(plants);
}
} void LovelyWorldCreator::createBuildings(int b) {
if ( !_currentWorld->hasBuildings(b) ) {
Buildings* buildings = new Buildings(b);
_currentWorld->addBuildings(buildings);
}
}

LovelyWorldCreator

这里createWorld先创造了一个空的世界,然后createMountains, createPlants, createBuildings 分别为这可爱的世界添加了山、树以及建筑物。现在,Mage终于可以用createWorld和LovelyWorldCreator来创造一个平行世界了:

 Mage I;
LovelyWorldCreator spell;
World* world = I.createWorld(spell);

美丽的白雪公主如愿以偿地住进了可爱的世界。当然,还有那些霍比特人们。

特点总结

啊,对了,差点忘了说,正因为我们采用了Builder模式(似乎刚跑题了会,赶紧拉回来),将World的构建从World本身的构造函数中分离出来,由WorldCreator封装了统一的创造过程,但是却能通过子类比如LovelyWorldCreator等来创造不同表现形式。这正是Builder设计模式的特点之一。总结说来,Builder模式有如下几个特点:

  1. 它使我们可以改变一个产品的内部表示。由于Builder对象提供的是一个抽象接口,这个接口可以使生成器隐藏这个产品的表示和内部结构。所以如果要改变产品的内部结构,只需定义一个新的生成器。
  2. 它将构造代码和表示代码分开。Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性。每个ConcreteBuilder包含了创建和装配一个特定产品的所有代码。这些代码只需要写一次;然后不同的Director可以复用它以在相同部件集合的基础上构作不同的Product。
  3. 它使我们可以对构造过程进行更精细的控制。Builder模式是在导向者的控制下一步一步构造产品的,仅当该产品完成时导向者才从生成器中取回它。因此Builder相比其他创建型模式能更好的反映产品的构造过程。

写在最后

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

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

  1. [学习笔记]设计模式之Abstract Factory

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在上篇笔记Builder设计模式中,时の魔导士祭出了自己的WorldCreator.尽管它因此能创造出一个有山有树有房子的世界,但是白 ...

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

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Adapter(适配器)模式主要解决接口不匹配的问题.为此,让我们要回到最初Builder模式创建平行世界时,白雪公主和小霍比特人的谜 ...

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

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

  4. [学习笔记]设计模式之Proxy

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 “魔镜啊魔镜,谁是这个世界上最美丽的人?” 每到晚上,女王都会问魔镜相同的问题(见Decorator模式).这是她还曾身为女巫时留下的 ...

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

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

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

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

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

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

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

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在Composite(组合)模式中,用户可以使用多个简单的组件以形成较大的组件,而这些组件还可能进一步组合成更大的.它重要的特性是能够 ...

  9. [学习笔记]设计模式之Flyweight

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 Flyweight(享元)模式运用共享技术,可以有效地支持大量细粒度的对象.今天我们会去参观小霍比特人们的酿酒工坊……等等,不是享元模 ...

随机推荐

  1. jQuery设置checkbox全选(区别jQuery版本)

    jQuery设置checkbox全选在网上有各种文章介绍,但是为什么在我们用他们的代码的时候就没有效果呢? 如果你的代码一点错误都没有,先不要急着怀疑人家代码的正确性,也许只是人家跟你用的jQuery ...

  2. RESTheart官方文档

    作者:Andrea Di Cesare, Maurizio Turatti RESTHeart是SoftInstigate公司创建并开发的开源项目. MongoDB的WEB操作接口 RESTHEART ...

  3. CoreGraphics --- CGContext

    CGContext又叫图形上下文,相当于一块画布,以堆栈形式存放,只有在当前context上绘图才有效.iOS有分多种图形上下文,其中UIView自带提供的在drawRect:方法中通过UIGraph ...

  4. UM_第三方登录

    参考官方文档(http://dev.umeng.com/social/ios/detail-share#7), 做出以下总结. 第三方登录主要用于简化用户登录流程,通过用户拥有的微博.QQ.微信等第三 ...

  5. mysql 时间字段的函数 timestamp

    Mysql 里格式 时间字段的函数 DATE_FORMAT unix_timestamp - 墨墨修行的日志 - 网易博客http://jjuanxi.blog.163.com/blog/static ...

  6. Word文献类型标志

    文献类型标识:专著:M:论文集:C:报纸文章:W:期刊文章:J:学位论文:D:研究报告:R:各种未定类型的文献:Z文献类型标识:专著:M:论文集:C:报纸文章:W:期刊文章:J:学位论文:D:研究报告 ...

  7. Gabor变换

    Gabor变换 Gabor变换属于加窗傅立叶变换,Gabor函数可以在频域不同尺度.不同方向上提取相关的特征.另外Gabor函数与人眼的生物作用相仿,所以经常用作纹理识别上,并取得了较好的效果.Gab ...

  8. 将excel数据导入内表的函数

    call function 'TEXT_CONVERT_XLS_TO_SAP'    exporting      i_tab_raw_data       = lt_raw "开始行    ...

  9. Desert King

    poj2728:http://poj.org/problem?id=2728 题意:给你n的点,每一个点会有一个坐标(x,y),然后还有一个z值,现在上你求一棵生成树,是的这棵生成树的所有边的费用/所 ...

  10. PHP 判断从表单提交的值是否为空

    @$time = $_GET['time'];if(empty($time)) { echo "empty";} else { echo "not empty" ...