C#设计模式(9)——装饰者模式(Decorator Pattern)(转)
一、引言
在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类)、AccessoriesPhone(挂件手机类)等,这样就会导致 ”子类爆炸“问题,为了解决这个问题,我们可以使用装饰者模式来动态地给一个对象添加额外的职责。下面让我们看看装饰者模式。
二、装饰者模式的详细介绍
2.1 定义
装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任,装饰者模式相比生成子类可以更灵活地增加功能。
2.2 装饰者模式实现
这里以手机和手机配件的例子来演示装饰者模式的实现,具体代码如下:

/// <summary>
/// 手机抽象类,即装饰者模式中的抽象组件类
/// </summary>
public abstract class Phone
{
public abstract void Print();
} /// <summary>
/// 苹果手机,即装饰着模式中的具体组件类
/// </summary>
public class ApplePhone:Phone
{
/// <summary>
/// 重写基类方法
/// </summary>
public override void Print()
{
Console.WriteLine("开始执行具体的对象——苹果手机");
}
} /// <summary>
/// 装饰抽象类,要让装饰完全取代抽象组件,所以必须继承自Photo
/// </summary>
public abstract class Decorator:Phone
{
private Phone phone; public Decorator(Phone p)
{
this.phone = p;
} public override void Print()
{
if (phone != null)
{
phone.Print();
}
}
} /// <summary>
/// 贴膜,即具体装饰者
/// </summary>
public class Sticker : Decorator
{
public Sticker(Phone p)
: base(p)
{
} public override void Print()
{
base.Print(); // 添加新的行为
AddSticker();
} /// <summary>
/// 新的行为方法
/// </summary>
public void AddSticker()
{
Console.WriteLine("现在苹果手机有贴膜了");
}
} /// <summary>
/// 手机挂件
/// </summary>
public class Accessories : Decorator
{
public Accessories(Phone p)
: base(p)
{
} public override void Print()
{
base.Print(); // 添加新的行为
AddAccessories();
} /// <summary>
/// 新的行为方法
/// </summary>
public void AddAccessories()
{
Console.WriteLine("现在苹果手机有漂亮的挂件了");
}
}

此时客户端调用代码如下:

