今天我们讨论一下 Builder 建造者模式,这个 Builder,其实和模板模式非常的像,但是也有区别,那就是在模板模式中父类对子类中的实现进行操作,在父类之中进行一件事情的处理,但是在 Builder 模式之中,父类和子类都不用关心怎么处理,而是用另一个类来完成对这些方法的有机组合,这个类的职责就是监工,规定了到底要怎么样有机的组合这些方法。在监工类(Director)中,将父类组合进去,然后调用父类的操作来抽象的实现一件事情,这就是面向接口(抽象)变成的妙处了,当然这个 Builder 可以使接口也可以是抽象类,在这里我们使用抽象类。
目录
1.UML图
从UML图中可以看出,建造者模式主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
2.案例
一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)还有小吃(Snack)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒(Wrapper)中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子(bottle)中,小吃有(FrenchFrice),蛋挞(EggTart)。
我们将创建一个表示食物条目(比如汉堡和冷饮、小吃)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中,小吃也是盒装。
然后我们创建一个 Meal 类,带有 Item 的 List 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilder。我构建了两种Meal类型,作为构造者,你可以使用不同组合构建更多Meal。
Item类:
internal abstract class Item
{
public string Name { get; set; }
public float Price { get; set; }
public IPack? Package { get; set; }
public void SetPrice(float p)=>Price = p;
}
IPack接口:
public interface IPack
{
public string Pack();
}
三种包装:
internal class Bottle : IPack
{
public string Pack()
{
return "Bottle";
}
}
internal class PlasticBag:IPack
{
public string Pack() => "PlasticBag";
}
internal class Wrapper:IPack
{
public string Pack()
{
return "Wrapper ";
}
}
饮料抽象类:
internal class Coke:ColdDrink
{
public Coke()
{
Price = 5.0f;
Name = "Coke";
}
}
两种冷饮:
internal class Coke:ColdDrink
{
public Coke()
{
Price = 5.0f;
Name = "Coke";
}
}
internal class Pepsi:ColdDrink
{
public Pepsi()
{
Name = "Pepsi";
Price = 6.0f;
}
}
小吃抽象类:
internal abstract class Snack : Item
{
public Snack() => Package = new Wrapper();
}
两种小吃:
internal class EggTart:Snack
{
public EggTart()
{
Price = 9.99f;
Name = "Egg Tart";
}
}
internal class FrenchFrice:Snack
{
public FrenchFrice()
{
Price = 12.0f;
Name = "French Frice";
}
}
汉堡和小吃类似,就不贴了,然后是Meal:
internal class Meal
{
private List<Item> Items { get; } = new List<Item>();
public void AddItem(Item item)=>Items.Add(item);
public float GetCost()
{
Console.WriteLine("**********************COST************************");
return Items.Aggregate(0f, (sum, item) => item.Price + sum);
}
public void ShowItems()
{
Console.WriteLine("--------------------------List-----------------------");
Items.ForEach(item =>
{
Console.WriteLine($"Item: {item.Name} \n\t packing: {item.Package?.Pack()}" +
$"\t\n price: {item.Price}");
});
}
}
构造者MealBuilder:
internal class MealBuilder
{
public Meal PrepareVegMeal()
{
Meal meal = new Meal();
meal.AddItem(new VegBurger());
meal.AddItem(new Coke());
meal.AddItem(new FrenchFrice());
return meal;
}
public Meal PrepareNonVegMeal()
{
Meal meal = new Meal();
meal.AddItem(new ChickenBurger());
meal.AddItem(new Pepsi());
meal.AddItem(new EggTart());
return meal;
}
}
3. 小结
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
完整代码:DesignPatternReview