C#设计模式-装饰器模式(Decorator Pattern)

引言

当我们完成一个软件产品开发后就需要对其进行各种测试,适配快速迭代下质量的保障。当有一个完善的产品的对象后,如果我们想要给他添加一个测试功能,那么我们可以用一个新的类去装饰它来实现对原有对象职责的扩展。新的类称为“装饰者”,原有的对象称为“被装饰者”。这种模式被称为装饰器模式。

概念

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

结构图

装饰器模式中的角色:

  • 抽象构件(Component)角色:声明封装器和被封装对象的公用接口。即给出一个抽象接口,已规范准备接收附加责任的对象。
  • 具体构件(ConcreteComponent)角色:类是被封装对象所属的类。 它定义了基础行为, 但装饰类可以改变这些行为。
  • 装饰(Decorator)角色:拥有一个指向被封装对象的引用成员变量。 该变量的类型应当被声明为通用部件接口, 这样它就可以引用具体的部件和装饰。 装饰基类会将所有操作委派给被封装的对象。
  • 具体装饰(ConcreteDecorator)角色:定义了可动态添加到部件的额外行为。 具体装饰类会重写装饰基类的方法, 并在调用父类方法之前或之后进行额外的行为。负责给构件对象“贴上”附加的责任。

实现

实现一个开发完成后的产品,对其进行手工功能测试、自动化测试、压力测试。将产品作为被装饰者,也就是构件。各种测试作为装饰者,计算附加不同的测试花费的总时间。

实现思路:

  • 定义一个产品抽象类。
  • 实现具体的产品,具体的产品继承产品抽象类。
  • 定义一个测试类型的抽象装饰类,继承产品抽象类。
  • 实现不同类型的测试,继承测试类型的抽象装饰类。
  • 使用时实例化一个产品,然后对产品进行附件不同的测试类型。
using System;

namespace Decorator
{
    class Program
    {
        static void Main(string[] args)
        {
            Product a = new ProductA();
            Console.WriteLine($"未执行{a.Test}测试时总共花费时间{a.TotalTime}");
            a = new ManualTest(a);
            Console.WriteLine($"执行{a.Test}总共花费时间{a.TotalTime}");
            Console.WriteLine("=====================================");

            Product b = new ProductB();
            b = new ManualTest(b);
            b = new StressTest(b);
            b = new AutoTest(b);
            Console.WriteLine($"执行{b.Test}总共花费时间{b.TotalTime}");

            Console.Read();
        }
    }

    /// <summary>
    /// 一个项目产品,抽象构件
    /// </summary>
    public abstract class Product
    {
        public string Name { get; set; }
        public double SpendTime { get; set; }
        public abstract string Test { get; }
        public abstract double TotalTime { get; }
    }

    /// <summary>
    /// 具体的项目产品A,具体构件
    /// </summary>
    public class ProductA : Product
    {
        public ProductA()
        {
            Name = "ProductA";
            SpendTime = 0;
        }

        public override string Test => this.Name;
        public override double TotalTime => this.SpendTime;
    }

    /// <summary>
    /// 具体的项目产品B,具体构件
    /// </summary>
    public class ProductB : Product
    {
        public ProductB()
        {
            Name = "ProductB";
            SpendTime = 0;
        }

        public override string Test => this.Name;
        public override double TotalTime => this.SpendTime;
    }

    /// <summary>
    /// 测试类型,抽象装饰
    /// </summary>
    public abstract class TestType : Product
    {
        Product _product = null;

        public TestType(Product product)
        {
            _product = product;
        }

        public override string Test
        {
            get
            {
                return _product.Test + "+" + this.Name;
            }
        }

        public override double TotalTime
        {
            get
            {
                return _product.TotalTime + this.SpendTime;
            }
        }
    }

    /// <summary>
    /// 手工测试类型,具体装饰
    /// </summary>
    public class ManualTest : TestType
    {

        public ManualTest(Product product) : base(product)
        {
            Name = "手工测试";
            SpendTime = 200;
        }
    }

    /// <summary>
    /// 自动化测试类型,具体装饰
    /// </summary>
    public class AutoTest : TestType
    {

        public AutoTest(Product product) : base(product)
        {
            Name = "自动化测试";
            SpendTime = 100;
        }

    }

    /// <summary>
    /// 压力测试类型,具体装饰
    /// </summary>
    public class StressTest : TestType
    {
        public StressTest(Product product) : base(product)
        {
            Name = "压力测试";
            SpendTime = 200;
        }
    }

}

运行后结果:

未执行ProductA测试时总共花费时间0
执行ProductA+手工测试总共花费时间200
=====================================
执行ProductB+手工测试+压力测试+自动化测试总共花费时间500

