Head First设计模式之装饰者模式

一、定义

装饰者模式,英文叫Decorator Pattern,在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案。

 

设计原则:

1. 多用组合,少用继承。

  利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。

2. 类应设计的对扩展开放,对修改关闭。

 

要点:

1. 装饰者和被装饰对象有相同的超类型。

2. 可以用一个或多个装饰者包装一个对象。

3. 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。

4. 对象可以在任何时候被装饰,所以可以在运行时动态的,不限量的用你喜欢的装饰者来装饰对象。

5. 装饰模式中使用继承的关键是想达到装饰者和被装饰对象的类型匹配,而不是获得其行为。

6. 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。在实际项目中可以根据需要为装饰者添加新的行为,做到“半透明”装饰者。

7. 适配器模式的用意是改变对象的接口而不一定改变对象的性能,而装饰模式的用意是保持接口并增加对象的职责

 

二、结构

 

 

装饰者模式的实现类图:

 

从图中可以看出装饰着模式包含如下参与者:

1、一个被包装类和包装类均需遵守的接口——IComponent;

2、被包装类——ConcreteComponent;

3、包装类的抽象类——Decorator;

4、包装类的具体实现——DecoratorA、DecoratorB;

5、发起调用的客户端程序——Client。

三、实现

 产品类

    /// <summary>
    /// 商品类
    /// </summary>
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

产品块

/// <summary>
    /// 产品块接口-被包装对象和包装对象均实现此接口
    /// </summary>
    public interface IProductsBlock
    {
        List<Product> GetProductsBlock();
    }

    /// <summary>
    /// 基本商品块-被包装的基础对象
    /// </summary>
    public class ProductsBlock : IProductsBlock
    {
        public List<Product> GetProductsBlock()
        {
            List<Product> products = new List<Product>() {
                  new Product() { Id = 11, Name = "一般商品1" },
                  new Product() { Id = 12, Name = "一般商品2" },
                  new Product() { Id = 13, Name = "一般商品3" }
             };

            return products;
        }
    }

    /// <summary>
    /// 包装类的抽象父类
    /// </summary>
    public abstract class BlockDecorator : IProductsBlock
    {
        protected IProductsBlock block;

        public BlockDecorator(IProductsBlock block)
        {
            this.block = block;
        }

        public abstract List<Product> GetProductsBlock();
    }

    /// <summary>
    /// 附加广告商品的包装器实现
    /// </summary>
    public class AdDecorator : BlockDecorator
    {
        public AdDecorator(IProductsBlock block)
            : base(block)
        { }

        public override List<Product> GetProductsBlock()
        {
            List<Product> adProducts = new List<Product>(){
                  new Product() { Id = 11, Name = "广告商品1" },
                  new Product() { Id = 12, Name = "广告商品2" }
                };
            var list = this.block.GetProductsBlock();
            list.InsertRange(0, adProducts);

            return list;
        }
    }

    /// <summary>
    /// 附加降价商品的包装类实现
    /// </summary>
    public class CutPriceDecorator : BlockDecorator
    {
        public CutPriceDecorator(IProductsBlock block)
            : base(block)
        { }

        public override List<Product> GetProductsBlock()
        {
            List<Product> adProducts = new List<Product>() {
                  new Product() { Id = 21, Name = "降价商品1" },
                  new Product() { Id = 22, Name = "降价商品2" }
              };
            var list = this.block.GetProductsBlock();
            list.InsertRange(0, adProducts);

            return list;
        }
    }

调用

//组装过程
            IProductsBlock block = new ProductsBlock();

            block = new CutPriceDecorator(block);//后期新增的降价商品

            block = new AdDecorator(block);

            //对客户程序来说,包装是透明的
            var products = block.GetProductsBlock();

            foreach (var p in products)
            {
                Console.WriteLine(p.Name);
            }
            Console.WriteLine("按任意键结束...");
            Console.ReadKey();

四、适用场景

