设计模式-建造者模式

建造者模式

适用场景:

一些基本物料不会变,而其组合经常变化的时候,可以选择建造者模式来改造代码

案例:模拟装修公司对于设计出一些套餐装修服务的场景

装修物料接口:Matter

/**
 * 装修物料
 */
public interface Matter {

    /**
     * 场景;地板、地砖、涂料、吊顶
     */
    String scene();

    /**
     * 品牌
     */
    String brand();

    /**
     * 型号
     */
    String model();

    /**
     * 平米报价
     */
    BigDecimal price();

    /**
     * 描述
     */
    String desc();

}

吊顶(ceiling)

  • 一级顶

    /**
     * 吊顶
     * 品牌;装修公司自带
     * 型号:一级顶
     */
    public class LevelOneCeiling implements Matter {
    
        public String scene() {
            return "吊顶";
        }
    
        public String brand() {
            return "装修公司自带";
        }
    
        public String model() {
            return "一级顶";
        }
    
        public BigDecimal price() {
            return new BigDecimal(260);
        }
    
        public String desc() {
            return "造型只做低一级,只有一个层次的吊顶,一般离顶120-150mm";
        }
    
    }
    
  • 二级顶

    public class LevelTwoCeiling  implements Matter {
    
        public String scene() {
            return "吊顶";
        }
    
        public String brand() {
            return "装修公司自带";
        }
    
        public String model() {
            return "二级顶";
        }
    
        public BigDecimal price() {
            return new BigDecimal(850);
        }
    
        public String desc() {
            return "两个层次的吊顶,二级吊顶高度一般就往下吊20cm,要是层高很高,也可增加每级的厚度";
        }
        
    }
    

涂料

  • 多乐土

    /**
     * 涂料
     * 品牌;多乐士(Dulux)
     */
    public class DuluxCoat  implements Matter {
    
        public String scene() {
            return "涂料";
        }
    
        public String brand() {
            return "多乐士(Dulux)";
        }
    
        public String model() {
            return "第二代";
        }
    
        public BigDecimal price() {
            return new BigDecimal(719);
        }
    
        public String desc() {
            return "多乐士是阿克苏诺贝尔旗下的著名建筑装饰油漆品牌,产品畅销于全球100个国家,每年全球有5000万户家庭使用多乐士油漆。";
        }
        
    }
    
  • 立邦

    public class LiBangCoat implements Matter {
    
        public String scene() {
            return "涂料";
        }
    
        public String brand() {
            return "立邦";
        }
    
        public String model() {
            return "默认级别";
        }
    
        public BigDecimal price() {
            return new BigDecimal(650);
        }
    
        public String desc() {
            return "立邦始终以开发绿色产品、注重高科技、高品质为目标,以技术力量不断推进科研和开发,满足消费者需求。";
        }
    
    }
    

然后是地板的几个品牌,地砖的几个品牌,和上边一样,实现matter接口

ifelse实现

public class DecorationPackageController {

    public String getMatterList(BigDecimal area, 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(area.multiply(new BigDecimal("0.2")).multiply(levelTwoCeiling.price()));
            price = price.add(area.multiply(new BigDecimal("1.4")).multiply(duluxCoat.price()));
            price = price.add(area.multiply(shengXiangFloor.price()));

        }

        // 轻奢田园
        if (2 == level) {

            LevelTwoCeiling levelTwoCeiling = new LevelTwoCeiling(); // 吊顶,二级顶
            LiBangCoat liBangCoat = new LiBangCoat();                // 涂料,立邦
            MarcoPoloTile marcoPoloTile = new MarcoPoloTile();       // 地砖,马可波罗

            list.add(levelTwoCeiling);
            list.add(liBangCoat);
            list.add(marcoPoloTile);

            price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelTwoCeiling.price()));
            price = price.add(area.multiply(new BigDecimal("1.4")).multiply(liBangCoat.price()));
            price = price.add(area.multiply(marcoPoloTile.price()));

        }

        // 现代简约
        if (3 == level) {

            LevelOneCeiling levelOneCeiling = new LevelOneCeiling();  // 吊顶,二级顶
            LiBangCoat liBangCoat = new LiBangCoat();                 // 涂料,立邦
            DongPengTile dongPengTile = new DongPengTile();           // 地砖,东鹏

            list.add(levelOneCeiling);
            list.add(liBangCoat);
            list.add(dongPengTile);

            price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelOneCeiling.price()));
            price = price.add(area.multiply(new BigDecimal("1.4")).multiply(liBangCoat.price()));
            price = price.add(area.multiply(dongPengTile.price()));
        }

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

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

        return detail.toString();

    }

}

