一、引言

在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类)、AccessoriesPhone(挂件手机类)等,这样就会导致 ”子类爆炸“问题,为了解决这个问题,我们可以使用装饰者模式来动态地给一个对象添加额外的职责。下面让我们看看装饰者模式。

二、装饰者模式的详细介绍

2.1 定义

装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任,装饰者模式相比生成子类可以更灵活地增加功能。

具体实现:

/// <summary>
/// 手机抽象类,即装饰者模式中的抽象组件类
/// </summary>
public abstract class Phone
{
public abstract void Print();
} /// <summary>
/// 苹果手机,即装饰着模式中的具体组件类
/// </summary>
public class ApplePhone:Phone
{
/// <summary>
/// 重写基类方法
/// </summary>
public override void Print()
{
Console.WriteLine("开始执行具体的对象——苹果手机");
}
} /// <summary>
/// 装饰抽象类,要让装饰完全取代抽象组件,所以必须继承自Photo
/// </summary>
public abstract class Decorator:Phone
{
private Phone phone; public Decorator(Phone p)
{
this.phone = p;
} public override void Print()
{
if (phone != null)
{
phone.Print();
}
}
} /// <summary>
/// 贴膜,即具体装饰者
/// </summary>
public class Sticker : Decorator
{
public Sticker(Phone p)
: base(p)
{
} public override void Print()
{
base.Print(); // 添加新的行为
AddSticker();
} /// <summary>
/// 新的行为方法
/// </summary>
public void AddSticker()
{
Console.WriteLine("现在苹果手机有贴膜了");
}
} /// <summary>
/// 手机挂件
/// </summary>
public class Accessories : Decorator
{
public Accessories(Phone p)
: base(p)
{
} public override void Print()
{
base.Print(); // 添加新的行为
AddAccessories();
} /// <summary>
/// 新的行为方法
/// </summary>
public void AddAccessories()
{
Console.WriteLine("现在苹果手机有漂亮的挂件了");
}
}

此时客户端调用代码如下:

class Customer
{
static void Main(string[] args)
{
// 我买了个苹果手机
Phone phone = new ApplePhone(); // 现在想贴膜了
Decorator applePhoneWithSticker = new Sticker(phone);
// 扩展贴膜行为
applePhoneWithSticker.Print();
Console.WriteLine("----------------------\n"); // 现在我想有挂件了
Decorator applePhoneWithAccessories = new Accessories(phone);
// 扩展手机挂件行为
applePhoneWithAccessories.Print();
Console.WriteLine("----------------------\n"); // 现在我同时有贴膜和手机挂件了
Sticker sticker = new Sticker(phone);
Accessories applePhoneWithAccessoriesAndSticker = new Accessories(sticker);
applePhoneWithAccessoriesAndSticker.Print();
Console.ReadLine();
}

在装饰者模式中各个角色有:

