免费建站系统怎么用,网站建设与维护费,模板板网站,展厅设计施工一体化一、为何需要装饰模式#xff08;Decorator#xff09;?
在软件设计中#xff0c;某个对象会组合很多不同的功能#xff0c;如果把所有功能都写在这个对象所在的类里#xff0c;该类会包含很多复杂的代码逻辑#xff0c;导致代码不美观且难以维护。于是就有了再定义一些…一、为何需要装饰模式Decorator?
在软件设计中某个对象会组合很多不同的功能如果把所有功能都写在这个对象所在的类里该类会包含很多复杂的代码逻辑导致代码不美观且难以维护。于是就有了再定义一些新类。这些类负责各自的功能模块就会实例化一些各司其职的对象。而这些对象再跟原始对象进行组合以共同完成一个复杂的完整功能。这些对象就称为装饰对象主要为原对象进行附加功能。有个问题就是如何把装饰对象跟原对象进行组合的同时又保证不修改原对象的情况下进行扩展不同的附加功能。这时候装饰模式就派上用场了。
比如有个推广产品的功能需求用户看视频后就奖励一个红包需要做成一个红包生成器。红包具有不同的功能皮肤换装、显示特效等。根据装饰模式来分析红包是原对象作为红包最原始的模样。而皮肤和特效是装饰对象是为红包装饰的附加了不同的功能如具有春节风格功能的皮肤具有抖动功能的特效。
针对红包生成器装饰模式的主要运用方面 1、用户自定义可选功能 用户可勾选使用特效功能弹出时会有抖动的效果。 这里的用户可分为使用红包的用户和调用红包生成器底层库接口的程序员 2、迭代版本可选功能 在迭代版本中 我们会对某一模块进行动态地移除或添加功能。如在上个版本中分析到红包精美的皮肤并不是用户的重要需求并且传送新皮肤会增加网络资源的成本反而简洁的界面更能吸引到用户。于是在此版本中可移除掉这个功能从而不会修改原对象里的代码逻辑。
即装饰模式的主要作用是用户可以灵活地动态地扩展附加功能并且不会影响原对象的代码逻辑。
以下是有关红包生成器错误设计的例子 //方式一原对象继承其他多个装饰对象的接口//public interface IRedPacket...//红包原对象接口//public interface IEffect...//特效//public interface ISkin...//皮肤public class RedPacket: IRedPacket,IEffect, ISkin{public RedPacket() { }public void setPlace()//实现 IRedPacket.setPlace 方法{setSound();//实现 IEffect.setSound 方法setShake();//实现 IEffect.setShake 方法setBgImg();//实现 ISkin.setBgImg 方法//...//如果有移除或扩展某些功能的新需求时//需要继承或者移除某个基类就会修改某些实现的方法。}public void setSound() { }public void setShake() { }public void setBgImg() { }//...}//方式二在原对象所在的类里进行对象组合// public class EffectIEffect...//public class SkinISkin...public class RedPacket : IRedPacket{private Effect effect;private Skin skin;public RedPacket() { }public void setPlace(){effect.setSound();effect.setShake();skin.setBgImg();//...//如果有移除或扩展某些功能的新需求时//需要继承或者移除某个装饰对象就会修改某些代码逻辑。}//...}//不管是在原对象里进行对象组合还是原对象继承其他对象的基类//当有新需求时都会对原对象进行修改代码逻辑从而导致难以扩展和维护的问题。
由以上代码可知在这两种方式中如果遇到新需求需要修改时第一种方式往往会比第二种方式更加的困难因为多继承关系需要对某些基类的每个方法进行实现。第二种方式也好不了哪里去因为在原对象里有修改过的痕迹。
特点
少继承多组合。少继承那些装饰类。多组合装饰对象应放在原对象类的外部进行组合而不是在原对象类里
结构
以下结构中的抽象组件为抽象类或接口
原始抽象组件Component定义了原始对象和装饰器对象的公共接口。如红包抽象组件附加功能的 setPlace 抽象方法作为公共接口原始对象具体类Concrete Component实现公共接口。红包具体类实现 setPlace 方法装饰抽象组件Decorator继承原始对象组件包装了一个原对象。且定义了与抽象组件公共的接口。皮肤具体类和特效具体类都继承于红包抽象组件。装饰对象具体类Concrete Decorator实现了装饰抽象组件的公共接口向抽象组件添加新的功能。红包增加换肤和特效的功能
适合应用场景特点
装饰对象对原对象进行附加功能多个装饰对象跟原始对象进行组合以实现多个不同的功能模块。比如一个红包具有换肤和特效的功能装饰对象都继承同一个原对象的抽象组件且每一个装饰对象都包装一个原对象通过调用公共接口来进行组合。 比如红包特效和皮肤都继承于红包通过公共方法来进行组合 二、例子
需求
在手机上根据不同的应用场景会出现具有不同功能和风格的虚拟键盘。在转账界面时弹出数字模式键盘。在搜索框里一旦输入一两个字母时就会有以输入字母开头的预知单词浮现。在聊天框里不仅有预知单词还有表情包的工具栏。
设计分析
原始对象为虚拟键盘。装饰对象为数字模式、预知单词、表情包。少继承把数字模式、预知单词、表情包这些功能单独做成一个装饰器与原始对象分离。多组合组合多个装饰对象实现同时具有多个不同功能的原始对象。
1、定义原对象抽象接口和装饰对象抽象接口
//Component:原对象抽象类public interface Ikeyboard{void show();//生成并弹出键盘的公共接口}//Decorator装饰对象抽象类//方式一在某些书中有提到可以跳过定义此 Decorator 抽象类的步骤//就直接定义public class NumberMode:Ikeyboard然后类里包装一个 Ikeyboard 派生实例。//方式二利用装饰接口遵循装饰模式的结构//public interface INumberMode...//定义某个指定的 Decorator 抽象类//public class NumberMode:INumberMode...//类里包装一个 Ikeyboard 派生实例。//先忽略以上方式一和方式二等理解透了本例子再回头来理解就知道怎么用了。//但为了遵循装饰模式的结构在这里我还是定义了一个更规范的 Decorator 抽象类。被用于单继承的基类public abstract class Decorator : Ikeyboard{protected Ikeyboard ikeyboard;public Decorator(Ikeyboard ikeyboard){this.ikeyboard ikeyboard;}public virtual void show() { }//添加其他抽象方法或实现的方法...}
2、定义原对象具体类和装饰对象具体类
//ConcreteComponent原对象具体类键盘public class Keyboard : Ikeyboard{public Keyboard() { }public void show() { }}//ConcreteDecorator装饰对象具体类数字模式键盘public class NumberMode : Decorator{public NumberMode(Ikeyboard ikeyboard):base(ikeyboard){base.ikeyboard ikeyboard;}public override void show() { ikeyboard.show(); Console.WriteLine(切换为数字模式键盘.); }}//ConcreteDecorator装饰对象具体类预知单词public class PresetWords : Decorator{public PresetWords(Ikeyboard ikeyboard) : base(ikeyboard){base.ikeyboard ikeyboard;}public override void show() { ikeyboard.show(); Console.WriteLine(增加了预知单词功能.); }}//ConcreteDecorator装饰对象具体类表情包工具public class EmojiPack : Decorator{public EmojiPack(Ikeyboard ikeyboard) : base(ikeyboard){base.ikeyboard ikeyboard;}public override void show() { ikeyboard.show(); Console.WriteLine(增加了表情包工具.); }}//还可扩展其他功能如//public class TouchKeypad : Decorator...//手写键盘装饰类//public class QuickPhrases: Decorator...//快捷短语装饰类...
3、主程序
class Program{static void Main(string[] args){Ikeyboard keyboard, numberMode, presetWords, preWords, chatKeyboard;//原对象keyboard new Keyboard();//1.在转账时弹出附带数字键盘模式的键盘numberMode new NumberMode(keyboard);Console.WriteLine(\n在转账时);numberMode.show();//2.在搜索框里弹出附带预知单词的键盘presetWords new PresetWords(keyboard);Console.WriteLine(\n在搜索框里);presetWords.show();//3.在聊天框里弹出既有预知单词又有表情包功能的键盘preWords new PresetWords(keyboard);chatKeyboard new EmojiPack(preWords);//preWords 里有 PresetWords 功能Console.WriteLine(\n在聊天框里);chatKeyboard.show();//chatKeyboard 里有 EmojiPack 和 PresetWords 的功能。Console.ReadLine();}}
三、半透明装饰模式。
以上关于虚拟键盘的例子是属于透明装饰模式。在软件编程的领域中所谓透明的意思就是用户只需要调用附加功能的公共方法而无须知道某个装饰对象里的一些具体公开行为。而半透明的意思就是用户只可以知道指定装饰对象的一些具体公开行为。
通俗地来说就像是某家店的玻璃门。假定玻璃门的门把手是透明的在门外急性子的顾客因为没法找到门把手冒然冲进去必定撞得头破血流。而这个门把手如果是半透明的有一层灰色罩着门外的顾客会选择先用门把手拉门再进去。这里的开门行为属于作为玻璃门的门把手这个装饰对象的顾客可以直接调用这装饰对象的开门功能
与透明相比除了附加功能之外用户还可以对指定的装饰对象动态地进行处理比较详细的逻辑。
半透明装饰模式的例子
需求
大家协同开发一个客户管理系统分工负责各自的任务。程序员A负责对某些编辑框进行封装。其中有一个显示客户信息的界面需要封装一种编辑框用来输入客户的联系电话。然后把编辑框模块提供给另一个程序员B使用。程序员B可以对该编辑框的输入设置限制为11位数字的手机号格式。可复制该内容但不可把其他内容粘贴在此处。
设计分析
原对象为编辑框。装饰对象为联系电话装饰对象半透明模式可对联系电话限制为11位数字可复制该内容但不可从别处的内容粘贴到联系电话里。
//Component原对象抽象类编辑框接口public interface ITextEdit{void create();}ConcreteComponent原对象具体类编辑框public class TextEdit : ITextEdit{public TextEdit() { }public void create() { }}//Decorator装饰对象抽象类public abstract class AbsTelephoneDecorator : ITextEdit{protected ITextEdit iTextEdit;public AbsTelephoneDecorator(ITextEdit iTextEdit){this.iTextEdit iTextEdit;}public virtual void create() { }public abstract void setNumberLength(int len);public abstract void setCanCopy(bool isCanCopy);public abstract void setCanPaste(bool isCanPaste);}//ConcreteDecorator装饰对象具体类联系电话public class TelephoneDecorator : AbsTelephoneDecorator{public TelephoneDecorator(ITextEdit iTextEdit) :base(iTextEdit){base.iTextEdit iTextEdit;}public override void create() { iTextEdit.create(); Console.WriteLine(手机号的编辑框创建完成.); }public override void setNumberLength(int len){Console.WriteLine($set NumberLength:{len});}public override void setCanCopy(bool isCanCopy){Console.WriteLine($IsCanCopy:{isCanCopy});}public override void setCanPaste(bool isCanPaste){Console.WriteLine($IsCanPaste:{isCanPaste});}}//主程序class Program{static void Main(string[] args){//半透明装饰模式用户可以对指定装饰对象里的一些行为进行处理。//原对象ITextEdit textEdit new TextEdit();//装饰对象AbsTelephoneDecorator telephone new TelephoneDecorator(textEdit);//用户设置装饰对象的一些行为telephone.setNumberLength(11);telephone.setCanCopy(true);telephone.setCanPaste(false);telephone.create();//透明装饰模式ITextEdit telephone2 new TelephoneDecorator(textEdit);//telephone2 无法提供装饰对象的 setNumberLength setCanCopy setCanPaste。Console.ReadLine();}}