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#的工程化实现及扩展》

  • 相关阅读:
    Maven关于web.xml中Servlet和Servlet映射的问题
    intellij idea的Maven项目运行报程序包找不到的错误
    修改Maven项目默认JDK版本
    刷题15. 3Sum
    刷题11. Container With Most Water
    刷题10. Regular Expression Matching
    刷题5. Longest Palindromic Substring
    刷题4. Median of Two Sorted Arrays
    刷题3. Longest Substring Without Repeating Characters
    刷题2. Add Two Numbers
  • 原文地址:https://www.cnblogs.com/zhixin9001/p/13263044.html
Copyright © 2011-2022 走看看