子类复子类,子类何其多

假如我们需要为游戏中开发一种坦克,除了各种不同型号的坦克外,我们还希望在不同场合中为其增加以下一种或多种功能:比如红外线夜视功能,比如水陆两栖功能,比如卫星定位功能等等。

动机(Motivation)

上述描述的问题根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?

意图(Intent)

动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。

——《设计模式》GoF

结构(Structure)

 

代码结构

using System;

namespace Decorator.Structural
{
/// <summary>
/// MainApp startup class for Structural
/// Decorator Design Pattern.
/// </summary>
class MainApp
{
static void Main()
{
// Create ConcreteComponent and two Decorators
ConcreteComponent c = new ConcreteComponent();
ConcreteDecoratorA d1 = new ConcreteDecoratorA();
ConcreteDecoratorB d2 = new ConcreteDecoratorB(); // Link decorators
d1.SetComponent(c);
d2.SetComponent(d1); d2.Operation();
// Wait for user
Console.ReadKey();
}
} /// <summary>
/// The 'Component' abstract class
/// </summary>
abstract class Component
{
public abstract void Operation();
} /// <summary>
/// The 'ConcreteComponent' class
/// </summary>
class ConcreteComponent : Component
{
public override void Operation()
{
Console.WriteLine("ConcreteComponent.Operation()");
}
} /// <summary>
/// The 'Decorator' abstract class
/// </summary>
abstract class Decorator : Component
{
protected Component component;
public void SetComponent(Component component)
{
this.component = component;
}
public override void Operation()
{
if (component != null)
{
component.Operation();
}
}
} /// <summary>
/// The 'ConcreteDecoratorA' class
/// </summary>
class ConcreteDecoratorA : Decorator
{
public override void Operation()
{
base.Operation();
Console.WriteLine("ConcreteDecoratorA.Operation()");
}
} /// <summary>
/// The 'ConcreteDecoratorB' class
/// </summary>
class ConcreteDecoratorB : Decorator
{
public override void Operation()
{
base.Operation();
AddedBehavior();
Console.WriteLine("ConcreteDecoratorB.Operation()");
}
void AddedBehavior()
{
}
}
}

输出:

 

代码示例:

class Program
{
static void Main(string[] args)
{
// Create book
Book book = new Book("Worley", "Inside ASP.NET", 10);
book.Display(); // Create video
Video video = new Video("Spielberg", "Jaws", 23, 92);
video.Display(); // Make video borrowable, then borrow and display
Console.WriteLine("\nMaking video borrowable:"); Borrowable borrowvideo = new Borrowable(video);
borrowvideo.BorrowItem("Customer #1");
borrowvideo.BorrowItem("Customer #2"); borrowvideo.Display(); // Wait for user
Console.ReadKey(); }
} /// <summary>
/// The 'Component' abstract class
/// </summary>
abstract class LibraryItem
{
private int _numCopies; // Property
public int NumCopies
{
get { return _numCopies; }
set { _numCopies = value; }
} public abstract void Display();
} /// <summary>
/// The 'ConcreteComponent' class
/// </summary>
class Book : LibraryItem
{
private string _author;
private string _title; // Constructor
public Book(string author, string title, int numCopies)
{
this._author = author;
this._title = title;
this.NumCopies = numCopies;
} public override void Display()
{
Console.WriteLine("\nBook ------ ");
Console.WriteLine(" Author: {0}", _author);
Console.WriteLine(" Title: {0}", _title);
Console.WriteLine(" # Copies: {0}", NumCopies);
}
} /// <summary>
/// The 'ConcreteComponent' class
/// </summary>
class Video : LibraryItem
{
private string _director;
private string _title;
private int _playTime; // Constructor
public Video(string director, string title,
int numCopies, int playTime)
{
this._director = director;
this._title = title;
this.NumCopies = numCopies;
this._playTime = playTime;
} public override void Display()
{
Console.WriteLine("\nVideo ----- ");
Console.WriteLine(" Director: {0}", _director);
Console.WriteLine(" Title: {0}", _title);
Console.WriteLine(" # Copies: {0}", NumCopies);
Console.WriteLine(" Playtime: {0}\n", _playTime);
}
} /// <summary>
/// The 'Decorator' abstract class
/// </summary>
abstract class Decorator : LibraryItem
{
protected LibraryItem libraryItem; // Constructor
public Decorator(LibraryItem libraryItem)
{
this.libraryItem = libraryItem;
} public override void Display()
{
libraryItem.Display();
}
} /// <summary>
/// The 'ConcreteDecorator' class
/// </summary>
class Borrowable : Decorator
{
protected List<string> borrowers = new List<string>(); // Constructor
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(" borrower: " + borrower);
}
}
}