1、 想透明并且动态地给对象增加新的职责的时候。
2、 给对象增加的职责,在未来存在增加或减少可能。
3、 用继承扩展功能不太现实的情况下,应该考虑用组合的方式。

五、优缺点

装饰者模式的优点:
1、 通过组合而非继承的方式,实现了动态扩展对象的功能的能力。
2、 有效避免了使用继承的方式扩展对象功能而带来的灵活性差,子类无限制扩张的问题。
3、 充分利用了继承和组合的长处和短处,在灵活性和扩展性之间找到完美的平衡点。
4、 装饰者和被装饰者之间虽然都是同一类型,但是它们彼此是完全独立并可以各自独立任意改变的。
5、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
装饰者模式的缺点:
1、 装饰链不能过长,否则会影响效率。
2、 因为所有对象都是Component,所以如果Component内部结构发生改变,则不可避免地影响所有子类(装饰者和被装饰者),也就是说,通过继承建立的关系总是脆弱地,如果基类改变,势必影响对象的内部,而通过组合(Decoator HAS A Component)建立的关系只会影响被装饰对象的外部特征。
3、只在必要的时候使用装饰者模式,否则会提高程序的复杂性,增加系统维护难度。

六、.NET中装饰者模式的实现

在.NET 类库中也有装饰者模式的实现,该类就是System.IO.Stream,下面看看Stream类结构:

上图中,BufferedStream、CryptoStream和GZipStream其实就是两个具体装饰类,这里的装饰者模式省略了抽象装饰角色(Decorator)。下面演示下客户端如何动态地为MemoryStream动态增加功能的。

 

       MemoryStream memoryStream = new MemoryStream(new byte[] {95,96,97,98,99});

            // 扩展缓冲的功能
            BufferedStream buffStream = new BufferedStream(memoryStream);

            // 添加加密的功能
            CryptoStream cryptoStream = new CryptoStream(memoryStream,new AesManaged().CreateEncryptor(),CryptoStreamMode.Write);
            // 添加压缩功能
            GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, true);

 

 

装饰者模式采用对象组合而非继承的方式实现了再运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了单独使用继承带来的 ”灵活性差“和”多子类衍生问题“。

同时它很好地符合面向对象设计原则中 ”优先使用对象组合而非继承“和”开放-封闭“原则。

 

主要参考文章:http://www.cnblogs.com/zdy_bit/archive/2012/08/31/2665716.html

 

欢迎阅读本系列文章:Head First设计模式之目录

 

时间: 2024-10-16 15:18:20

Head First设计模式之装饰者模式的相关文章

PHP设计模式之装饰者模式代码实例

  这篇文章主要介绍了PHP设计模式之装饰者模式代码实例,装饰者模式就是不修改原类代码和继承的情况下动态扩展类的功能,本文就给出了代码实例,需要的朋友可以参考下 定义: 装饰者模式就是不修改原类代码和继承的情况下动态扩展类的功能.传统的编程模式都是子类继承父类实现方法重载,使用装饰器模式,只需添加一个新的装饰器对象,更加灵活,避免类数量和层次过多. 角色: Component(被装饰对象基类) ConcreteComponent(具体被装饰对象) Decorator(装饰者基类) Contret

深入理解JavaScript系列(29):设计模式之装饰者模式详解

 这篇文章主要介绍了深入理解JavaScript系列(29):设计模式之装饰者模式详解,装饰者用用于包装同接口的对象,不仅允许你向方法添加行为,而且还可以将方法设置成原始对象调用(例如装饰者的构造函数),需要的朋友可以参考下     介绍 装饰者提供比继承更有弹性的替代方案. 装饰者用用于包装同接口的对象,不仅允许你向方法添加行为,而且还可以将方法设置成原始对象调用(例如装饰者的构造函数). 装饰者用于通过重载方法的形式添加新功能,该模式可以在被装饰者前面或者后面加上自己的行为以达到特定的目的.

