zoukankan      html  css  js  c++  java
  • 设计模式-建造模式

    在阎宏博士的《JAVA与模式》一书中开头是这样描述建造(Builder)模式的:

    建造模式是对象的创建模式。

    建造模式可以将一个产品的内部表象(internal representation)与产品的生产过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。(内容转自此文章)

     产品的内部表象

      一个产品常有不同的组成成分作为产品的零件,这些零件有可能是对象,也有可能不是对象,它们通常又叫做产品的内部表象(internal representation)。不同的产品可以有不同的内部表象,也就是不同的零件。使用建造模式可以使客户端不需要知道所生成的产品有哪些零件,每个产品的对应零件彼此有何不同,是怎么建造出来的,以及怎么组成产品。

    建造模式的结构

    在这个示意性的系统里,最终产品Product只有两个零件,即part1和part2。相应的建造方法也有两个:buildPart1()和buildPart2()、同时可以看出本模式涉及到四个角色,它们分别是:

      抽象建造者(Builder)角色:给 出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者 (ConcreteBuilder)角色。具体建造者类必须实现这个接口所要求的两种方法:一种是建造方法(buildPart1和 buildPart2),另一种是返还结构方法(retrieveResult)。一般来说,产品所包含的零件数目与建造方法的数目相符。换言之,有多少 零件,就有多少相应的建造方法。

      具体建造者(ConcreteBuilder)角色:担任这个角色的是与应用程序紧密相关的一些类,它们在应用程序调用下创建产品的实例。这个角色要完成的任务包括:1.实现抽象建造者Builder所声明的接口,给出一步一步地完成创建产品实例的操作。2.在建造过程完成后,提供产品的实例。

      导演者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象。应当指出的是,导演者角色并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者角色。

      产品(Product)角色:产品便是建造中的复杂对象。一般来说,一个系统中会有多于一个的产品类,而且这些产品类并不一定有共同的接口,而完全可以是不相关联的。

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

     

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

    代码

    /**
     * 产品类
     * */
    class Product {
        var part1: String = ""
        var part2: String = ""
    }
    /**
     * 抽象建造者类IBuilder
     * */
    interface IBuilder {
        fun builderPart1()
        fun builderPart2()
        fun retrieveResult(): Product
    }
    /**
     * 具体建造者
     * */
    class ConcreteBuilder : IBuilder {
    
        private val product = Product();
    
        override fun builderPart1() {
            product.part1 = "编号:9527"
        }
    
        override fun builderPart2() {
            product.part2 = "名称:xxxx"
        }
    
        override fun retrieveResult(): Product {
            return product
        }
    }
    /**
     *  导演者类
     * */
    class Director constructor(builder_: IBuilder) {
        /**
         * 持有当前需要使用的建造器对象
         */
        private var builder: IBuilder? = null
    
        init {
            this.builder = builder_
        }
    
        fun construct() {
            //开始构建产品
            this.builder?.builderPart1()
            this.builder?.builderPart2()
        }
    }

    测试

    val builder = ConcreteBuilder()
    //客户端把具体建造者对象交给导演者,导演者操作具体建造者,开始创建产品
    val director = Director(builder)
    //开始创建产品
    director.construct()
    //当产品完成后,建造者把产品返还给客户端。
    val product = builder.retrieveResult()
    println(product.part1)
    println(product.part2)

    使用场景

    使用建造模式构建复杂对象

      考虑这样一个实际应用,要创建一个保险合同的对象,里面很多属性的值都有约束,要求创建出来的对象是满足这些约束规则的。约束规则比如:保险合同通常情况下可以和个人签订,也可以和某个公司签订,但是一份保险合同不能同时与个人和公司签订。这个对象里有很多类似这样的约束,采用建造模式来构建复杂的对象,通常会对建造模式进行一定的简化,因为目标明确,就是创建某个复杂对象,因此做适当简化会使程序更简洁。大致简化如下:

      ●  由于是用Builder模式来创建某个对象,因此就没有必要再定义一个Builder接口,直接提供一个具体的建造者类就可以了。

      ●  对于创建一个复杂的对象,可能会有很多种不同的选择和步骤,干脆去掉“导演者”,把导演者的功能和Client的功能合并起来,也就是说,Client这个时候就相当于导演者,它来指导构建器类去构建需要的复杂对象。

    /**
     * 保险合同对象
     */
    class InsuranceContract {
        //保险合同编号
        private var contractId = ""
        /**
         * 被保险人员的名称,同一份保险合同,要么跟人员签订,要么跟公司签订
         * 也就是说,“被保险人员”和“被保险公司”这两个属性,不可能同时有值
         */
        private var personName = ""
        //被保险公司的名称
        private var companyName = ""
    
        //将产品对象的构造函数私有化,防止客户端不使用构建器来构建产品对象,
        private constructor(concreteBuilder_: ConcreteBuilder) {
            this.contractId = concreteBuilder_.contractIdValue
            this.personName = concreteBuilder_.personNameValue
            this.companyName = concreteBuilder_.companyNameValue
        }
    
        fun someOperation() {
            println("current operation contract id is: ${this.contractId}")
        }
    
        //成员内部类
        //关键在于需要用inner关键字来修饰内部类的声明,才能让Kotlin中默认的静态内部类变为非静态的成员内部类
        //Kotlin默认内部类是静态内部类,和Java相反
        class ConcreteBuilder {
            private var contractId = ""
            private var personName = ""
            private var companyName = ""
    
            constructor(contractId: String) {
                this.contractId = contractId
            }
    
            //为了不让创建builder时让外部设置到这里相关属性,所以用以下by lazy方式,并只读
            //大家有没其它更好方法。。
            val contractIdValue: String by lazy {
                this.contractId
            }
    val personNameValue: String by lazy {
    this.personName }
    val companyNameValue: String by lazy {
    this.companyName } fun setPersonName(personName: String): ConcreteBuilder { this.personName = personName return this } fun setCompanyName(companyName: String): ConcreteBuilder { this.companyName = companyName return this } fun build(): InsuranceContract { if (contractId.isEmpty()) throw IllegalArgumentException("contract id cannot be null") val signPerson = personName.isNotEmpty() val signCompany = companyName.isNotEmpty() if (signPerson && signCompany) { throw IllegalArgumentException("一份保险合同不能同时与个人和公司签订") } if (!signPerson && !signCompany) { throw IllegalArgumentException("一份保险合同不能没有签订对象") } return InsuranceContract(this) } } }

    客户端

    val builder = InsuranceContract.ConcreteBuilder("95237")
    //设置需要的数据,然后构建保险合同对象
    val contract = builder.setCompanyName("google").build()
    contract.someOperation()
  • 相关阅读:
    [CSP-S模拟测试]:party?(霍尔定理+最小割+树链剖分)
    [CSP-S模拟测试]:marshland(最大费用可行流)
    [CSP-S模拟测试]:Revive(点分治)
    [CSP-S模拟测试]:Lighthouse(哈密顿回路+容斥)
    [CSP-S模拟测试]:Lost My Music(凸包)
    [CSP-S模拟测试]:God Knows(线段树维护单调栈)
    [CSP-S模拟测试]:Star Way To Heaven(最小生成树Prim)
    [CSP-S模拟测试]:gcd(莫比乌斯反演)
    [CSP-S模拟测试]:water(BFS)
    BZOJ3462 DZY Loves Math II 【多重背包 + 组合数】
  • 原文地址:https://www.cnblogs.com/johnnyzhao/p/10407381.html
Copyright © 2011-2022 走看看