设计模式——建造者模式

前面讲述了工厂模式,主要用于创建对象;建造者模式与工厂模式的相同之处在于都属于创建型的设计模式,即都是为解决方便创建对象而产生的设计模式!

不同之处在于,工厂模式一般用于创建产品本身较为简单,但是产品种类比较多,产品分类较复杂的场景;建造者模式一般用于创建产品本身比较复杂,但是不同产品的创建都需要遵循一定的流程章法,并且产品在组成上不存在很大的相似性,而展现有较大差异。

建造者模式的思想

产品 - 可以是抽象类,也可以是普通类

抽象建造者 - 约束建造所需流程。抽象类

实际建造者(继承与抽象建造者)  - 实际生产产品的类

导演类 - 用于操作实际建造者,生产产品;

下面看经典的代码:

/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 抽象的建造者
 *              可以约束真实建造者可能需要执行的流程,一定程度上保证某些流程不被遗漏
 *              也可以只有一个produce方法,具体实现交由子类,但这样在一定程度上就放松了约束
 * @author panteng
 * @date 17-2-4.
 */
public abstract class AbstractBuilder {
    public abstract void producePart1();
    public abstract void producePart2();
    public abstract void producePart3();

    /**
     *
     * @return Product可以是具体的类,也可以是抽象类
     */
    public abstract Product getProduct();
}
AbstractBuilder

 

/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 导演类
 *              导演类和产品没有直接关系,和建造者有直接关系,只按照一定的规则操作建造者生产产品
 * @author panteng
 * @date 17-2-4.
 */
public class Director {
    private AbstractBuilder builder;

    public Director(){
    }

    public Director(AbstractBuilder inBuilder){
        this.builder = inBuilder;
    }

    /**
     * @description 在此保证建造者必须执行那些流程
     * @return
     */
    public Product buildProduct(){
        builder.producePart1();
        builder.producePart2();
        builder.producePart3();
        return builder.getProduct();
    }

    public AbstractBuilder getBuilder(){
        return builder;
    }
    public void setBuilder(AbstractBuilder builder){
        this.builder = builder;
    }
}
Director

 

/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 产品对象
 *              可以是抽象类
 * @author panteng
 * @date 17-2-4.
 */
public class Product {
    public String part1;
    public String part2;
    public String part3;

    public String getPart1(){
        return part1;
    }
    public void setPart1(String part1){
        this.part1 = part1;
    }
    public String getPart3(){
        return part3;
    }
    public void setPart3(String part3){
        this.part3 = part3;
    }
    public String getPart2(){
        return part2;
    }
    public void setPart2(String part2){
        this.part2 = part2;
    }

    /**
     * Returns a string representation of the object. In general, the
     * {@code toString} method returns a string that
     * "textually represents" this object. The result should
     * be a concise but informative representation that is easy for a
     * person to read.
     * It is recommended that all subclasses override this method.
     * <p>
     * The {@code toString} method for class {@code Object}
     * returns a string consisting of the name of the class of which the
     * object is an instance, the at-sign character `{@code @}', and
     * the unsigned hexadecimal representation of the hash code of the
     * object. In other words, this method returns a string equal to the
     * value of:
     * <blockquote>
     * <pre>
     * getClass().getName() + '@' + Integer.toHexString(hashCode())
     * </pre></blockquote>
     *
     * @return a string representation of the object.
     */
    @Override
    public String toString(){
        return this.part1 + " - " + this.part2 + " - " + this.part3;
    }
}
Product

 

/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 实际建造者
 * @author panteng
 * @date 17-2-4.
 */
public class RealBuilder extends AbstractBuilder {
    private Product product = new Product();
    @Override
    public void producePart1(){
        product.setPart1("p1");
    }
    @Override
    public void producePart2(){
        product.setPart2("p2");
    }
    @Override
    public void producePart3(){
        product.setPart3("p3");
    }

    /**
     *
     * @return Product可以是具体的类,也可以是抽象类
     */
    @Override
    public Product getProduct(){
        return product;
    }
}
RealBuilder

 

/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 实际建造者
 * @author panteng
 * @date 17-2-4.
 */
public class RealBuilder2 extends AbstractBuilder {
    private Product product = new Product();
    @Override
    public void producePart1(){
        product.setPart1("p2:1");
    }
    @Override
    public void producePart2(){
        product.setPart2("p2:2");
    }
    @Override
    public void producePart3(){
        product.setPart3("p2:3");
    }

    /**
     *
     * @return Product可以是具体的类,也可以是抽象类
     */
    @Override
    public Product getProduct(){
        return product;
    }
}
RealBuilder2

 

/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

import org.junit.Test;

/**
 * @description 测试建造者模式
 * @author panteng
 * @date 17-2-4.
 */
public class BuilderModelTest {
    @Test
    public void builderModelTest(){
        RealBuilder builder = new RealBuilder();
        Director director = new Director(builder);
        Product product = director.buildProduct();
        System.out.println(product);

        RealBuilder2 builder2 = new RealBuilder2();
        director.setBuilder(builder2);
        product = director.buildProduct();
        System.out.println(product);

    }
}
BuilderModelTest

 

个人认为,以上设计模式有一个不足之处,即一个builder只能生产一个产品;

解决此不足之处的方案:在AbstractBuilder中增加一个设置product的方法,在director的 buildProduct方法中,new一个新的product,然后注入。这样做的缺点是在破坏了导演类与创建产品无关的约定。实践是检验真理的标准,如果这种方案能很好的解决经典模式的不足,更好的在实践中应用,也未尝不可。欢迎各位吐槽

 

修改后代码如下:

/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 抽象的建造者
 *              可以约束真实建造者可能需要执行的流程,一定程度上保证某些流程不被遗漏
 *              也可以只有一个produce方法,具体实现交由子类,但这样在一定程度上就放松了约束
 * @author panteng
 * @date 17-2-4.
 */
public abstract class AbstractBuilder {
    Product product = new Product();

    public abstract void producePart1();
    public abstract void producePart2();
    public abstract void producePart3();

    /**
     *
     * @return Product可以是具体的类,也可以是抽象类
     */
    public abstract Product getProduct();

    public void setProduct(Product product){
        this.product = product;
    }
}
AbstractBuilder
/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 实际建造者
 * @author panteng
 * @date 17-2-4.
 */
public class RealBuilder extends AbstractBuilder {

    @Override
    public void producePart1(){
        product.setPart1("p1");
    }
    @Override
    public void producePart2(){
        product.setPart2("p2");
    }
    @Override
    public void producePart3(){
        product.setPart3("p3");
    }

    /**
     *
     * @return Product可以是具体的类,也可以是抽象类
     */
    @Override
    public Product getProduct(){
        return product;
    }
}
RealBuilder
/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 实际建造者
 * @author panteng
 * @date 17-2-4.
 */
public class RealBuilder2 extends AbstractBuilder {
    private Product product = new Product();
    @Override
    public void producePart1(){
        product.setPart1("p2:1");
    }
    @Override
    public void producePart2(){
        product.setPart2("p2:2");
    }
    @Override
    public void producePart3(){
        product.setPart3("p2:3");
    }

    /**
     *
     * @return Product可以是具体的类,也可以是抽象类
     */
    @Override
    public Product getProduct(){
        return product;
    }
}
RealBuilder2
/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 导演类
 *              导演类和产品没有直接关系,和建造者有直接关系,只按照一定的规则操作建造者生产产品
 * @author panteng
 * @date 17-2-4.
 */
public class Director {
    private AbstractBuilder builder;

    public Director(){
    }

    public Director(AbstractBuilder inBuilder){
        this.builder = inBuilder;
    }

    /**
     * @description 在此保证建造者必须执行那些流程
     * @return
     */
    public Product buildProduct(){
        builder.setProduct(new Product());
        builder.producePart1();
        builder.producePart2();
        builder.producePart3();
        return builder.getProduct();
    }

    public AbstractBuilder getBuilder(){
        return builder;
    }
    public void setBuilder(AbstractBuilder builder){
        this.builder = builder;
    }
}
Director
/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

/**
 * @description 产品对象
 *              可以是抽象类
 * @author panteng
 * @date 17-2-4.
 */
public class Product {
    public String part1;
    public String part2;
    public String part3;

    public String getPart1(){
        return part1;
    }
    public void setPart1(String part1){
        this.part1 = part1;
    }
    public String getPart3(){
        return part3;
    }
    public void setPart3(String part3){
        this.part3 = part3;
    }
    public String getPart2(){
        return part2;
    }
    public void setPart2(String part2){
        this.part2 = part2;
    }

    /**
     * Returns a string representation of the object. In general, the
     * {@code toString} method returns a string that
     * "textually represents" this object. The result should
     * be a concise but informative representation that is easy for a
     * person to read.
     * It is recommended that all subclasses override this method.
     * <p>
     * The {@code toString} method for class {@code Object}
     * returns a string consisting of the name of the class of which the
     * object is an instance, the at-sign character `{@code @}', and
     * the unsigned hexadecimal representation of the hash code of the
     * object. In other words, this method returns a string equal to the
     * value of:
     * <blockquote>
     * <pre>
     * getClass().getName() + '@' + Integer.toHexString(hashCode())
     * </pre></blockquote>
     *
     * @return a string representation of the object.
     */
    @Override
    public String toString(){
        return super.toString() + " = " + this.part1 + " - " + this.part2 + " - " + this.part3;
    }
}
Product
/*
 * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved
 */

package com.pt.builder;

import org.junit.Test;

/**
 * @description 测试建造者模式
 * @author panteng
 * @date 17-2-4.
 */
public class BuilderModelTest {
    @Test
    public void builderModelTest(){
        RealBuilder builder = new RealBuilder();
        Director director = new Director(builder);
        Product product = director.buildProduct();
        System.out.println(product);
        product = director.buildProduct();
        System.out.println(product);

        RealBuilder2 builder2 = new RealBuilder2();
        director.setBuilder(builder2);
        product = director.buildProduct();
        System.out.println(product);

    }
}
BuilderModelTest

 

建造者模式一般在游戏中使用较多:如不同类型的角色,一般属性相同,展现不同; 不同场景的创建,都要创建天、地、物、人等!

 

 

===========================设计模式系列文章=========================

简单工厂模式

工厂方法模式

抽象工厂模式

建造者模式

原型模式

适配器模式

桥接模式

装饰模式

代理模式

组合模式

门面模式

享元模式

责任链模式

命令模式

中介者模式

备忘录模式

观察者模式

状态模式

策略模式

模板方法模式

访问者模式

posted @ 2017-02-04 17:55  沙中世界  阅读(203)  评论(0编辑  收藏  举报