应用场景

  • 在无需修改代码的情况下即可使用对象, 且希望在运行时为对象新增额外的行为时可以使用装饰模式。因为装饰能将业务逻辑组织为层次结构,可为各层创建一个装饰, 在运行时将各种不同逻辑组合成对象。 由于这些对象都遵循通用接口, 客户端代码能以相同的方式使用这些对象。
  • 如果用继承来扩展对象行为的方案难以实现或者根本不可行,可以使用装饰模式。

优缺点

优点

  • 无需创建新子类即可扩展对象的行为。
  • 可以在运行时添加或删除对象的功能。
  • 可以用多个装饰封装对象来组合几种行为。
  • 装饰类和被装饰类可以独立发展,不会相互耦合。
  • 单一职责原则。 可以将实现了许多不同行为的一个大类拆分为多个较小的类。

缺点

  • 在封装器栈中删除特定封装器比较困难。
  • 实现行为不受装饰栈顺序影响的装饰比较困难。
  • 各层的初始化配置代码看上去可能会很糟糕。
(0)

相关推荐

  • 设计模式:装饰者模式

    装饰者模式 概述 我们先来看一个快餐店的例子. 快餐店有炒面.炒饭这些快餐,可以额外附加鸡蛋.火腿.培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦. 使 ...

  • 技术图文:03 结构型设计模式(下)

    结构型设计模式(下) 本教程主要介绍一系列用于如何将现有类或对象组合在一起形成更加强大结构的经验总结. 知识结构: 图1 知识结构 组合模式 -- 树形结构的处理 Sunny 软件公司欲开发一个杀毒( ...

  • 设计模式-结构型-装饰者模式

    装饰者模式(wrapper): 允许向一个现有的对象添加新的功能,同时又不改变其结构.装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能.使用对象的关联关系代替继承关系,更加 ...

  • 详解JAVA面向对象的设计模式 (七)、装饰模式

    装饰模式 Decorator 装饰模式比较简单,我就不单独写实现例子了.参考设计图去实现不是什么问题.建议可以写一写找找感觉. 在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修.相 ...

  • DercoratorPattern装饰者模式

    装饰者模式 1,定义 动态的给一个对象添加一些额外的职责. 装饰者模式通常有4个角色,就增加功能来说,装饰者模式比生成子类更加的灵活. Component:抽象构件.通常是一个接口或者抽象类,定义最核 ...

  • 设计模式-装饰器模式

    装饰器模式 定义 装饰器模式也叫包装模式 在不改变原有对象的基础上,把功能附加到对象上,提供了比继承更有弹性的替代方案 能够扩展原有对象的功能 属于结构型模式 生活中的例子 买煎饼 我们煎饼可以加鸡蛋 ...

  • PHP设计模式—装饰器模式

    定义: 装饰器模式(Decorator):动态的给一个对象添加一些额外的职责,就增加功能来说,装饰器比生成子类更加灵活. 结构: Component:定义一个对象接口,可以给这些对象动态地添加职责. ...

  • PHP设计模式之装饰器模式

    PHP设计模式之装饰器模式 工厂模式告一段落,我们来研究其他一些模式.不知道各位大佬有没有尝试过女装?据说女装大佬程序员很多哟.其实,今天的装饰器模式就和化妆这件事很像.相信如果有程序媛MM在的话,马 ...

  • [PHP小课堂]PHP设计模式之装饰器模式

    [PHP小课堂]PHP设计模式之装饰器模式 关注公众号:[硬核项目经理]获取最新文章 添加微信/QQ好友:[DarkMatterZyCoder/149844827]免费得PHP.项目管理学习资料

  • java设计模式之装饰器模式

    装饰器模式的定义: 装饰器模式也叫作包装器模式,指在不改变原有对象的基础上,动态地给一个对象添加一些额外的职责.就增加功能来说,装饰器模式相比生成子类更为灵活,属于结构性设计模式. 装饰器模式提供了比 ...

  • 设计模式之装饰器模式

    设计模式之装饰器模式

  • 3年工作必备 装饰器模式

    回复"000"获取大量电子书 大家好,我是老田,从今天开始,本公众号每周给大家送福利,送什么呢?肯定是技术书啦,没那么多花里胡哨的,参与方式见文末. 好啦,进入我们的主题,今天我给 ...

  • 结构型设计模式 - 装饰者模式详解

    基本定义 装饰者模式属于结构型模式,它可以动态的将新功能附加到对象上,同时又不改变其结构.在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(OCP). 模式结构 装饰者和被装饰者有相同 ...