十二.结构型设计模式——Decorator Patten(装饰模式)

  • 定义

  动态地给一个对象增加其他职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更加灵活。

  UML类图如下:

  

  其中类和对象的关系为:

  1. Component(部件):定义对象的接口,可以给这些对象动态增加职责(方法)。

  2. ConcreteComponent(具体部件):定义具体的对象,Decorator可以给它增加额外的职责(方法)。

  3. Decorator(装饰抽象类):维护一个内有的Component,并且定义一个与Component接口一致的接口。

  4. ConcreteDecorator(具体装饰对象):具体的装饰对象,给内在的具体部件对象增加具体的职责(方法)。

  典型应用的顺序图如下:

  

  客户调用具体装饰类的AddOperation方法,而具体装饰类又调用自身具体部件(ConcreteComponent)类的Operation方法。

  • 实例1——图书馆中的项目

  采用装饰模式为图书馆中的项目(书或影像盘)增加“可借”功能性。

  UML类图如下:

  

代码
//定义抽象类LibraryItem
abstract class LibraryItem
{
private int numCopies;
public int NumCopies
{
get { return numCopies; }
set { numCopies = value; }
}
public abstract void Display();
}
//定义具体的部件类Book
class Book : LibraryItem
{
private string author;
private string title;
public Book(string author, string title, int numCopies)
{
this.author = author;
this.title = title;
this.NumCopies = numCopies;
}
public override void Display()
{
Console.WriteLine(
"\n书 --------------");
Console.WriteLine(
" 作者:{0}",author);
Console.WriteLine(
" 书名:{0}", title); ;
Console.WriteLine(
" #数量:{0}", NumCopies);
}
}
//定义具体的部件类Video
class Video : LibraryItem
{
private string director;
private string title;
private int playTime;
public Video(string director, string title, int numCopies, int playTime)
{
this.director = director;
this.title = title;
this.playTime = playTime;
this.NumCopies = numCopies;
}
public override void Display()
{
Console.WriteLine(
"\n影像 --------------");
Console.WriteLine(
" 导演:{0}", director);
Console.WriteLine(
" 片名:{0}", title); ;
Console.WriteLine(
" #数量:{0}", NumCopies);
Console.WriteLine(
" 播放时间:{0}", playTime);
}
}

//定义抽象装饰类
abstract class Decorator : LibraryItem
{
protected LibraryItem libraryItem;
public Decorator(LibraryItem libraryItem)
{
this.libraryItem=libraryItem;
}
public override void Display()
{
libraryItem.Display();
}
}
//定义具体装饰类
class Borrowable : Decorator
{
protected ArrayList borrowers = new ArrayList();
//调用父类的构造函数
public Borrowable(LibraryItem libraryItem) : base(libraryItem) { }
public void BorrowItem(string name)
{
borrowers.Add(name);
libraryItem.NumCopies
--;
}
public void ReturnItem(string name)
{
borrowers.Remove(name);
libraryItem.NumCopies
++;
}
public override void Display()
{
base.Display();
foreach(string borrower in borrowers)
{
Console.WriteLine(
" 借出人:{0}", borrower);
}
}
}

//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
//创建Book及Video,并显示出来
Book book = new Book("Schnell", "My Home", 10);
Video video
= new Video("Spielberg", "Schindler's list", 23, 60);
book.Display();
video.Display();
//增加video的borrowable属性,然后借出并显示
Console.WriteLine("\n影像增加可借属性");
Borrowable borrowvideo
= new Borrowable(video);
borrowvideo.BorrowItem(
"张三");
borrowvideo.BorrowItem(
"李四");
borrowvideo.BorrowItem(
"王五");
borrowvideo.ReturnItem(
"王五");
borrowvideo.Display();
Console.Read();
}
}

 

 

  • 优势和缺陷

  装饰模式提供了比静态继承更好的柔韧性,它允许开发一系列的功能类用来代替增加对象的行为,这既不会污染原来对象的源码,还能使代码更容易编写,使类更具扩展性,因为变化都是由新的装饰类来完成。还可以建立连接的装饰对象关系链。

  需要注意的是,装饰链不宜过长。装饰链太长会使系统花费较长时间用于初始化对象,同时信息在链中的传递也会浪费太多的时间。这个情况好比物品包装,包了一层又一层,大包套小包。另外,如果原来的对象接口发生变化,它所以的装饰类都要修改以匹配它的变化。派生子类会影响对象的内部,而一个Decorator只会影响对象的外表。

  • 应用情景

  下面的情景很适合应用装饰模式:

  1. 你想透明并且动态地给对象增加新的职责(方法),而不会影响其他对象。

  2. 你给对象增加的职责在未来会发生变化。

  3. 用子类扩展功能不实际的情况下。

posted on 2010-11-04 22:34  tLEE  阅读(711)  评论(0编辑  收藏  举报

导航