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

简介: 一、定义 装饰者模式,英文叫Decorator Pattern,在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案。

一、定义

装饰者模式,英文叫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设计模式之目录

 

相关文章
|
14天前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
30 2
|
19天前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
|
28天前
|
设计模式 Java Kotlin
Kotlin - 改良设计模式 - 装饰者模式
Kotlin - 改良设计模式 - 装饰者模式
28 4
|
1月前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
|
2月前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
42 0
|
2月前
|
设计模式 Java Kotlin
Kotlin 学习笔记- 改良设计模式 - 装饰者模式
Kotlin 学习笔记- 改良设计模式 - 装饰者模式
27 0
|
2月前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
23 0
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
1月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###