输出:

 

Decorator模式的几个要点

  • 通过采用组合、而非继承的手法, Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。
  • Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类应该透明——换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。
  • Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。
  • Decorator模式并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

.NET框架中的Decorator应用

设计模式学习之路——Decorator装饰模式(结构模式)的更多相关文章

  1. C#设计模式学习笔记:(3)抽象工厂模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...

  2. C#设计模式学习笔记:(2)工厂方法模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7567880.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...

  3. 设计模式学习之路——Facade 外观模式(结构型模式)

    动机: 组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战.如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系 ...

  4. C#设计模式学习笔记:(8)装饰模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7723225.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲结构型设计模式的第三个模式--装 ...

  5. C#设计模式学习笔记:(20)职责链模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8109100.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第八个模式--职 ...

  6. javascript设计模式学习之十二——享元模式

    一.享元模式的定义及使用场景 享元模式是为了解决性能问题而诞生的设计模式,这和大部分设计模式为了提高程序复用性的原因不太一样,如果系统中因为创建了大量类似对象而导致内存占用过高,享元模式就非常有用了. ...

  7. 设计模式学习总结(五)创建者模式(Builder)

    创建者模式,主要针对某些产品有类似的生产步骤,且有需要有先后顺序的进行各个部件的生成. 一.示例展示: 通过学习及总结,以下是我完成的创建者模式的示例: 1.创建产品类:Laptop public c ...

  8. 设计模式学习总结(六)原型模式(Prototype)

    原型模式即通过对象拷贝的方式来实现对同类对象的生成的一种设计模式! 浅复制:对于值类型,则直接复制该值,对于引用类型的字段则是对其引用的复制,如果原引用与现引用只要有一个的值发生变化,则都会造成两者值 ...

  9. C#设计模式学习笔记:(17)中介者模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7966240.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第五个模式--中 ...

随机推荐

  1. 2013 duilib入门简明教程 -- FAQ (19)

        虽然前面的教程几乎把所有的知识点都罗列了,但是有很多问题经常在群里出现,所以这里再次整理一下.     需要注意的是,在下面的问题中,除了加上XML属性外,主窗口必须继承自WindowImpl ...

  2. Azure Service Febric 笔记:Web API应用

    1.什么是Service Febric 贴一段微软官方的介绍 Service Fabric 是一种分布式系统平台,可让你轻松打包.部署和管理可缩放.可靠的微服务.Service Fabric 还解决了 ...

  3. XE1:使用SSMS创建Extended Events

    Extended Events 用于取代SQL trace,是SQL Server 追踪系统运行的神器,其创建过程十分简单. 一,创建Extended Events的Session step1,打开N ...

  4. Transaction Replication6:Transaction cleanup

    distribution中暂存的Transactions和Commands必须及时cleanup,否则,distribution size会一直增长,最终导致数据更新耗时增加,影响replicatio ...

  5. MVC4做网站六后台管理:6.2网站信息设置

    用来实现网站标题.名称.关键字.描述.版权等信息的设置. 模型字段: 网站的设置信息前后台都要用到,所以要把模型方式Ninesky/Models文件夹中,代码如下: ///////////////// ...

  6. Android之登录那点事

    随着互联网的高速发展,一个应用为了保护用户的隐私,通常会通过设置用户名+密码的验证方式保证用户隐私的相对安全,我知道一般网站的登录验证,通常会设置一个二维码,通过验证二维码,防止恶意软件通过机械程序, ...

  7. 【原创】开源Math.NET基础数学类库使用(15)C#计算矩阵行列式

                   本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新  开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 上个月 ...

  8. Rust初步(七):格式化

    在Rust中,如果要进行屏幕输出,或者写入到文件中,需要对数据进行格式化.这一篇总结一下它所支持的几种格式化方式. 这篇文章参考了以下官方文档,不过,按照我的风格,我还是会突出于C#语言的比较,这样可 ...

  9. .NET 中Barcode Library的应用二

    .NET中Barcode Library的应用二 介绍 在上一篇中我已经简单介绍了这个函数库(条形码应用之一------------函数库的简介).在这一篇中我将使用这个库提供更多的操作,希望对大家有 ...

  10. (转载)基于LBS地图的开发,满足地图上有头像的需求

    最近做的项目主要是LBS这块 主打成员定位功能 我们的UI设计是这样的 乍一看上去是挺好挺美观的 不同的人会显示不同的头像 可是当人扎堆的时候 问题就来了 当人多的时候(例如上图所示) 地图滑动起来就 ...