系统架构技能之设计模式—装饰模式
系统架构技能之设计模式—装饰模式
一、上篇回顾
通过上篇的简单描述,我们知道了桥接模式主要是为了解决,一个对象的多个维度的变化因素的变化太快,难以控制的问题,我们通过将每个维度的变化因素进行抽象, 然后我们的对象只要依赖于抽象即可,具体的实现调用我们不关心,通过对象组合的方式,我们就能组合出我们想要的对象。无疑这是一种非常灵活的也是满足设计模式的原则的,抽象和实现分离,使他们各自发生变化都不受对方的影响。而且我们也讲述了,使用桥接模式的几个典型的场景,现在我们的实际项目中就有这样的问题,我也是在项目的使用过程中加深对桥接模式的理解的,桥接模式为系统在多个维度的变化的适应性方面提供了很好的参考,特别适合底层框架的开发过程中使用,可以适应不同变化因素的改变。上篇桥接模式放出后,我也得到了不少朋友的反馈,很感谢大家,这里特别的感谢一个名叫“萧萧”的朋友,他指出了我在文章中给出的例子有些不妥之处,我本篇将会对上篇中给出的例子,从给出符合桥接模式的实现,这里对大家say sorry !
public interface IORM
{
ISave Save
{
get;
set;
}
IDelete Delete
{
get;
set;
}
ICreate Create
{
get;
set;
}
ICache Cache
{
get;
set;
}
IQuery Query
{
get;
set;
}
void Test();
}
我们在这个接口的定义中并没有体现桥接模式中的变化因素的抽象的概念,我们修改为如下形式:
public interface IORM
{
ISave Save
{
get;
set;
}
IDelete Delete
{
get;
set;
}
ICreate Create
{
get;
set;
}
void Test();
}
public interface ICache
{
object Cache(object o);
}
public interface IQuery
{
object Query(string condition);
}
具体的缓存服务与查询服务的实现:
public class Cache : ICache
{
public object Cache(object o){
return o;
}
}
public class Query: IQuery
{
public object Query(string condition){
return GetList(condition);
}
}
具体的ORM实现代码如下:
public class ORM : IORM
{
private IQuery query=new Query();
private ICache cache=new Cache();
public ORM(IQuery query,ICache cache)
{
this.query=query;
this.cache=cache;
}
public ISave Save
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public IDelete Delete
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public ICreate Create
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public void Test()
{
///测试缓存对象!
this.cache.Cache(new object());
}
}
对于之前按照我给的思路的方式,请大家纠正下思路,我这里对大家造成的误解,表示抱歉,因为我举例子的时候,表述的和我们本文的初衷有些偏差,希望大家见谅,请大家继续提出宝贵意见和建议。
二、摘要
本篇将会开始讲述结构型模式中的很有技巧性的模式之一-装饰模式,我们理解的装饰就是给类穿个衣服,或者装扮一下,让某个类具有一个新的特性或者新的职责。我们在应对对于通过类的继承来实现类扩展功能时候的膨胀的速度过快的问题,我想这个也是我们上一篇讲述的桥接模式中提到的一些问题,不过装饰模式和桥接模式的侧重点稍微不同。装饰模式更侧重的是某个类型的功能经常的动态的增加的情况。那么如果我们通过继承的方式来实现,那么将会是噩梦。继承复继承,继承何其多,然而通过装饰模式,我们就能很优雅的为某个类型添加新的职责。我们也知道,由于C#不支持多继承,我们只能为某个类型添加新增功能的时候,我们只能单继承+多实现接口的形式来为类型添加新的功能。而装饰模式可以很优雅的解决这样的问题。
装饰模式的主要目的就是:动态的为某个类型添加新的职责。
我们将自己开发一个简单的播放器,我们通过第一次装饰可以为播放器增加播放音乐的功能,具体包括播放所有的音频格式文件,通过第二次装饰,我们可以为这个播放器添加播放视频格式文件的功能,这时候,如果我们想要一个万能的播放器,那么我们将通过2次装饰来完成这个播放器的类型职责的添加。
三、本文大纲
四、装饰模式的特点及使用场景
4.1、装饰模式的特点
装饰模式:装饰模式主要是解决了,我们以动态的,透明的形式为某个类型添加一个新的职责,客户程序可以不知道我们具体添加的功能职责,而客户程序只是根据对象提供的方法进行调用即可。而具体职责操作留给装饰对象去完成。
我们这里给出装饰模式的经典模式的类图:
我们这里对这个经典结构进行稍微的说明:我们这里结合我们上面的播放器来说
1、对于Compnent,我们定义一个IPlayer这样的接口,我们接口中定义方法,Play()方法。
2、然后我们把要装饰这个播放器的装饰类型进行抽象,抽象出接口IDecorator接口。这个即可继承自IPlayer接口。
3、接着,我们定义一个简单的播放器对象,实现这个IPlayer接口。
4、我们实现播放音乐和视频文件的装饰类型的实现。
5、我们将简单的播放器对象,通过装饰对象进行装饰,为这个简单播放器套上一层一层的外壳,使其具有播放音乐和视频的功能。
4.2、装饰模式的使用场景
1、当我们需要为某个现有的对象,动态的增加一个新的功能或职责时,可以考虑使用装饰模式。
2、适应于某个对象的职责经常发生变化或者经常需要动态的增加职责,避免因为这种为了适应这样的变化,而增加继承子类扩展的方式,因为这种方式为 造成,子类膨胀的速度过快,难以控制。
五、装饰模式的经典实现
经过上面的简单描述,我想大家很想知道装饰模式的经典实现方案吧!我们来结合播放器的实例来分析实现,下面给出核心代码:
/// 简单播放器的统一接口
///
public interface IPlayer
{
void Play();
}
///
/// 播放器的普通实现类
///
public class Player : IPlayer
{
#region IPlayer 成员
public void Play()
{
throw new NotImplementedException();
}
#endregion
}
//
// 装饰器接口
//
public interface IDecorator : IPlayer
///
/// 装饰器的抽象基类
///
public abstract class DecoratorBase : IDecorator
{
protected IPlayer play = null;
public DecoratorBase(IPlayer player)
{
this.play = player;
}
#region IPlayer 成员
public abstract void Play();
#endregion
}
///
/// 音乐播放器装饰器
///
public class MusicDecorator : DecoratorBase
{
public MusicDecorator(IPlayer play)
: base(play)
{
}
public override void Play()
{
throw new NotImplementedException();
}
}
///
/// 视频播放器装饰类
///
public class VedioDecorator : DecoratorBase
{
public VedioDecorator(IPlayer play)
: base(play)
{
}
public override void Play()
{
throw new NotImplementedException();
}
}
具体的测试调用代码如下:
{
IPlayer play = new Player();
play = new MusicDecorator(play);
play = new VedioDecorator(play);
}
经过了2次的装饰后,该播放器对象,可以播放任何格式的文件。通过上面的过程,我们发现了我们在使用装饰模式的时候,有如下几点我们需要把我好:
1、我们在开发的过程中,如果需要为某个类型添加多个职责的时候,我们需要多个装饰实现类,这样类太多的时候,我们需要将这个对象装饰多次。
2、我们对我们的装饰过的对象,如何指定的撤销某个装饰过程的状态呢?类似我们使用某些应用程序中的undo操作。
3、对于动态装饰的对象,我们debug的时候,我们很难跟踪和调试。
六、装饰模式的其他方案
一、通过配置文件来完成装饰操作
具体的操作过程如下:配置文件中配置对象要完成的装饰步骤,通过公共的配置读取类,通过字典缓存装饰的步骤,然后传入普通的对象,返回装饰后的对象。
我们这里给出配置文件的格式:
版权声明:本文为博主原创文章,未经博主允许不得转载。