23种设计模式之装饰器模式(Decorator Pattern)
装饰器模式(Decorator Pattern)
允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
通过采用组合、而非继承的手法,Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以
根据需要扩展多个功能。避免了单独使用继承带来的“灵活性差"和"多子类衍生问题"。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能
缺点:多层装饰比较复杂
使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销 例如游戏装备,以及各层vip,商场的多重优惠等等
应用实例:不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体

装饰器模式主要组成部分:
Component:定义一个对象接口,可以给这些对象动态地添加职责
ConcreteComponent:定义一个对象,可以给这个对象添加一些职责
Decorator:维持一个指向Component的指针,并定义一个与Component接口一致的接口
ConcreteDecorator:负责向ConcreteComponent添加功能
假如我们需要为游戏中开发一种坦克,除了各种不同型号的坦克外,我们还希望在不同场合中为其增加以下一种或多种功能;比如红外线夜视功能,比如水陆两栖功能,比如卫星定位功能等等
按类继承的作法如下:
//抽象坦克
public abstract class Tank
{
public abstract void Shot();
public abstract void Run();
}
各种型号:
//T50型号
public class T50:Tank
{
public override void Shot()
{
Console.WriteLine("T50坦克平均每秒射击5发子弹");
}
public override void Run()
{
Console.WriteLine("T50坦克平均每时运行30公里");
}
}
//T75型号
public class T75 : Tank
{
public override void Shot()
{
Console.WriteLine("T75坦克平均每秒射击10发子弹");
}
public override void Run()
{
Console.WriteLine("T75坦克平均每时运行35公里");
}
}
//T90型号
public class T90 :Tank
{
public override void Shot()
{
Console.WriteLine("T90坦克平均每秒射击10发子弹");
}
public override void Run()
{
Console.WriteLine("T90坦克平均每时运行40公里");
}
}
各种不同功能的组合:比如IA具有红外功能接口、IB具有水陆两栖功能接口、IC具有卫星定位功能接口。
//T50坦克各种功能的组合
public class T50A:T50,IA
{
//具有红外功能
}
public class T50B:T50,IB
{
//具有水陆两栖功能
}
public class T50C:T50,IC
{ }
public class T50AB:T50,IA,IB
{}
public class T50AC:T50,IA,IC
{}
public class T50BC:T50,IB,IC
{}
public class T50ABC:T50,IA,IB,IC
{}
//T75各种不同型号坦克各种功能的组合
public class T75A:T75,IA
{
//具有红外功能
}
public class T75B:T75,IB
{
//具有水陆两栖功能
}
public class T75C:T75,IC
{
//具有卫星定位功能
}
public class T75AB:T75,IA,IB
{
//具有红外、水陆两栖功能
}
public class T75AC:T75,IA,IC
{
//具有红外、卫星定位功能
}
public class T75BC:T75,IB,IC
{
//具有水陆两栖、卫星定位功能
}
public class T75ABC:T75,IA,IB,IC
{
//具有红外、水陆两栖、卫星定位功能
}
//T90各种不同型号坦克各种功能的组合
public class T90A:T90,IA
{
//具有红外功能
}
public class T90B:T90,IB
{
//具有水陆两栖功能
}
public class T90C:T90,IC
{
//具有卫星定位功能
}
public class T90AB:T90,IA,IB
{
//具有红外、水陆两栖功能
}
public class T90AC:T90,IA,IC
{
//具有红外、卫星定位功能
}
public class T90BC:T90,IB,IC
{
//具有水陆两栖、卫星定位功能
}
public class T90ABC:T90,IA,IB,IC
{
//具有红外、水陆两栖、卫星定位功能
}
由此可见,如果用类继承实现,子类会爆炸式地增长
装饰器模式实现代码:
namespace Decorator
{
public abstract class Tank
{
public abstract void Shot();
public abstract void Run();
}
}
public class T50:Tank
{
public override void Shot()
{
Console.WriteLine("T50坦克平均每秒射击5发子弹");
}
public override void Run()
{
Console.WriteLine("T50坦克平均每时运行30公里");
}
}
public class T75 : Tank
{
public override void Shot()
{
Console.WriteLine("T75坦克平均每秒射击10发子弹");
}
public override void Run()
{
Console.WriteLine("T75坦克平均每时运行35公里");
}
}
public class T90 :Tank
{
public override void Shot()
{
Console.WriteLine("T90坦克平均每秒射击10发子弹");
}
public override void Run()
{
Console.WriteLine("T90坦克平均每时运行40公里");
}
}
public abstract class Decorator :Tank //Do As 接口继承 非实现继承
{
private Tank tank; //Has a 对象组合
public Decorator(Tank tank)
{
this.tank = tank;
}
public override void Shot()
{
tank.Shot();
}
public override void Run()
{
tank.Run();
}
}
public class DecoratorA :Decorator
{
public DecoratorA(Tank tank) : base(tank)
{
}
public override void Shot()
{
//Do some extension //功能扩展 且有红外功能
base.Shot();
}
public override void Run()
{ base.Run();
}
}
public class DecoratorB :Decorator
{
public DecoratorB(Tank tank) : base(tank)
{
}
public override void Shot()
{
//Do some extension //功能扩展 且有水陆两栖功能
base.Shot();
}
public override void Run()
{
base.Run();
}
}
public class DecoratorC :Decorator
{
public DecoratorC(Tank tank) : base(tank)
{
}
public override void Shot()
{
//Do some extension //功能扩展 且有卫星定位功能
base.Shot();
}
public override void Run()
{ base.Run();
} }
前端调用:
static void Main(string[] args)
{
Tank tank = new T50();
DecoratorA da = new DecoratorA(tank); //且有红外功能
DecoratorB db = new DecoratorB(da); //且有红外和水陆两栖功能
DecoratorC dc = new DecoratorC(db); //且有红外、水陆两栖、卫星定们三种功能
dc.Shot();
dc.Run();
}
Decorator在.NET(Stream)中的应用:
BufferedStream和CryptoStream其实就是两个包装类,这里的Decorator模式省略了抽象装饰角色(Decorator),示例代码如下:
public static void Main(string[] args)
{
MemoryStream ms =
new MemoryStream(new byte[] { ,,,,});
//扩展了缓冲的功能
BufferedStream buff = new BufferedStream(ms); //扩展了缓冲,加密的功能
CryptoStream crypto = new CryptoStream(buff);
}
public sealed class BufferedStream : Stream
{
// Methods
private BufferedStream();
public BufferedStream(Stream stream);
public BufferedStream(Stream stream, int bufferSize);
// Fields
private int _bufferSize;
private Stream _s;
}
通过反编译,可以看到BufferedStream类的代码(只列出部分),它是继承于Stream类
本文参考文档:
https://www.cnblogs.com/abcdwxc/archive/2007/09/06/884495.html
https://www.runoob.com/design-pattern/decorator-pattern.html
https://www.cnblogs.com/banluduxing/p/9152453.html
23种设计模式之装饰器模式(Decorator Pattern)的更多相关文章
- python 设计模式之装饰器模式 Decorator Pattern
#写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...
- c#设计模式之装饰器模式(Decorator Pattern)
引子 在面向对象语言中,我们常常会听到这样一句话:组合优于继承.那么该如何去理解这句话呢? 下面我将以游戏装备为模型用简单的代码去展示它 先创建一个装备的抽象类,然后创建刀枪2个具体的业务子类 pub ...
- 【UE4 设计模式】装饰器模式 Decorator Pattern
概述 描述 动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活.是一种对象结构型模式. 套路 抽象构件(Component) 具体构 ...
- 实践GoF的23种设计模式:装饰者模式
摘要:装饰者模式通过组合的方式,提供了能够动态地给对象/模块扩展新功能的能力.理论上,只要没有限制,它可以一直把功能叠加下去,具有很高的灵活性. 本文分享自华为云社区<[Go实现]实践GoF的2 ...
- 设计模式学习--装饰者模式(Decorator Pattern)
概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...
- 设计模式(三)——装饰器模式(Decorator Pattern)
发现太过于刻意按照计划来写博客,有点不实际,刚好最近在一个网课上复习AOP的知识,讲到了装饰器模式和代理模式,顺便复习总结一下. 首先了解一下装饰器模式,从名字里面可以看出来,装饰器模式就类似于房子装 ...
- 用最简单的例子理解装饰器模式(Decorator Pattern)
假设有一个公司要做产品套餐,即把不同的产品组合在一起,不同的组合对应不同的价格.最终呈现出来的效果是:把产品组合的所有元素呈现出来,并显示该组合的价格. 每个产品都有名称和价格,首先设计一个关于产品的 ...
- C#设计模式之装饰者模式(Decorator Pattern)
1.概述 装饰者模式,英文名叫做Decorator Pattern.装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 2 ...
- php装饰器模式(decorator pattern)
十一点了. <?php /* The decorator pattern allows behavior to be added to an individual object instance ...
随机推荐
- Spring学习之旅(十三)--使用NoSQL数据库
除了关系型数据库之外,现在还有一种 NoSQL 数据库非常流行,而 Spring 自然也没有放过对它的支持. NoSQL 数据库有很多种,如: MongoDBGenericJackson2JsonRe ...
- 【sybase】You can’t run SELECT INTO in this database的解决办法
进入master 数据库,使用sp_dboption test ,'select into',true命令(其中test为要设置可用的数据库)
- 本地搭建持续集成(AzureDevops)
下载地址:https://visualstudio.microsoft.com/zh-hans/downloads/ 首先你需要SQL2017以上版本 ,不支持以下版本 完成下载之后进行安装(可选中文 ...
- 分享一个赚钱方法:用趣分类app在家轻松赚钱
什么是趣分类 近期,垃圾分类是社会各界和广大市民关心的一个热门话题,随着垃圾分类工作的推进,各地都掀起学习垃圾分类的热潮.为了我们的美好生活,打响"垃圾分类"这场硬仗刻不容缓.据了 ...
- [Revit]开始:编写一个简单外部命令
1 创建项目 以Visual Stidio作为开发工具,测试平台为Revit 2017 打开VS,创建一个C# .NET Framwork类库项目,选择..net框架版本为.NET Framwork ...
- [普及]NOIP 2015 推销员 贪心
NOIP 2015 推销员 题意: 有一个喜欢疲劳的推销员,告诉你在一个单口胡同(数轴)中的n户家庭的位置,和向他们推销可以获得的疲劳度.分别输出向(1,2,3,4...n)户人家推销可以得到的最大疲 ...
- CodeForces 1058 F Putting Boxes Together 树状数组,带权中位数
Putting Boxes Together 题意: 现在有n个物品,第i个物品他的位置在a[i],他的重量为w[i].每一个物品移动一步的代价为他的w[i].目前有2种操作: 1. x y 将第x的 ...
- 牛客小白月赛6 I 公交线路 最短路 模板题
链接:https://www.nowcoder.com/acm/contest/136/I来源:牛客网 题目描述 P市有n个公交站,之间连接着m条道路.P市计划新开设一条公交线路,该线路从城市的东站( ...
- Java日志框架总结
1. 前言 从写代码开始,就陆陆续续接触到了许多日志框架,较常用的属于LOG4J,LogBack等.每次自己写项目时,就copy前人的代码或网上的demo.配置log4j.properties或者lo ...
- 【Offer】[23] 【链表中环的入口结点】
题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 一个链表中包含环,如何找出环的入口结点?  思路分析 判断链表中是否有环:用快慢指针的方法,慢指针走一步,快指针走两步,如果快指针追上 ...