1、简介
简要说明就是动态地给一个对象添加一些额外的职责。适用于需要扩展一个类的功能,或给一个类添加多个变化的情况。
装饰器,顾名思义就是在原有基础上添加一些功能。
装饰器模式中各个角色有:
抽象构件(Component)角色:定义一个对象接口,可以给这些对象动态地添加一些职责。
具体构件(ConcreteComponent)角色:定义了一个具体的对象,也可以给这个对象添加一些职责。
抽象装饰类(Decorator)角色:持有一个构件(Component)对象的引用,并定义一个与抽象构件接口一致的接口。
具体装饰类(ConcreteDecorator)角色:负责给构件对象“贴上”一些附加的职责。
2、适用场景
扩展一个类的功能:原有类无法修改或者修改困难的情况下,对类进行多次扩展或功能性比较相互独立,有效防止多次扩展的情况下子类的膨胀。
动态增加或撤销功能:需要动态地给对象添加或移除功能时。
多个功能组合:需要实现多个功能的不同组合时。
3、举例说明
现在有一个手机,其仅有打电话的功能。现在需要给它加一个看视频和放音乐的功能。
传统做法如下:
手机类:
/// <summary>
/// 手机类
/// </summary>
public class Phone
{public void Call(){Console.WriteLine("打电话");}
}
增加功能:
public class FYY:Phone
{public void Install(){Console.WriteLine("放音乐!");}
}public class FSP : Phone
{public void Install(){Console.WriteLine("放视频!");}
}
使用:
private void WTBtn_Click(object sender, EventArgs e)
{Phone phone0 = new Phone();//旧版手机phone0.Call();//仅有打电话的功能Console.WriteLine("\n");FYY phone = new FYY();//新版手机1phone.Call();//打电话,手机本来的功能phone.Install();//放音乐,给手机拓展的东西Console.WriteLine("\n");FSP phone1=new FSP();//新版手机2phone1.Call();//打电话,手机本来的功能phone1.Install();//放视频,给手机拓展的东西
}
可以看出,每当需要增加一个功能,就需要增加一个子类。这种简单的单一功能问题还不大,当你既要放音乐又要放视频的功能的话,那你要不就是再实现一个之类去包含两种功能,要不就多重继承让放音乐的类继承放视频的类。这样实例化放音乐的类就能达到使用两种功能。当需要的功能越多,继承就越深,或者有重复方法的类就越多。但这显然不科学。 装饰器模式就能很好解决此类问题。
抽象构件(Component)角色
/// <summary>/// 抽象构件角色(Component)/// </summary>public abstract class IPhone{public abstract void Call();}
具体构件(ConcreteComponent)角色
/// <summary>/// 具体构件角色(Concrete Component)/// </summary>public class Phone : IPhone{public override void Call(){Console.WriteLine("打电话");}}
抽象装饰类(Decorator)角色
/// <summary>
/// 抽象装饰类(Decorator)角色
/// </summary>
public abstract class PhoneDecorator : IPhone
{protected IPhone Phone { get; }public PhoneDecorator(IPhone phone){Phone = phone;}/// <summary>/// 可以定义新增的功能方法,也可以不定义,直接给Call方法添加装饰/// </summary>public abstract void Install();
}
具体装饰类(ConcreteDecorator)角色
/// <summary>/// 具体装饰类(ConcreteDecorator)角色/// </summary>// 放音乐功能装饰器public class YYDecorator : PhoneDecorator{public YYDecorator(IPhone phone) : base(phone){}public override void Call(){base.Phone.Call();RunYY();//也可以给打电话这件事添加放音乐的功能}public override void Install(){Console.WriteLine("安装音乐!");}public void RunYY(){Console.WriteLine("语音通话!");}}/// <summary>/// 具体装饰类(ConcreteDecorator)角色/// </summary>// 放音乐功能装饰器public class SPDecorator : PhoneDecorator{public SPDecorator(IPhone phone) : base(phone){}public override void Call(){base.Phone.Call();RunSP();//也可以给打电话这件事添加视频通话的功能}public override void Install(){Console.WriteLine("安装视频!");}public void RunSP(){Console.WriteLine("视频通话!");}}
使用:
private void WTBtn_Click(object sender, EventArgs e){IPhone phone = new Phone();//初始手机phone.Call();//初始手机打电话Console.WriteLine("");PhoneDecorator phone1 = new YYDecorator(phone);//语音通话手机phone1.Install();//安装语音通话功能phone1.Call();//安装语音通话功能的手机打电话Console.WriteLine("");PhoneDecorator phone2 = new SPDecorator(phone);//视频通话手机phone2.Install();//安装视频通话功能phone2.Call();//安装视频通话功能的手机打电话Console.WriteLine("");PhoneDecorator phone3 = new SPDecorator(phone);//视频的手机phone3.Install();//安装视频功能phone3 = new YYDecorator(phone3);//语音的手机phone3.Install();//安装音乐功能phone3.Call();//双支持的手机再打电话}
注意:Call()方法和Install()方法都是实现了装饰器,一个是给原本的功能前后添加功能段,一个是独立添加其他的功能。
可以看出使用装饰类如果需要实现组合配置,仅需要对像重新实例化即可,无需创建新的对象,和使用多重继承了。 若需要增加其他功能仅需要增加具体装饰类(ConcreteDecorator)角色即可。
最后:
优点:
1、灵活性:装饰器模式可以以动态的方式在运行时给对象增加额外的职责,而不需要在编译时决定添加哪些功能。通过使用装饰器模式,可以在不改变原始对象结构的情况下,根据需要灵活地扩展对象的行为。
2、可插拔:通过使用装饰器模式,可以将功能分解成一系列的装饰器类,使得代码更加模块化和易于维护。可以在运行时动态地组合和替换装饰器对象,从而改变对象的行为。
3、可扩展性:装饰器模式可以避免继承带来的类膨胀问题,因为你可以通过组合装饰器对象来扩展对象的行为,而不是通过继承来添加新的功能。
4、符合开闭原则:装饰器模式完全遵守开闭原则,即对扩展开放,对修改封闭。通过使用装饰器模式,可以方便地添加新的装饰器类来扩展对象的行为,而不需要修改原始对象的代码。
缺点:
1、代码量增加:装饰器模式需要创建很多小类,即使只添加一个功能,也要额外创建一个类,这会使得程序更复杂。
2、增加代码复杂度:使用装饰器模式不但需要实例化组件,还要把组件包装到装饰者中,这会增加代码的复杂度。
3、设计难度高:装饰器模式需要对系统进行全面理解,设计出结构良好的装饰器类和被装饰类,才能够达到预期的效果。
4、性能问题:由于装饰器模式需要在运行时动态地创建对象和调用方法,这可能会导致性能上的问题。
注意:过度使用装饰器模式可能会导致程序变得复杂,增加系统中类的数量,并可能产生大量小粒度对象,使得代码变得难以维护。因此,在使用装饰器模式时需要谨慎考虑设计是否合适。