C#设计模式(9)——装饰者模式(Decorator Pattern)

原文:C#设计模式(9)--装饰者模式(Decorator Pattern) 一.引言 在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类).AccessoriesPhone(挂件手机类)等,这样就会导致 "子类爆炸"问题,为了解决这个问题,我们可以使用装饰者模式来动态地给一个对象添加额外的职责.下面让我们看看装饰者模式. 二.装饰者模式的详细介绍 2.

PHP设计模式之装饰器模式

装饰器设计模式适用于下列工作场合: 需求变化是快速和细小的,而且几乎不影响应用程序的其他部分.() 使用装饰器设计模式设计类的目标是: 不必重写任何已有的功能性代码,而是对某个基于对象应用增量变化. 装饰器设计模式采用这样的构建方式: 在主代码流中应该能够直接插入一个或多个更改或"装饰"目标对象的装饰器,同时不影响其他代码流. <?php        class CD {            public $trackList;                        

PHP设计模式之装饰者模式_php技巧

介绍 装饰者模式动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案. 思维导图   有这样一个项目,做一个餐厅订餐系统.起初的代码结构是这样的.前面有很多Beverage的继承类,现在遇到的问题是牛奶的价钱上涨了,那么所有相关的类,我们都要进行调整,比如Milk,SugarAndMilk类,这种类还有很多,我们需要逐个去修改类中的方法--开发人员每次都做这种事情,要疯了!所以我们要改变现有的结构.以下的图都是简图,实际的图,可没有这么简单.      设计问题: 1>类

学习JavaScript设计模式之装饰者模式_javascript技巧

有时我们不希望某个类天生就非常庞大,一次性包含许多职责.那么我们就可以使用装饰着模式. 装饰着模式可以动态地给某个对象添加一些额外的职责,从而不影响这个类中派生的其他对象. 装饰着模式将一个对象嵌入另一个对象之中,实际上相当于这个对象被另一个对象包装起来,形成一条包装链. 一.不改动原函数的情况下,给该函数添加些额外的功能 1. 保存原引用 window.onload = function() { console.log(1); }; var _onload = window.onload ||

设计模式:装饰者模式

设计模式系列目录 装饰者模式么,在生活中我们是经常接触的.比如像我们这么快节奏的生活,好多都是早上去买煎饼.一般我们会这么说:"来一个粗粮煎饼,加两个鸡蛋,加一根肠" 或者:"来个山东煎饼,只加土豆丝"等等."煎饼" 就是这个么个有弹性的对象,面饼是不变的,其它的像鸡蛋,肠什么的者在装饰面饼.这个也是我们编程时的一个设计原则 对扩展开放,对修改关闭.(在最后我会给出C# 和C++ 两种代码示例) 装饰者模式和"煎饼"差不多,它

.NET简谈设计模式之(装饰者模式)

装饰者模式其实有点难以理解,特别是对初学者来说可能有点晕,因为它的概念互相冲突,哪里互相冲突我们下面会讲解到. 本人保持一贯的写作风格,重在入门.在本人的这篇文章中会用一个比较恰当的比喻来让我们对问题迎刃而解,例子虽然简单但是重点突出. 在写这篇文章之前我在网上大概搜了一下关于"装饰者模式"的一些文章,但是讲解的效果都不太理想.要么就是找书搬过来的,要么就是对着书的例子从新创造一个.我看了大概三四篇这样子,不行看着头晕.文章的主人很想把问题的关键说清楚,但是很少能在原有代码的基础上画龙

设计模式学习--装饰者模式(Decorator Pattern)

概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有着相同的结构.这样一来,在运行的过程中,就可以将这些对象融合在一起,将相同的属性等成员有机的结合,就像生成另外一种类型一样,而实际上,我们并不需要真的创建这个类型,它是动态生成的.这些装饰者既可以组合,也可以撤销组合,既回复到原来对象状态. 示例介绍: 装饰者模式的关键就是装饰者类型的定义,而其中应