在装饰者模式中,装饰者类通常对原始类的功能进行增强或减弱。这种模式是在不必改变原始类的情况下,动态地扩展一个对象的功能。这种类型的设计模式属于结构型模式,因为这种模式涉及到两个类型之间的关系,这两个类型是组合在一起的,这种组合关系通常是通过继承来实现的。
装饰者模式的主要优点是可以在不修改原始类的情况下,通过使用单个类来包装其对象,动态地扩展一个对象的功能。其主要缺点是装饰者模式会导致设计中出现很多小类,如果过度使用,会使程序变得复杂。
我们使用一个案例来对快餐店的点餐功能进行改进,快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、培根这些配菜,加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦,这时候我们就可以使用装饰者模式,在不改变原始类的情况下,动态扩展对象功能。
首先,创建一个最基本的主食抽象类,定义了价格和类型两个属性,以及获取费用和获取类型两个抽象方法
public abstract class FastFood
{
///
/// 价格
///
public float _price { get; set; }
///
/// 类型
///
public string _desc { get; set; }
public FastFood()
{
}
public FastFood(float price, string desc)
{
_price = price;
_desc = desc;
}
///
/// 获取费用
///
///
public abstract float GetCost();
///
/// 获取类型
///
///
public abstract string GetDesc();
}
然后,对于上面的主食抽象类分别增加两个实现类,炒饭和炒面,在这里通过基类的含有(float price, string desc)参数的构造函数分别对价格和类型赋值,如果在此处直接调用了GetDesc,那么此时的炒饭和炒粉是什么都还没加的,因此我们在重写GetDesc加上了"啥都不加"字符
///
/// 炒饭
///
public class FriedRice : FastFood
{
public FriedRice() : base(10, "炒饭")
{
}
public override float GetCost()
{
return _price;
}
public override string GetDesc()
{
return _desc + " 啥都不加";
}
}
///
/// 炒面
///
public class FriedNoodles : FastFood
{
public FriedNoodles() : base(12, "炒面")
{
}
public override float GetCost()
{
return _price;
}
public override string GetDesc()
{
return _desc + " 啥都不加";
}
}
创建一个配料抽象类继承于主食抽象类,并且定义了一个FastFood(主食)类型的属性_fastFood
///
/// 配料类
///
public abstract class Garnish : FastFood
{
public FastFood _fastFood { get; set; }
}
对配料类做两个实现,鸡蛋和培根,通过这两个对象,我们可以动态地给一个FastFood对象添加鸡蛋或培根,并计算出新的价格和描述,这就是装饰者模式的核心思想。
在方法中我们再次重写了获取类型和价格的方法。
GetCost()方法是用来计算总价的,即鸡蛋或培根的价格加上被装饰对象的价格。
GetDesc()方法是用来获取描述的,即鸡蛋或培根的描述加上被装饰对象的描述。
///
/// 添加鸡蛋
///
public class Egg : Garnish
{
public Egg(FastFood fastFood)
{
_fastFood = fastFood;
_desc = "鸡蛋";
_price = 3;
}
public override float GetCost()
{
return _price + _fastFood._price;
}
public override String GetDesc()
{
return _desc + _fastFood._desc;
}
}
///
/// 添加培根
///
public class Bacon : Garnish
{
public Bacon(FastFood fastFood)
{
_fastFood = fastFood;
_price = 5;
_desc = "培根";
}
public override float GetCost()
{
return _price + _fastFood._price;
}
public override String GetDesc()
{
return _desc + _fastFood._desc;
}
}
测试类
class MyClass
{
public static void Main(string[] args)
{
//点一份炒饭
FastFood riceFood = new FriedRice();
//花费的价格
Console.WriteLine(riceFood.GetDesc() + " " + riceFood.GetCost() + "元");
//点一份炒面
FastFood noodleFood = new FriedNoodles();
//花费的价格
Console.WriteLine(noodleFood.GetDesc() + " " + noodleFood.GetCost() + "元");
//点一份加鸡蛋的炒饭
FastFood food1 = new FriedRice();
food1 = new Egg(food1);
//花费的价格
Console.WriteLine(food1.GetDesc() + " " + food1.GetCost() + "元");
//点一份加培根的炒面
FastFood food2 = new FriedNoodles();
food2 = new Bacon(food2);
//花费的价格
Console.WriteLine(food2.GetDesc() + " " + food2.GetCost() + "元");
}
}
运行结果
使用场景
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
不能采用继承的情况主要有两类:
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
当对象的功能要求可以动态地添加,也可以再动态地撤销时。