第十一周_S-设计模式-建造者模式

31 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第 3 天,点击查看活动详情

建造者模式

将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。「大话设计模式」

Java 中 StringBuilder#append 就是一个建造者模式

原型实战

买房子,需要请装修公司服务,(虽然我买不起房子)那么装修公司可以看成一个大对象,里面由吊顶、涂料、地板、地砖等组成。并且这些物品都有不同的类型和档次。

我们只需要选几档就能创出装修公司这么一个对象。

/**
 * 提供了基本信息,保证所有的装修材料都可以按照统一标准进行获取
 */
public interface Matter {
    String scence();//场景:地板/地砖/涂料/吊顶

    String brand();//品牌

    String model();//型号

    BigDecimal price();//价格

    String desc();//描述
}
//一级吊顶
public class LevelOneCeiling implements Matter {
    @Override
    public String scence() {
        return "吊顶";
    }

    @Override
    public String brand() {
        return "装修公司自带";
    }

    @Override
    public String model() {
        return "一级顶";
    }

    @Override
    public BigDecimal price() {
        return new BigDecimal(100);
    }

    @Override
    public String desc() {
        return "一级顶,比较便宜些";
    }
}
//二级吊顶
public class LevelTwoCeiling implements Matter {

    @Override
    public String scence() {
        return "吊顶";
    }

    @Override
    public String brand() {
        return "装修公司自带";
    }

    @Override
    public String model() {
        return "二级顶";
    }

    @Override
    public BigDecimal price() {
        return new BigDecimal(200);
    }

    @Override
    public String desc() {
        return "二级顶,更加豪华";
    }
}

类似的还有地板、地砖、涂料等。

下面是测试类:我们选了档次,然后挨个创建对象并且组装成一个大对象。并最终输出结果

public class TestController {
    public static void main(String[] args) {
        String result = getMatterList(BigDecimal.valueOf(132.52), 1);
        System.out.println(result);
    }

    public static String getMatterList(BigDecimal bigDecimal, Integer level) {
        List<Matter> list = new ArrayList<Matter>(); //装修清单
        BigDecimal price = BigDecimal.ZERO;  //装修价格
        if(1==level){ //豪华
            //吊顶
            LevelTwoCeiling levelTwoCeiling=new LevelTwoCeiling();
            //涂料-多乐士
            DuluxCoat duluxCoat=new DuluxCoat();
            //圣像
            ShengXiangFloor shengXiangFloor=new ShengXiangFloor();
            list.add(levelTwoCeiling);
            list.add(duluxCoat);
            list.add(shengXiangFloor);
            price = price.add(levelTwoCeiling.price());
            price = price.add(duluxCoat.price());
            price = price.add(shengXiangFloor.price());
        }else if(2==level){
            //另外的吊顶、涂料、地板、地砖
        }else if(3==level){
            //xxx
        }
        StringBuilder detail = new StringBuilder("\r\n-----\r\n"+
                "装修清单" + "\r\n" +
                "套餐等级:"+level+"\r\n"+
                "套餐价格:"+price.setScale(2,BigDecimal.ROUND_HALF_UP)+
                " 元\r\n"+
                "房屋面积:"+bigDecimal.doubleValue()+"平米\r\n"+
                "材料清单:"+"\r\n");

        for(Matter matter:list){
            detail.append(matter.scence()).append(":").append(matter.brand()).append("、").
                    append(matter.model()).append("、平米价:").append(matter.price()).
                    append(" 元。\n");
        }
        return detail.toString();
    }

建造者模式

public interface IMenu {
    IMenu appendCeiling(Matter matter);//吊顶

    IMenu appendCoat(Matter matter);//涂料

    IMenu appendFloor(Matter matter); // 地板

    IMenu appendTile(Matter matter); // 地砖

    String getDetail(); // 明细
}
public class Builder {

    public IMenu levelOne(Double area) {
        return new DecorationPackageMenu(area, "豪华欧式").appendTile(new DongPengTile())
            .appendCeiling(new LevelTwoCeiling()) // 吊顶,⼆二级顶
            .appendCoat(new DuluxCoat()) // 涂料料,多乐⼠士
            .appendFloor(new ShengXiangFloor()); // 地板,圣象
    }

    //levelTwo
    //....
}
public class DecorationPackageMenu implements IMenu{

    private List<Matter> list = new ArrayList<Matter>();  //装修清单
    private BigDecimal price = BigDecimal.ZERO;     //装修价格
    private BigDecimal area;                          //面积
    private String grade;   //装修等级

    public DecorationPackageMenu() {
    }

    public DecorationPackageMenu(Double area, String grade) {
        this.area = new BigDecimal(area);
        this.grade = grade;
    }

    @Override
    public IMenu appendCeiling(Matter matter) {
        list.add(matter);
        price=price.add(area.multiply(new BigDecimal("0.2").multiply(matter.price())));
        return this;
    }

    @Override
    public IMenu appendCoat(Matter matter) {
        list.add(matter);
        price = price.add(area.multiply(new
                                        BigDecimal("1.4")).multiply(matter.price()));
        return this;
    }

    @Override
    public IMenu appendFloor(Matter matter) {
        list.add(matter);
        price = price.add(area.multiply(matter.price()));
        return this;
    }

    @Override
    public IMenu appendTile(Matter matter) {
        list.add(matter);
        price = price.add(area.multiply(matter.price()));
        return this;
    }

    @Override
    public String getDetail() {
        StringBuilder detail = new StringBuilder("\r\n-----\r\n"+
                                                 "装修清单" + "\r\n" +
                                                 "套餐等级:"+grade+"\r\n"+
                                                 "套餐价格:"+price.setScale(2,BigDecimal.ROUND_HALF_UP)+
                                                 " 元\r\n"+
                                                 "房屋面积:"+area.doubleValue()+"平米\r\n"+
                                                 "材料清单:"+"\r\n");

        for(Matter matter:list){
            detail.append(matter.scence()).append(":").append(matter.brand()).append("、").
                append(matter.model()).append("、平米价:").append(matter.price()).
                append(" 元。\n");
        }
        return detail.toString();
    }
}

从上面可以看出建造者模式,在创建的时候,可以很明确的知道我那些需要创建,可以直接提示,而且可以随意添加。不怕会漏掉什么东西。比如肯德基等快餐就是‘建造者’模式:一个牛肉汉堡=牛肉片+生菜+面包+芝士(这是基本的,必选)。如果客人有额外要求可以再继续添加东西。

DecorationPackageMenu 上面这个类中,在填充各个对象的时候,需要注意的点是直接返回了 this 。这样添加下一个对象的时候才会直接在此对象上添加。

总结

优点:一些基本对象不会变(而且不会遗忘),组合又会经常变化的情况下,可以选择。

缺点:组合的基本对象过多,会造成类膨胀。比如,我们上面只是囊括了装修的四个方面就建了8个类,甚至每个对象细分可能就有十几个,那就是十几倍。