设计模式之五——建造者模式

作者: dd299 | 来源:发表于2019-06-24 12:17 被阅读15次

    原文传送门

    1 介绍

    建造者模式(Builder),又叫生成器模式。是对象的创建模式。建造者模式可以将产品的内部表象与产品的生成过程分割开来。从而可以使一个建造过程生成具有不同的内部表象的产品对象。

    1.1 什么是建造者模式

    将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    1.2 解决什么样的问题

    如果我们用了建造者模式,那么用户就只需指定需要建造的类型就可以得到它们,而具体的建造过程和细节就不需要知道了。

    由于建造角色的过程比较复杂,其中还有相互依赖关系,所以我们使用建造者模式将将建造复杂对象的过程和组成对象的部件解耦。这样既保证了基本属性全都一致(这里的一致指的是该包含的应该全都包含)也封装了其中的具体实现细节。

    同时,在修改某个具体角色的时候我们只需要修改对应的具体角色就可以了,不会影响到其他角色。

    如果需要新增角色,只要再增加一个具体建造者,并在该建造者中写好具体细节的建造部分代码就OK了。

    2 原理

    • 参与者
      • Director:导演者。担任这个角色的类调用具体建造者角色以创建产品对象。一般不与产品类发生依赖关系。一般来说,Director被用来封装程序中易变的部分。
      • Builder:抽象建造者。给 出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是 ConcreteBuilder。ConcreteBuilder必须实现这个接口所要求的两种方法:一种是建造方法(buildPart1和 buildPart2),另一种是返还结构方法(retrieveResult)。一般来说,产品所包含的零件数目与建造方法的数目相符。换言之,有多少零件,就有多少相应的建造方法。
      • ConcreteBuilder:具体建造者。它们在应用程序调用下创建产品的实例。这个角色要完成的任务包括:1.实现抽象建造者Builder所声明的接口,给出一步一步地完成创建产品实例的操作。2.在建造过程完成后,提供产品的实例。
      • Product:产品。产品便是建造中的复杂对象。一般来说,一个系统中会有多于一个的产品类,而且这些产品类并不一定有共同的接口,而完全可以是不相关联的。

    导演者角色是与客户端打交道的角色。导演者将客户端创建产品的请求划分为对各个零件的建造请求,再将这些请求委派给具体建造者角色。具体建造者角色是做具体建造工作的,但是却不为客户端所知。

    一般来说,每有一个产品类,就有一个相应的具体建造者类。这些产品应当有一样数目的零件,而每有一个零件就相应地在所有的建造者角色里有一个建造方法。

    2.1 uml图

    • 类图


      类图
    • 时序图
    时序图

    客户端负责创建导演者和具体建造者对象。然后,客户端把具体建造者对象交给导演者,导演者操作具体建造者,开始创建产品。当产品完成后,建造者把产品返还给客户端。

    把创建具体建造者对象的任务交给客户端而不是导演者对象,是为了将导演者对象与具体建造者对象的耦合变成动态的,从而使导演者对象可以操纵数个具体建造者对象中的任何一个。

    2.2 代码示例

    Director代码示例

    public class Director {
    
        /**
         * 持有当前需要使用的建造器对象
         */
        private Builder builder;
        /**
         * 构造方法,传入建造器对象
         * @param builder 建造器对象
         */
        public Director(Builder builder){
            this.builder = builder;
        }
        /**
         * 产品构造方法,负责调用各个零件建造方法
         */
        public void construct(){
            builder.buildPart1();
            builder.buildPart2();
        }
    
    }
    
    

    Builder代码示例

    public interface Builder {
    
        void buildPart1();
    
        void buildPart2();
    
        Product retrieveResult();
    
    }
    

    ConcreteBuilder代码示例

    public class ConcreteBuilder implements Builder {
    
        private Product product = new Product();
    
        /**
         * 产品零件建造方法1
         */
        @Override
        public void buildPart1() {
            //构建产品的第一个零件
            product.setPart1("编号:9527");
        }
    
        /**
         * 产品零件建造方法2
         */
        @Override
        public void buildPart2() {
            //构建产品的第二个零件
            product.setPart2("名称:XXX");
    
        }
    
        /**
         * 产品返还方法
         */
        @Override
        public Product retrieveResult() {
            return product;
        }
    }
    
    

    Product代码示例

    public class Product {
    
        /**
         * 定义一些关于产品的操作
         */
        private String part1;
        private String part2;
        public String getPart1() {
            return part1;
        }
        public void setPart1(String part1) {
            this.part1 = part1;
        }
        public String getPart2() {
            return part2;
        }
        public void setPart2(String part2) {
            this.part2 = part2;
        }
    }
    
    

    调用示例

        public static void main(String[] args) {
            Builder builder = new ConcreteBuilder();
            Director director = new Director(builder);
            director.construct();
            Product product = builder.retrieveResult();
            System.out.println(product.getPart1());
            System.out.println(product.getPart2());
        }
    

    运行结果

    编号:9527
    名称:XXX
    

    2.3 优缺点

    • 优点
      • 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
      • 用户使用不同的具体建造者即可得到不同的产品对象 。
      • 可以更加精细地控制产品的创建过程 。
      • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
    • 缺点
      • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

      • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

    3 适用场景

    在以下情况下可以使用建造者模式:

    1. 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。

    2. 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。

    3. 对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。

    4. 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

    4 与其他模式的关系

    4.1 建造者模式与策略模式的区别

    建造者模式在结构上很接近于策略模式,事实上建造者模式是策略模式的一种特殊情况。

    二者的区别在于用意不同。
    建造者模式作用于客户端一点一点的建造新的对象。不同类型的具体建造者虽然都拥有相同的接口,但是他们所创建出来的对象则可能完全不同。
    而策略模式的目的是为算法提供抽象的接口。一个具体策略类把一个算法包装到一个对象里面,而不同额具体策略对象为一种一般性的服务提供不同的实现。

    4 总结

    在建造者模式的结构中引入了一个Director类,该类的作用主要有两个:一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程。Director针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过Director类调用建造者的相关方法,返回一个完整的产品对象。

    建造者模式分成两个很重要的部分:

    1. 一个部分是Builder接口,这里是定义了如何构建各个部件,也就是知道每个部件功能如何实现,以及如何装配这些部件到产品中去;

    2. 另外一个部分是Director,Director是知道如何组合来构建产品,也就是说Director负责整体的构建算法,而且通常是分步骤地来执行。

    不管如何变化,建造模式都存在这么两个部分,一个部分是部件构造和产品装配,另一个部分是整体构建的算法。认识这点是很重要的,因为在建造模式中,强调的是固定整体构建的算法,而灵活扩展和切换部件的具体构造和产品装配的方式。

    再直白点说,建造模式的重心在于分离构建算法和具体的构造实现,从而使得构建算法可以重用。具体的构造实现可以很方便地扩展和切换,从而可以灵活地组合来构造出不同的产品对象。


    参考书籍及文章
    1.《Java与模式》,电子工业出版社,阎宏

    1. 《大话设计模式》,清华大学出版社,程杰
    2. 《设计模式——可复用面向对象软件的基础》,机械工业出版社,Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides
    3. 《23种设计模式(4):建造者模式》, https://blog.csdn.net/zhengzhb/article/details/7375966
    4. 《图说设计模式》,https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/builder.html

    相关文章

      网友评论

        本文标题:设计模式之五——建造者模式

        本文链接:https://www.haomeiwen.com/subject/sumnqctx.html