通过传递的参数来判断是做什么风格的装修,然后分别单独构建装修材料,然后组合起来,计算价格

建造者模式重构代码

工程结构如图:

image

建造者模型结构:

image

  • Builder类,建造者类具体的各类组装由此类实现。
  • DecorationPackageMenu类,实现Imenu接口,通过list放置要装修的材料,保存价格的变量,装修风格的变量和面积的变量都是通过构造函数的参数传递进来

代码实现:

  • Imenu接口:

    public interface IMenu {
    
        /**
         * 吊顶
         */
        IMenu appendCeiling(Matter matter);
    
        /**
         * 涂料
         */
        IMenu appendCoat(Matter matter);
    
        /**
         * 地板
         */
        IMenu appendFloor(Matter matter);
    
        /**
         * 地砖
         */
        IMenu appendTile(Matter matter);
    
        /**
         * 明细
         */
        String getDetail();
    
    }
    

    接口类中定义了填充各项物料的方法; 吊顶 、 涂料 、 地板 、 地砖 ,以及最终提供获取全部明细的方法。

  • 装修包的实现

    public class DecorationPackageMenu implements IMenu {
    
        private List<Matter> list = new ArrayList<Matter>();  // 装修清单
        private BigDecimal price = BigDecimal.ZERO;      // 装修价格
    
        private BigDecimal area;  // 面积
        private String grade;     // 装修等级;豪华欧式、轻奢田园、现代简约
    
        private DecorationPackageMenu() {
        }
    
        public DecorationPackageMenu(Double area, String grade) {
            this.area = new BigDecimal(area);
            this.grade = grade;
        }
    
        public IMenu appendCeiling(Matter matter) {
            list.add(matter);
            price = price.add(area.multiply(new BigDecimal("0.2")).multiply(matter.price()));
            return this;
        }
    
        public IMenu appendCoat(Matter matter) {
            list.add(matter);
            price = price.add(area.multiply(new BigDecimal("1.4")).multiply(matter.price()));
            return this;
        }
    
        public IMenu appendFloor(Matter matter) {
            list.add(matter);
            price = price.add(area.multiply(matter.price()));
            return this;
        }
    
        public IMenu appendTile(Matter matter) {
            list.add(matter);
            price = price.add(area.multiply(matter.price()));
            return this;
        }
    
        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.scene()).append(":").append(matter.brand()).append("、").append(matter.model()).append("、平米价格:").append(matter.price()).append(" 元。\n");
            }
    
            return detail.toString();
        }
    
    }
    
    • 装修包的实现中每一个方法都返回了 this ,也就可以非常方便的用于连续填充各项物料
    • 同时在填充时也会根据物料计算平米数下的报价,吊顶和涂料按照平米数乘以该类定义的price计算
    • 最后同样提供了统一的获取装修清单的明细方法。
  • 建造者方法

    public class Builder {
    
        public IMenu levelOne(Double area) {
            return new DecorationPackageMenu(area, "豪华欧式")
                    .appendCeiling(new LevelTwoCeiling())    // 吊顶,二级顶
                    .appendCoat(new DuluxCoat())             // 涂料,多乐士
                    .appendFloor(new ShengXiangFloor());     // 地板,圣象
        }
    
        public IMenu levelTwo(Double area){
            return new DecorationPackageMenu(area, "轻奢田园")
                    .appendCeiling(new LevelTwoCeiling())   // 吊顶,二级顶
                    .appendCoat(new LiBangCoat())           // 涂料,立邦
                    .appendTile(new MarcoPoloTile());       // 地砖,马可波罗
        }
    
        public IMenu levelThree(Double area){
            return new DecorationPackageMenu(area, "现代简约")
                    .appendCeiling(new LevelOneCeiling())   // 吊顶,二级顶
                    .appendCoat(new LiBangCoat())           // 涂料,立邦
                    .appendTile(new DongPengTile());        // 地砖,东鹏
        }
    
    }
    

    统一的建造⽅方式,通过不同物料填充出不同的装修风格; 豪华欧式 、 轻奢⽥田园 、 现代简约等

编写测试类

public class ApiTest {

    @Test
    public void test_Builder(){
        Builder builder = new Builder();

        // 豪华欧式
        System.out.println(builder.levelOne(132.52D).getDetail());

        // 轻奢田园
        System.out.println(builder.levelTwo(98.25D).getDetail());

        // 现代简约
        System.out.println(builder.levelThree(85.43D).getDetail());
    }

}
posted @ 2021-04-27 16:42  RealGang  阅读(52)  评论(0编辑  收藏  举报