zoukankan      html  css  js  c++  java
  • 设计模式(4) 建造者模式

    • 什么是建造者模式
    • 经典建造者模式的优缺点
    • 对建造者模式的扩展

    什么是建造者模式

    建造者模式将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。
    虽然与工厂模式、抽象工厂模式、单件模式同为创建型模式,但建造者模式与之前学习的模式相比,更为关注创建过程的细节,它一般用于创建复杂对象,从独立创建每个部分到最后的组装,它承担每个步骤的工作。由于它把创建每个部分都独立为一个单一的过程,因此不仅可以完成较为精细的创建,还可以根据创建步骤编排,生成不同的目标实例。
    GOF对建造者模式的描述是:
    Separate the construction of a complex object from its representation so that the same construction process can create different representations..
    — Design Patterns : Elements of Reusable Object-Oriented Software

    创建者模式非常适用于产品局部加工过程变化较大,但组装过程相对固定的场景。
    比如电脑的组装,基本的组装过程是固定的,但是具体主板、CPU、显卡、内存、硬盘等选择的品牌和型号可能差异很大;还有汽车的生产也是这样,整体组装过程基本相同,但不同品牌、价格的汽车在具体部件上差异很大。

    其UML类图如下:
    markdown

    其中包括三个角色:

    • IBuilder:负责描述创建一个产品各个组成的抽象接口。
    • Concrete Builder:实现IBuilder要求的内容,并且提供一个获得产品的方法。
    • Director:基于IBuilder定义的构造产品的抽象步骤,指导Concrete Builder生成产品的过程。

    这里的产品类型并没有统一的IProduct接口,主要是因为经过不同ConcreteBuilder加工后的产品差别相对较大,给它一个公共的基准抽象对象意义不大,

    代码实现:

    public class House
    {
        public void AddWindowAndDoor() { }
        public void AddWallAndFloor() { }
        public void AddCeiling() { }
    }
    
    public class Car
    {
        public void AddWheel() { }
        public void AddEngine() { }
        public void AddBody() { }
    }
    
    public interface IBuilder
    {
        void BuildPart1();
        void BuildPart2();
        void BuildPart3();
    }
    
    public class CarBuilder : IBuilder
    {
        private Car car;
        public void BuildPart1()
        {
            car.AddEngine();
        }
        public void BuildPart2()
        {
            car.AddWheel();
        }
        public void BuildPart3()
        {
            car.AddBody();
        }
    }
    
    public class HouseBuilder : IBuilder
    {
        private House house;
        public void BuildPart1()
        {
            house.AddWallAndFloor();
        }
        public void BuildPart2()
        {
            house.AddCeiling();
        }
        public void BuildPart3()
        {
            house.AddWindowAndDoor();
        }
    }
    
    public class Director
    {
        public void Construct(IBuilder builder)
        {
            builder.BuildPart1();
            builder.BuildPart2();
            builder.BuildPart3();
        }
    }
    

    调用:

    [Test]
    public void BuilderTest()
    {
        Director director = new Director();
        CarBuilder carBuilder = new CarBuilder();
        HouseBuilder houseBuilder = new HouseBuilder();
    
        director.Construct(carBuilder);
        director.Construct(houseBuilder);
    
        Assert.AreEqual(typeof(Car), carBuilder.Car.GetType());
        Assert.AreEqual(typeof(House), houseBuilder.House.GetType());
    }
    

    经典建造者模式的优缺点

    • 优点:创建者模式将复杂对象的每个组成创建步骤暴露出来,借助Director(或客户程序自己)既可以选择其执行次序,也可以选择要执行哪些步骤。上述过程可以在应用中动态完成,相比较工厂方法和抽象工厂模式的一次性创建过程而言,创建者模式适合创建“更为复杂且每个组成变化较多”的类型。
    • 缺点:但建造者模式也存在一些缺点,比如正因为会暴露出更多的执行步骤,这就需要需要Director(或客户程序)具有更多的领域知识,使用不慎很容易造成相对更为紧密的耦合。

    而且经典建造者中IBuilder定义了数目固定的装配动作,而Director有把这些动作的执行顺序也固定了,虽然建造者模式可以生产差异非常大的产品,但要求这些产品具有固定的装配步骤,这就大大局限了这种模式的使用场景,因为现实中这样的要求往往很难满足。不同的产品往往具有数目不同的装配动作和次序,如果要把这样的产品添加到建造者的生产列表中是做不到的,需要另外实现一套,改动比较大。

    对建造者模式的扩展

    上述问题可以通过对经典模式适当优化来解决。
    IBuilder中定义的不同步骤可以进一步抽象为一个Action,CarBuilder和HouseBuilder的代码也很相似,可以提取一个基类,这部分代码放在基类中。
    代码如下:

    public interface IBuilder<T> where T : class, new()
    {
        T BuildUp();
    }
    
    public abstract class BuilderBase<T> : IBuilder<T> where T : class, new()
    {
        protected IList<Action> steps = new List<Action>();
    
        protected T product = new T();
        public virtual T BuildUp()
        {
            foreach (Action step in steps)
            {
                step();
            }
            return product;
        }
    }
    
    public class ConcreteCarBuilder : BuilderBase<Car>
    {
        public ConcreteCarBuilder() : base()
        {
            steps.Add(product.AddEngine);
            steps.Add(product.AddWheel);
            steps.Add(product.AddBody);
        }
    }
    
    public class ConcreteHouseBuilder : BuilderBase<House>
    {
        public ConcreteHouseBuilder() : base()
        {
            steps.Add(product.AddWallAndFloor);
            steps.Add(product.AddCeiling);
            steps.Add(product.AddWindowAndDoor);
        }
    }
    

    实体Builder兼做Director,具体的构建步骤由实体Builder来决定,这样做的好处是非常灵活,不同的Builder可以有不同数目的动作,动作的顺序也可以自行安排。

    调用:

    [Test]
    public void DelegateBuilderTest()
    {
        IBuilder<Car> builder = new ConcreteCarBuilder();
        var product = builder.BuildUp();
        Assert.AreEqual(typeof(Car), product.GetType());
    
        IBuilder<House> builder1 = new ConcreteHouseBuilder();
        var product1 = builder1.BuildUp();
        Assert.AreEqual(typeof(House), product1.GetType());
    }
    

    参考书籍:
    王翔著 《设计模式——基于C#的工程化实现及扩展》

  • 相关阅读:
    团队开发项目客户端——游戏子系统的设计(上)
    团队开发项目客户端——注册子系统的设计
    团队项目开发客户端——登录子系统的设计
    协程库
    Linux下用命令查看CPU ID以及厂家等信息
    c++中字符串的截取:
    C++ STL 的各结构实现
    关于mysql查询最近一条记录
    BerkeleyDB原理及其对应API
    高性能服务器设计(Jeff Darcy's notes on high-performance server design
  • 原文地址:https://www.cnblogs.com/zhixin9001/p/13263044.html
Copyright © 2011-2022 走看看