1.5设计模式之建造者模式(Builder)

1.5.1 模式意图:

当面临一个复杂对象的创建时,通常由各个子对象按照一定的顺序组合而成;随着需求的不断变化,复杂对象对应的各个子对象也随之变化,但组合顺序相对稳定。我们需要将一个复杂的构建与其表示相分离,使得同样的构建过程或顺序可以创建不同的复杂对象。使用建造者模式即可达到这样的目的。

1.5.2 模式概念:

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

1.5.3 模式元素:

  • 抽象建造者(AbstractBuilder)
  • 具体建造者(HouseBuilder)
  • 指挥者(Director)
  • 产品角色(House、Cement、Brick等)

1.5.4 代码示例:

写一个建造房子的例子:首先你要准备水泥,钢筋,砖块,然后盖一层、二层、三层直到最高层,通电,通水等等多道“工序”才能完成成品。其中每一道工序都是对产品的全新创建、但是我们只关心最后的终极产品---【房子】。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Custom.Log;

# region BaseClass
/// <summary>
/// 水泥
/// </summary>
public class Cement { }
/// <summary>
/// 砖块
/// </summary>
public class Brick { }
/// <summary>
/// 钢筋
/// </summary>
public class Rebar { }
/// <summary>
/// 楼层
/// </summary>
public class Floor { }

/// <summary>
/// 水
/// </summary>
public class Water { }
/// <summary>
/// 电
/// </summary>
public class Electricity { }
/// <summary>
/// 房屋
/// </summary>
public class House { }
#endregion
public abstract class AbstractBuilder
{
    public abstract void Build_Cement();
    public abstract void Build_Brick();
    public abstract void Build_Rebar();
    public abstract void Build_Floor();
    public abstract void Build_Water();
    public abstract void Build_Electricity();
    public abstract House Build_House();
}

public class HouseBuilder : AbstractBuilder
{
    public override void Build_Brick()
    {
        this.Log("建造砖块");
    }
    public override void Build_Cement()
    {
        this.Log("建造水泥");
    }
    public override void Build_Electricity()
    {
        this.Log("建造电力");
    }
    public override void Build_Floor()
    {
        this.Log("建造楼层");
    }
    public override void Build_Rebar()
    {
        this.Log("建造钢筋");
    }
    public override void Builde_Water()
    {
        this.Log("建造水");
    }
    public override House Build_House()
    {
        this.Log("房屋建造完成");
        return new House();
    }
}

public class Director
{
    private AbstractBuilder abstractBuilder = null;

    public Director(AbstractBuilder tempAbstractBuilder)
    {
        abstractBuilder = tempAbstractBuilder;
    }

    public House GetResult()
    {
        abstractBuilder.Build_Brick();//建造砖块
        abstractBuilder.Build_Cement();//建造水泥
        abstractBuilder.Build_Rebar();//建造钢筋
        abstractBuilder.Build_Floor();//建造楼层
        abstractBuilder.Build_Electricity();//电力完成
        abstractBuilder.Build_Water();//水源完成
        House temp =  abstractBuilder.Builde_House();//成品房屋完成
        this.Log($"完全品{temp}完成");
        return temp;
    }
}

调用

public class BuilderComponent : MonoBehaviour
{
    void Start()
    {
        AbstractBuilder abstractBuilder = new HouseBuilder();
        Director director = new Director(abstractBuilder);
        House house = director.GetResult();
    }
}

打印信息

1.5.5 写法对比:

1.5.6 模式分析:

下面笔者和大家具体分析一下建造者模式,其实这个和抽象工厂模式特别的相似,都是创建产品,但建造者模式只是把原有工厂的创建函数交给了指挥者(Director)来执行。从写法上讲,我认为【AbstractFactoryPlus】【Builder】更贴切,就是让多行的创建代码演变成现在的Director director = new Director(abstractBuilder);House house = director.GetResult();,从结构上看是把原本的创建过程再次的封装和转移,如果想修改创建过程可以直接在Director类中修改。最后通过Director组装,在组装的过程中可以不断的向一个容器内添加,获取最终的产品。

从职责划分上看:
【抽象工厂】主要是没有元素或者说一个元素对应一个产品,以这种方式产出多个产品。
【建造者模式】是多个元素建造一个产品。目的是关心一个复杂产品的构建过程(再次封装和转移)。

指挥者(Director)主要是用于创建一些复杂的对象,这些对象内部的构建顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。不过真的需要建造顺序更改时,我们也可以重新定义一个新的Director,只需要把原有的Director类替换掉就可以,上层的改动也相对较小,绝对的开闭原则是不存在的,相对的开闭才是我们应该掌握的。

【扩展说明】

或许有人会问,为什么不用接口代替对应的AbstractBuilder呢?
笔者是这样理解的,接口是一个独立的功能,不是一个系统。以手机为例:它含有上网、听音乐、看电影、储存、拍照、收发信息等功能,每一个功能都可以是一个接口。且这种接口具有通用性。

问什么说是通用性?
例如上网功能接口电脑也可以继承,拍照这种功能接口专业摄像机也可以继承这就是通用性。这种微小独立功能的划分也符合接口隔离原则

但是抽象类呢,它更像一个特殊的缩小系统,就像手机是众多电子产品中的一种,但手机的品牌很多,基于这种缩小系统基本一致性的特点,不同的品牌,我们就可以根据手机抽象类实现不同的品牌实例。而且在抽象基类里面可以实现一些字段和函数,接口则不能。

接口是能够,抽象是含有。

【举例说明】

List这种就是集各种通用功能于一身的

这种文件操作的抽象类就很局限了

1.5.7 应用场景:

  • 生成的对象具有复杂的内部结构。
  • 生成的对象内部属性本身相互依赖。

1.5.8 小结:

  • 笔者认为,如果构建过程相对简单,2-5行代码可以解决的问题用工厂模式就可以了,如果十分复杂的创建就可以考虑下建造者模式。

  • 建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品的组装顺序和细节,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者(示例中的BuilderHouse)就可以了。


更多设计模式详见:设计模式全家桶

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容