一、上篇回顾
通过上篇的简单描述,我们知道了桥接模式主要是为了解决,一个对象的多个维度的变化因素的变化太快,难以控制的问题,我们通过将每个维度
的变化因素进行抽象, 然后我们的对象只要依赖于抽象即可,具体的实现调用我们不关心,通过对象组合的方式,我们就能组合出我们想要的对象。无
疑这是一种非常灵活的也是满足设计模式的原则的,抽象和实现分离,使他们各自发生变化都不受对方的影响。而且我们也讲述了,使用桥接模式的几
个典型的场景,现在我们的实际项目中就有这样的问题,我也是在项目的使用过程中加深对桥接模式的理解的,桥接模式为系统在多个维度的变化的适
应性方面提供了很好的参考,特别适合底层框架的开发过程中使用,可以适应不同变化因素的改变。上篇桥接模式放出后,我也得到了不少朋友的反
馈,很感谢大家,这里特别的感谢一个名叫“萧萧”的朋友,他指出了我在文章中给出的例子有些不妥之处,我本篇将会对上篇中给出的例子,从给出
符合桥接模式的实现,这里对大家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次装饰来完成这个播放器的类型职责的添加。
三、本文大纲
a、上篇回顾。
b、摘要。
c、本文大纲。
d、装饰模式的特点及使用场景。
e、装饰模式的经典实现。
f、装饰模式的其他方案。
g、装饰模式使用总结。
h、系列进度。
i、下篇预告。
四、装饰模式的特点及使用场景
4.1、装饰模式的特点
装饰模式:装饰模式主要是解决了,我们以动态的,透明的形式为某个类型添加一个新的职责,客户程序可以不知道我们具体添加的功能职责,
而客户程序只是根据对象提供的方法进行调用即可。而具体职责操作留给装饰对象去完成。
我们这里给出装饰模式的经典模式的类图:
我们这里对这个经典结构进行稍微的说明:我们这里结合我们上面的播放器来说
1、对于Compnent,我们定义一个IPlayer这样的接口,我们接口中定义方法,Play()方法。
2、然后我们把要装饰这个播放器的装饰类型进行抽象,抽象出接口IDecorator接口。这个即可继承自IPlayer接口。
3、接着,我们定义一个简单的播放器对象,实现这个IPlayer接口。
4、我们实现播放音乐和视频文件的装饰类型的实现。
5、我们将简单的播放器对象,通过装饰对象进行装饰,为这个简单播放器套上一层一层的外壳,使其具有播放音乐和视频的功能。
4.2、装饰模式的使用场景
1、当我们需要为某个现有的对象,动态的增加一个新的功能或职责时,可以考虑使用装饰模式。
2、适应于某个对象的职责经常发生变化或者经常需要动态的增加职责,避免因为这种为了适应这样的变化,而增加继承子类扩展的方式,因为
这种方式为 造成,子类膨胀的速度过快,难以控制。
五、装饰模式的经典实现
经过上面的简单描述,我想大家很想知道装饰模式的经典实现方案吧!我们来结合播放器的实例来分析实现,下面给出核心代码:
/// <summary>
/// 简单播放器的统一接口
/// </summary>
public interface IPlayer
{
void Play();
}
/// <summary>
/// 播放器的普通实现类
/// </summary>
public class Player : IPlayer
{
#region IPlayer 成员public void Play()
{
throw new NotImplementedException();
}#endregion
}/// <summary>
/// 装饰器接口
/// </summary>
public interface IDecorator : IPlayer
{}
/// <summary>
/// 装饰器的抽象基类
/// </summary>
public abstract class DecoratorBase : IDecorator
{
protected IPlayer play = null;
public DecoratorBase(IPlayer player)
{
this.play = player;
}#region IPlayer 成员
public abstract void Play();
#endregion
}/// <summary>
/// 音乐播放器装饰器
/// </summary>
public class MusicDecorator : DecoratorBase
{
public MusicDecorator(IPlayer play)
: base(play)
{
}
public override void Play()
{
throw new NotImplementedException();
}
}/// <summary>
/// 视频播放器装饰类
/// </summary>
public class VedioDecorator : DecoratorBase
{
public VedioDecorator(IPlayer play)
: base(play)
{
}
public override void Play()
{
throw new NotImplementedException();
}
}具体的测试调用代码如下:
static void Main(string[] args)
{
IPlayer play = new Player();
play = new MusicDecorator(play);
play = new VedioDecorator(play);
}经过了2次的装饰后,该播放器对象,可以播放任何格式的文件。通过上面的过程,我们发现了我们在使用装饰模式的时候,有如下几点我们需要把我好:
1、我们在开发的过程中,如果需要为某个类型添加多个职责的时候,我们需要多个装饰实现类,这样类太多的时候,我们需要将这个对象装饰多次。
2、我们对我们的装饰过的对象,如何指定的撤销某个装饰过程的状态呢?类似我们使用某些应用程序中的undo操作。
3、对于动态装饰的对象,我们debug的时候,我们很难跟踪和调试。
六、装饰模式的其他方案
装饰模式的具体过程如下:
一、通过配置文件来完成装饰操作
具体的操作过程如下:配置文件中配置对象要完成的装饰步骤,通过公共的配置读取类,通过字典缓存装饰的步骤,然后传入普通的对象,返回装
饰后的对象。
我们这里给出配置文件的格式:
<?xml version="1.0" encoding="utf-8" ?>
<Decorator>
<Section>
<Class name="" type="">
<Step name="" type=""/>
<Step name="" type=""/>
<Step name="" type=""/>
</Class>
<Class name="" type="">
<Step name="" type=""/>
<Step name="" type=""/>
<Step name="" type=""/>
</Class>
<Class name="" type="">
<Step name="" type=""/>
<Step name="" type=""/>
<Step name="" type=""/>
</Class>
</Section>
</Decorator>我们来看具体的装饰器步骤构造器代码:
public class DecoratorFactory
{
public static T BuilderPlay<T>(T play)
{
Dictionary<Type, List<IDecorator>> steps = new Dictionary<Type, List<IDecorator>>();
List<IDecorator> list = steps[typeof(T)];foreach (IDecorator item in list)
{
play = (T)Activator.CreateInstance(item.GetType(), play);
}return play;
}
}二、通过特性+反射的形式来组织装饰的过程
这里就不给出具体的实现代码了,下篇或者后续给出分析几类基于特性+标记的实现方案。大伙也可以提出其他的不同实现思路。
七、装饰模式使用总结
通过上面的讲述,我们知道装饰模式,特别适合对某个类型的对象,动态的增加新的职责,应用程序就像使用原来的对象一样使用对象新增的装饰后的功能,装
饰模式就好像是穿了一层层的外壳,这样的方式避免了通过继承来为类型添加新的职责的形式可取,通过继承的方式容易造成子类的膨胀,但是当装饰类太多的时
候,也是个难以维护的问题,至少是在装饰对象的时候,我们可能需要多步操作来完成对象的装饰,这时候我们可以同上面提出的改进的方案,来完成自动配置装饰
模式,记录操作模式的状态,可以进行有效的回滚操作,以完成撤销操作。