class Customer
{
static void Main(string[] args)
{
// 我买了个苹果手机
Phone phone = new ApplePhone(); // 现在想贴膜了
Decorator applePhoneWithSticker = new Sticker(phone);
// 扩展贴膜行为
applePhoneWithSticker.Print();
Console.WriteLine("----------------------\n"); // 现在我想有挂件了
Decorator applePhoneWithAccessories = new Accessories(phone);
// 扩展手机挂件行为
applePhoneWithAccessories.Print();
Console.WriteLine("----------------------\n"); // 现在我同时有贴膜和手机挂件了
Sticker sticker = new Sticker(phone);
Accessories applePhoneWithAccessoriesAndSticker = new Accessories(sticker);
applePhoneWithAccessoriesAndSticker.Print();
Console.ReadLine();
}

从上面的客户端代码可以看出,客户端可以动态地将手机配件增加到手机上,如果需要添加手机外壳时,此时只需要添加一个继承Decorator的手机外壳类,从而,装饰者模式扩展性也非常好。
2.3 装饰者模式的类图
实现完了装饰者模式之后,让我们看看装饰者模式实现中类之间的关系,具体见下图:

在装饰者模式中各个角色有:
- 抽象构件(Phone)角色:给出一个抽象接口,以规范准备接受附加责任的对象。
- 具体构件(AppPhone)角色:定义一个将要接收附加责任的类。
- 装饰(Dicorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
- 具体装饰(Sticker和Accessories)角色:负责给构件对象 ”贴上“附加的责任。
三、装饰者模式的优缺点
看完装饰者模式的详细介绍之后,我们继续分析下它的优缺点。
优点:
- 装饰这模式和继承的目的都是扩展对象的功能,但装饰者模式比继承更灵活
- 通过使用不同的具体装饰类以及这些类的排列组合,设计师可以创造出很多不同行为的组合
- 装饰者模式有很好地可扩展性
缺点:装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变的更复杂。并且更多的对象会是的差错变得困难,特别是这些对象看上去都很像。
四、使用场景
下面让我们看看装饰者模式具体在哪些情况下使用,在以下情况下应当使用装饰者模式:
- 需要扩展一个类的功能或给一个类增加附加责任。
- 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能
五、.NET中装饰者模式的实现
在.NET 类库中也有装饰者模式的实现,该类就是System.IO.Stream,下面看看Stream类结构:
上图中,BufferedStream、CryptoStream和GZipStream其实就是两个具体装饰类,这里的装饰者模式省略了抽象装饰角色(Decorator)。下面演示下客户端如何动态地为MemoryStream动态增加功能的。

MemoryStream memoryStream = new MemoryStream(new byte[] {95,96,97,98,99});
// 扩展缓冲的功能
BufferedStream buffStream = new BufferedStream(memoryStream);
// 添加加密的功能
CryptoStream cryptoStream = new CryptoStream(memoryStream,new AesManaged().CreateEncryptor(),CryptoStreamMode.Write);
// 添加压缩功能
GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, true);

六、总结
到这里,装饰者模式的介绍就结束了,装饰者模式采用对象组合而非继承的方式实现了再运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了单独使用继承带来的 ”灵活性差“和”多子类衍生问题“。同时它很好地符合面向对象设计原则中 ”优先使用对象组合而非继承“和”开放-封闭“原则。
C#设计模式(9)——装饰者模式(Decorator Pattern)(转)的更多相关文章
- 设计模式学习--装饰者模式(Decorator Pattern)
概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...
- python 设计模式之装饰器模式 Decorator Pattern
#写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...
- 23种设计模式之装饰器模式(Decorator Pattern)
装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包 ...
- C#设计模式之装饰者模式(Decorator Pattern)
1.概述 装饰者模式,英文名叫做Decorator Pattern.装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 2 ...
- c#设计模式之装饰器模式(Decorator Pattern)
引子 在面向对象语言中,我们常常会听到这样一句话:组合优于继承.那么该如何去理解这句话呢? 下面我将以游戏装备为模型用简单的代码去展示它 先创建一个装备的抽象类,然后创建刀枪2个具体的业务子类 pub ...
- 【UE4 设计模式】装饰器模式 Decorator Pattern
概述 描述 动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活.是一种对象结构型模式. 套路 抽象构件(Component) 具体构 ...
- 浅谈设计模式--装饰者模式(Decorator Pattern)
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
- 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法
装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...
- 设计模式 - 装饰者模式(Decorator Pattern) 具体解释
装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者 ...
- 设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)
在前两篇博客中详细的介绍了"策略模式"和“观察者模式”,今天我们就通过花瓶与鲜花的例子来类比一下“装饰模式”(Decorator Pattern).在“装饰模式”中很好的提现了开放 ...
随机推荐
- DTD约束简介
DTD约束简介 文档类型声明 文档类型声明就是DOCTYPE,它告诉解析器,XML文档必须遵循DTD定义.同时,他也告诉解析器,到哪里找到文档定义的其余内容.在前边的例子里DOCTYPE很简单: &l ...
- How do I use IValidatableObject? 使用IValidatableObject添加自定义属性验证
Here's how to accomplish what I was trying to do. Validatable class: public class ValidateMe : IVali ...
- GoLang之反射
反射 反射(reflect) 所谓反射(reflect)就是能检查程序在运行时的状态. 使用反射的三条定律: 反射可以将“接口类型变量”转换为“反射类型对象”: 反射可以将“反射类型对象”转换为“接口 ...
- python中的if __name__ == 'main'
当你打开一个.py文件时,经常会在代码的最下面看到if __name__ == '__main__':,现在就来介 绍一下它的作用: 对于编程语言来说,程序都必须要有一个入口,比如java和c#必须要 ...
- Dynamic attention in tensorflow
新代码在contrib\seq2seq\python\ops\attention_decoder_fn.py 和之前代码相比 不再采用conv的方式来计算乘,直接使用乘法和linear 给出了两种at ...
- ARM7与GSM实现的简单的远程控制
幸好单从控制GSM上来说,并不是太难,它是基于串口通信的,而且全是基于AT指令的控制,说起来,就只是熟悉下指令,而且这次比赛用到的指令也比较少,主要是关于短消息方面的指令.我们用到的指令如下: AT& ...
- Elasticsearch Java API的基本使用
说明 在明确了ES的基本概念和使用方法后,我们来学习如何使用ES的Java API. 本文假设你已经对ES的基本概念已经有了一个比较全面的认识. 客户端 你可以用Java客户端做很多事情: 执行标准的 ...
- Golang知识图谱
原:https://www.processon.com/view/link/5a9ba4c8e4b0a9d22eb3bdf0?from=timeline 百度脑图重制:http://naotu.bai ...
- C# Aspose.Cells.dll Excel操作总结
简介 Aspose.Cells是一款功能强大的 Excel 文档处理和转换控件,不依赖 Microsoft Excel 环境,支持所有 Excel 格式类型的操作. 下载 Aspose.Cells.d ...
- MySQL查询表与表字段的信息
环境: Mysql数据库 库名:db_name 表名: table_name1 table_name2 查询一个里面所有表的信息: use information_scheam; select * f ...