  • 抽象构件(Phone)角色:给出一个抽象接口,以规范准备接受附加责任的对象。
  • 具体构件(AppPhone)角色:定义一个将要接收附加责任的类。
  • 装饰(Dicorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
  • 具体装饰(Sticker和Accessories)角色:负责给构件对象 ”贴上“附加的责任。

三、装饰者模式的优缺点

看完装饰者模式的详细介绍之后,我们继续分析下它的优缺点。

优点:

  1. 装饰这模式和继承的目的都是扩展对象的功能,但装饰者模式比继承更灵活
  2. 通过使用不同的具体装饰类以及这些类的排列组合,设计师可以创造出很多不同行为的组合
  3. 装饰者模式有很好地可扩展性

缺点:装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变的更复杂。并且更多的对象会是的差错变得困难,特别是这些对象看上去都很像。

四、使用场景

下面让我们看看装饰者模式具体在哪些情况下使用,在以下情况下应当使用装饰者模式:

  1. 需要扩展一个类的功能或给一个类增加附加责任。
  2. 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
  3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能

c# 设计模式 之:装饰模式的更多相关文章

  1. 乐在其中设计模式(C#) - 装饰模式(Decorator Pattern)

    原文:乐在其中设计模式(C#) - 装饰模式(Decorator Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 装饰模式(Decorator Pattern) 作者:weba ...

  2. java设计模式之装饰模式

    发现设计模式的学习越来越让自己学习的东西太少了,应该多接触一些东西,多出去走一走. 装饰模式概念: 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活(大话设计模式) 在不 ...

  3. 【GOF23设计模式】装饰模式

    来源:http://www.bjsxt.com/ 一.[GOF23设计模式]_装饰模式.IO流底层架构.装饰和桥接模式的区别 package com.test.decorator; /** * Com ...

  4. Java——设计模式(装饰模式_IO)

     /* * 装饰设计模式: *  对一组对象的功能进行增强时,就可以使用该模式进行问题的解决; * 装饰和继承都能实现一样的特点:  就是进行功能的扩转增强. * */ public class  ...

  5. .net设计模式之装饰模式

    概述: 装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 装饰模式的特点: (1) 装饰对象和真实对象有相同的接口.这样 ...

  6. Java IO设计模式(装饰模式与适配器模式)

    01. 装饰模式 1. 定义 Decorator装饰器,就是动态地给一个对象添加一些额外的职责,动态扩展,和下面继承(静态扩展)的比较.因此,装饰器模式具有如下的特征: 它必须持有一个被装饰的对象(作 ...

  7. 【Unity与23种设计模式】装饰模式(Decorator)

    GoF中定义: "动态地附加额外的责任给一个对象.装饰模式提供了一个灵活的选择,让子类可以用来扩展功能." 装饰模式一般用来增加新功能 它可以避免更改已经实现的程序代码 从而增加系 ...

  8. C 设计模式:装饰模式

    最近在公司分享了下C语言版的设计模式,记录一下吧. 参考:<设计模式之禅>中“装饰模式”章节. 上面书中是用C++来实现的,我使用了书中的例子,改用C语言来实现. 一.基础知识 面向对象最 ...

  9. 从零开始单排学设计模式「装饰模式」黑铁 I

    阅读本文大概需要 3.6 分钟. 本篇是设计模式系列的第四篇,虽然之前也写过相应的文章,但是因为种种原因后来断掉了,而且发现之前写的内容也很渣,不够系统. 所以现在打算重写,加上距离现在也有一段时间了 ...

  10. 设计模式之装饰模式,session共享的底层原理

    前言 还记得当初写spring-session实现分布式集群session的共享的时候,里面有说到利用filter和HttpServletRequestWrapper可以定制自己的getSession ...

随机推荐

  1. EF CodeFirst Mirgration

    新建类库Models,加入以下三个类: Product: public class Product { /// <summary> /// 编号 /// </summary> ...

  2. 图解-安卓中调用OpenGL

    游戏开发中经常使用到OpenGL,当然很多人都喜欢直接用现有的游戏引擎,但相信了解的更多对你没有坏处 安卓开发中,采用的OpenGL ex2的规范,前几天看了下这个规范,整体上难度比1.0规范难度加大 ...

  3. mysql 存储过程,函数,触发器

    存储过程和函数 mysql> HELP CREATE PROCEDURE; Name: 'CREATE PROCEDURE' Description: Syntax: CREATE [DEFIN ...

  4. tomcat启动(三)Catalina分析-load方法分析

    load()方法按从上到下顺序分析(主要分析本人所没学过的知识点,其它略过...). Digester类作用 使用sax技术对xml进行解析 未开始解析时Digester.push(this)这个用来 ...

  5. B+树 -- Java实现

    一.B+树定义 B+树定义:关键字个数比孩子结点个数小1的树. 除此之外B+树还有以下的要求: B+树包含2种类型的结点:内部结点(也称索引结点)和叶子结点.根结点本身即可以是内部结点,也可以是叶子结 ...

  6. springMVC对于Controller返回值的可选类型

    2018-01-11 对于springMVC处理方法支持支持一系列的返回方式:  (1)ModelAndView (2)Model (3)ModelMap (4)Map (5)View (6)Stri ...

  7. ruby文件操作

    Ruby代码 1.#读文件 2.f = File.open("myfile.txt", "r") 3.f.each_line do|line| 4.puts & ...

  8. django2.1---中间件

    在http请求 到达视图函数之前   和视图函数return之后,django会根据自己的规则在合适的时机执行中间件中相应的方法. Django1.9版本以后中间件的执行流程 1.执行完所有的requ ...

  9. JavaScript 函数使用return返回值

    我们可以把数据通过函数的 参数 来传入函数,也可以使用 return 语句把数据从一个函数中传出来. 举个栗子 function plusThree(num) {return num + 3;}var ...

  10. Runtime初识

    什么是Runtime   我们写的代码在程序运行过程中都会被转化成runtime的C代码执行,例如[target doSomething];会被转化成objc_msgSend(target, @sel ...