建造者模式(四)

Andy 2023年05月30日 534次浏览

使用多个简单的对象一步一步构建成一个复杂的对象
用套餐可以形象的解释这个模式,很多比如可乐,汉堡不变的,但是组合起来就有很多变化

看下图
菜鸟的案例:我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒(Wrapper)中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子(Bottle)中。

我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。

然后我们创建一个 Meal 类,带有 Item 的 ArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilder。BuilderPatternDemo,我们的演示类使用 MealBuilder 来创建一个 Meal。
image.png
看这个图你可能有一些凌乱,那分开来看

段落引用案例图分割成下面几部分

image.png
看这个,从下往上看,
VegBurger和Chicken Burger 继承自Burger,
coke和pepsi继承自Cold drink
看过我UML介绍的就会知道实线箭头代表的是关联关系,线上也写了uses(使用)
Burger关联Wrapper,也就是汉堡用纸盒
Cold drink关联Bottle ,也就是冷饮用瓶子
而纸盒和瓶子实现了包装接口,说明它两一个封装汉堡,一个包装冷饮
也可以这么理解,就是一个包装接口和两个包装接口实现类,而汉堡和冷饮分别关联了这俩个实体类
这样关联起来后,外面的想要汉堡或者冷饮时必须带着包装它们的纸盒或者瓶子

再看下面的图

image.png
汉堡和冷饮都实现了Item接口,在这你可以考虑到Burger和ColdDrink,到底是接口呢?还是抽象类?还是普通类?埋个伏笔。

Meal关联Item,看到Meal里有ArrayList 了吧,是一个Meal里可以有一个集合的Item,而且是不完全相同的Item,这样就可以有不同的组合,

再看最后一部分图

image.png

这部分从下往上看
main 肯定就是外部的执行程序了,它调用MealBuilder的两个方法,这两个方法看就知道一个是带VegBurger 或者 不带VegBurger 的组合
而MealBuilder就需要他去创建Meal,生成执行的组合

代码
(1)Item和Packing俩接口
Item.java

public interface Item {
   public String name();
   public Packing packing();
   public float price();    
}

Packing.java

public interface Packing {
   public String pack();
}

上面这俩个是接口,顺着这两往下走

(2)创建实现 Packing 接口的实体类。
Wrapper.java

public class Wrapper implements Packing {
 
   @Override
   public String pack() {
      return "Wrapper";
   }
}

Bottle.java

public class Bottle implements Packing {
 
   @Override
   public String pack() {
      return "Bottle";
   }
}

3、创建实现 Item 接口的抽象类,该类提供了默认的功能。
Burger.java

public abstract class Burger implements Item {
 
   @Override
   public Packing packing() {
      return new Wrapper();
   }
 
   @Override
   public abstract float price();
}

ColdDrink.java

public abstract class ColdDrink implements Item {
 
    @Override
    public Packing packing() {
       return new Bottle();
    }
 
    @Override
    public abstract float price();
}

猜到了吗?是抽象类,你可以想想抽象类和接口的区别,
在这是因为组合中冷饮的价格和汉堡的价格因为选择的不同价格会有差异,而价格就只有汉堡的实体类去填写,而Item接口中有price方法,所以这只能是抽象类,等到汉堡或冷饮的实体类再重写,确定价格,
原本上面 public abstract float price();可以不用写的,想name一样直接在最后的实体类重写就好,但是为了好理解,并介绍为什么这用抽象类,就写上了

(4)创建扩展了 Burger 和 ColdDrink 的实体类。
VegBurger.java

public class VegBurger extends Burger {
 
   @Override
   public float price() {
      return 25.0f;
   }
 
   @Override
   public String name() {
      return "Veg Burger";
   }
}

ChickenBurger.java

public class ChickenBurger extends Burger {
 
   @Override
   public float price() {
      return 50.5f;
   }
 
   @Override
   public String name() {
      return "Chicken Burger";
   }
}

Coke.java

public class Coke extends ColdDrink {
 
   @Override
   public float price() {
      return 30.0f;
   }
 
   @Override
   public String name() {
      return "Coke";
   }
}

Pepsi.java

public class Pepsi extends ColdDrink {
 
   @Override
   public float price() {
      return 35.0f;
   }
 
   @Override
   public String name() {
      return "Pepsi";
   }
}

5、创建一个 Meal 类,带有上面定义的 Item 对象。
Meal.java

    import java.util.ArrayList;
    import java.util.List;
     
    public class Meal {
    //至于为什么实体类能加进接口的类型的集合里,这就有先见之明了,因为Item接口里有一个组合所有的抽象类
    //这就是为什么上面汉堡和冷饮用抽象类,也为这做铺垫
       private List<Item> items = new ArrayList<Item>();    
     //按逻辑这里的集合里就两个,一个是汉堡的,一个是冷饮的
       public void addItem(Item item){
          items.add(item);
       }
     
       public float getCost(){
          float cost = 0.0f;
          for (Item item : items) {
             cost += item.price();
          }        
          return cost;
       }

 
   public void showItems(){
      for (Item item : items) {
      //遍历集合里的东西的名称和价格
         System.out.print("Item : "+item.name());
         System.out.print(", Packing : "+item.packing().pack());
         System.out.println(", Price : "+item.price());
      }        
   }    
}

6、创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象。
MealBuilder.java

这个类创建Meal,也为Meal中的组合进行选择添进Meal的ArrayList

public class MealBuilder {
 
   public Meal prepareVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new VegBurger());
      meal.addItem(new Coke());
      return meal;
   }   
 
   public Meal prepareNonVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new ChickenBurger());
      meal.addItem(new Pepsi());
      return meal;
   }
}

7、BuiderPatternDemo 使用 MealBuider 来演示建造者模式(Builder Pattern)。
BuilderPatternDemo.java

public class BuilderPatternDemo {
   public static void main(String[] args) {
      MealBuilder mealBuilder = new MealBuilder();
 
      Meal vegMeal = mealBuilder.prepareVegMeal();
      //哪种组合,以汉堡为分类
      System.out.println("Veg Meal");
      vegMeal.showItems();
      System.out.println("Total Cost: " +vegMeal.getCost());
 
      Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
      System.out.println("\n\nNon-Veg Meal");
      nonVegMeal.showItems();
      System.out.println("Total Cost: " +nonVegMeal.getCost());
   }
}

8、执行程序,输出结果:
Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0

Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5

这就是我分析的建造者模式,
参考菜鸟教程的建造者模式
————————————————
版权声明:本文为CSDN博主「胖墩的IT」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43113679/article/details/99320039