建造者模式(Builder Pattern)使用多个简单的对象一步步构建成一个复杂的对象,它提供了一种创建对象的最佳方式。
总会有一个Builder类,一步步构造成最终的对象,该Builder类是独立于其他对象的。
意图:将一个复杂的构建与其相分离,使得同样的构建过程可以创建不同的表示。
主要解决:在软件系统中,对于“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:将变与不变分离开。
关键代码:建造者——创建和提供实例。导演——管理建造出来的实例的依赖关系。
优点:
- 建造者独立,易扩展。
- 便于控制细节风险。
缺点:
- 产品必须有共同点,范围有限。
- 如内部变化复杂,会有很多的建造类。
使用场景:
- 需要生成的对象具有复杂的内部结构
- 需要生成的对象内部属性本身相互依赖
注意事项:
与工厂模式的区别是:更加关注与零件装配的顺序。
使用模板:
主要是三个角色:建造者Builder、具体的建造者xxxBuilder、监工Director
- 建造者角色:定义生成实例所需要的所有方法;
- 具体的建造者角色:实现生成实例所需要的所有方法,并且定义获取最终生成实例的方法;
- 监工角色:定义使用建造者角色中的方法来生成实例的方法;
应用实例:
去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。
一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。
我们将创建一个表示食物条目(比如汉堡和冷饮)的 IItem 接口,以及一个表示食物包装的 IPacking 接口,汉堡是包在纸盒中,冷饮是装在瓶子中。
namespace Builder
{
public interface IItem
{
string GetName();
IPacking GetPacking();
float GetPrice();
}
public abstract class Burger : IItem
{
public abstract string GetName();
public IPacking GetPacking()
{
return new Wrapper();
}
public abstract float GetPrice();
}
public class VegBurger : Burger
{
public override string GetName()
{
return "VegBurger";
}
public override float GetPrice()
{
return 25.0f;
}
}
public class ChickenBurger : Burger
{
public override string GetName()
{
return "ChickenBurger";
}
public override float GetPrice()
{
return 50.5f;
}
}
public abstract class ColdDrink : IItem
{
public abstract string GetName();
public IPacking GetPacking()
{
return new Bottle();
}
public abstract float GetPrice();
}
public class Coke : ColdDrink
{
public override string GetName()
{
return "Coke";
}
public override float GetPrice()
{
return 30.0f;
}
}
public class Pepsi : ColdDrink
{
public override string GetName()
{
return "Pepsi";
}
public override float GetPrice()
{
return 35.0f;
}
}
}
namespace Builder
{
public interface IPacking
{
string Pack();
}
public class Bottle : IPacking
{
public string Pack()
{
return "Bottle";
}
}
public class Wrapper : IPacking
{
public string Pack()
{
return "Wrapper";
}
}
}
然后我们创建一个 Meal 类,带有 Item 的 列表和一个通过结合 Item 来创建不同类型的 Meal 对象的 Builder。
using System.Collections.Generic;
using UnityEngine;
namespace Builder
{
public class Meal
{
private readonly List<IItem> _items = new List<IItem>();
public void AddItem(IItem item)
{
_items.Add(item);
}
public float GetCost()
{
float cost = 0.0f;
foreach (var item in _items)
{
cost += item.GetPrice();
}
return cost;
}
public void ShowItems()
{
foreach (var item in _items)
{
Debug.Log("Item : " + item.GetName());
Debug.Log(", Packing : " + item.GetPacking().Pack());
Debug.Log(", Price : " + item.GetPrice());
}
}
}
}
namespace Builder
{
public abstract class Builder
{
protected readonly Meal _meal = new Meal();
public abstract void PrepareVegMeal();
public abstract void PrepareNonVegMeal();
public Meal GetMeal()
{
return _meal;
}
}
public class VegMealBuilder : Builder
{
public override void PrepareVegMeal()
{
_meal.AddItem(new VegBurger());
_meal.AddItem(new Coke());
}
public override void PrepareNonVegMeal()
{
}
}
public class NonVegMealBuilder : Builder
{
public override void PrepareVegMeal()
{
}
public override void PrepareNonVegMeal()
{
_meal.AddItem(new ChickenBurger());
_meal.AddItem(new Pepsi());
}
}
}
通过使用不同的 Builder 传入Director来创建不同的 Meal实例。
namespace Builder
{
public class Director
{
public void Construct(Builder builder)
{
builder.PrepareVegMeal();
builder.PrepareNonVegMeal();
}
}
}
设计类图如下: