首页 > 其他分享 >装饰模式(Decorator)

装饰模式(Decorator)

时间:2023-10-18 16:55:21浏览次数:34  
标签:对象 Component 模式 public 组件 DateTime 装饰 Decorator

定义

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

装饰模式的结构和说明

  • Component:组件对象的接口,可以给这些对象动态地添加职责。
  • ConcreteComponent:具体的组件对象,实现组件对象接口,通常就是被装饰器装饰的原始对象,也就是可以给这个对象添加职责。
  • Decorator:所有装饰器的抽象父类,需要定义一个与组件接口一致的接口,并持有一个Component对象,其实就是持有一个被装饰的对象。
  • ConcreteDecorator:实际的装饰器对象,实现具体要向被装饰对象添加的功能。

示例代码

public abstract class Component
{
    /// <summary>
    /// 示例方法
    /// </summary>
    public abstract void Operation();
}

/// <summary>
/// 具体实现组件对象接口的功能
/// </summary>
public class ConcreteComponent : Component
{
    public override void Operation()
    {
        //相应的功能处理
    }
}

/// <summary>
/// 装饰器接口,维持一个指向组件对象的接口对象,并定义一个与组件接口一致的接口
/// </summary>
public abstract class Decorator : Component
{
    //持有组件对象
    protected Component _component;

    protected Decorator(Component component)
    {
        _component = component;
    }

    public override void Operation()
    {
        //转发请求给组件对象,可以在转发前后执行一些附加动作
        _component.Operation();
    }
}

/// <summary>
/// 装饰器的具体实现对象,向组件对象添加职责
/// </summary>
public class ConcreteDecoratorA : Decorator
{
    public ConcreteDecoratorA(Component component) : base(component)
    {
    }

    /*
     * 添加的状态
     */
    private string addedState;

    public string GetAddedState()
    {
        return addedState;
    }

    public override void Operation()
    {
        //调用父类的方法,可以再调用前后执行一些附件的动作
        //在这里进行处理的时候,可以使用添加的状态
        base.Operation();
    }
}

/// <summary>
/// 装饰器的具体实现对象,向组件对象添加职责
/// </summary>
public class ConcreteDecoratorB : Decorator
{
    public ConcreteDecoratorB(Component component) : base(component)
    {
    }

    /// <summary>
    /// 需要添加的职责
    /// </summary>
    private void AdderBehavior()
    {
        //需要添加的职责实现
    }

    public override void Operation()
    {
        //调用父方法,可以在调用前后执行一些附加动作
        base.Operation();
        AdderBehavior();
    }
}

重写示例代码

  • 需要定义一个组件对象的接口,在这个接口中定义计算奖金的业务方法,因为外部就是使用这个接口来操作装饰模式构成的对象结构中的对象。
  • 需要添加一个基本的实现组件接口的对象,可以让它返回奖金为0就可以了。
  • 把各个计算奖金的规则当作装饰器对象,需要为它们定义一个统一的抽象的装饰器对象,方便约束各个具体的装饰器的接口。
  • 把各个计算奖金的规则实现成为具体的装饰器对象。

代码

/// <summary>
/// 计算奖金的组件接口
/// </summary>
public abstract class Component
{
    /// <summary>
    /// 计算某人在某段时间内的奖金,有些参数在演示中并不会使用
    /// 但在实际业务实现上是会用的,为了标识这个具体的业务方法
    /// 因此这些参数被保留了
    /// </summary>
    /// <param name="user"></param>
    /// <param name="begin"></param>
    /// <param name="end"></param>
    /// <returns></returns>
    public abstract double CalcPrize(string user, DateTime begin, DateTime end);
}

/// <summary>
/// 基本的实现计算奖金的类,也是被装饰器装饰的对象
/// </summary>
public class ConcrereComponent : Component
{
    public override double CalcPrize(string user, DateTime begin, DateTime end)
    {
        //只是一个默认的实现,默认没有奖金
        return 0;
    }
}

/// <summary>
/// 装饰器的接口,需要和被装饰的对象实现同样的接口
/// </summary>
public abstract class Decorator : Component
{
    //持有被装饰的组件对象
    protected Component _c;

    protected Decorator(Component c)
    {
        _c = c;
    }

    public override double CalcPrize(string user, DateTime begin, DateTime end)
    {
        //转调组件对象的方法
        return _c.CalcPrize(user, begin, end);
    }
}

/// <summary>
/// 装饰器对象,计算当月业务奖金
/// </summary>
public class MonthPrizeDecorator : Decorator
{
    public MonthPrizeDecorator(Component c) : base(c)
    {
    }

    public override double CalcPrize(string user, DateTime begin, DateTime end)
    {
        //先获取前面运算出来的奖金
        var money = base.CalcPrize(user, begin, end);
        //然后计算当月业务奖金,按人员和时间去获取当月业务额,然后再乘以3%
        var prize = 10000 * 0.03; //10000先写死了,可以去数据库获取
        Console.WriteLine($"{user}当月业务奖金{prize}");
        return money + prize;
    }
}

public class SumPrizeDecorator : Decorator
{
    public SumPrizeDecorator(Component c) : base(c)
    {
    }

    public override double CalcPrize(string user, DateTime begin, DateTime end)
    {
        //先获取前面运算出来的奖金
        var money = base.CalcPrize(user, begin, end);
        //:然后计算累计奖金,其实应按人员去获取累计的业务额,然后再乘以01%
        //简单演示一下,假定大家的累计业务额都是1000000 元
        var prize = 1000000 * 0.001;
        Console.WriteLine($"{user}累计奖金{prize}");
        return money + prize;
    }
}

public class GroupPrizeDecorator : Decorator
{
    public GroupPrizeDecorator(Component c) : base(c)
    {
    }

    public override double CalcPrize(string user, DateTime begin, DateTime end)
    {
        //先获取前面运算出来的奖金
        var money = base.CalcPrize(user, begin, end);
        //然后计算当月团队业务奖金,先计算出团队总的业务额,然后再乘以1%
        //假设都是一个团队的
        var group = 35000; //写死了,可以数据库获取
        var prize = group * 0.01;
        Console.WriteLine($"{user}当月团队业务奖金{prize}");
        return money + prize;
    }
}

代码调用

static void Main(string[] args)
{
    //先创建计算基本奖金的类,这也是被装饰的对象
    var c1 = new Decorator1.ConcrereComponent();

    //然后对计算的基本奖金进行装饰,这里要组合各个装饰
    //说明,各个装饰者之间最好是不要有先后顺序的限制
    //也就是先装饰谁和后装饰谁都应该是一样的

    //先组合普通业务人员的奖金计算
    var d1 = new MonthPrizeDecorator(c1);
    var d2 = new SumPrizeDecorator(d1);

    //注意:这里只需使用最后组合好的对象调用业务方法即可,会依次调用回去
    //日期对象都没有用上,所以传nul1就可以了
    var zs = d2.CalcPrize("张三", DateTime.Now, DateTime.Now);
    Console.WriteLine($"======张三应得奖金:{zs}");

    //如果是业务经理,还需要一个计算团队的奖金计算
    var d3 = new GroupPrizeDecorator(d2);
    var ww = d3.CalcPrize("王五", DateTime.Now, DateTime.Now);
    Console.WriteLine($"=======王经理应得奖金:{ww}");

    Console.ReadKey();
}

运行结果

aa

模式讲解

认识装饰模式

装饰模式能够实现动态地为对象添加功能,是从一个对象外部来给对象增加功能,相当于是改变了对象的外观。当装饰过后,从外部使用系统的角度看,就不再是使用原始的那个对象了,而是使用被一系列的装饰器装饰过后的对象。

这样就能够灵活地改变一个对象的功能,只要动态组合的装饰器发生了改变,那么最终所得到的对象的功能也就发生了改变。

变相地还得到了另外一个好处,那就是装饰器功能的复用,可以给一个对象多次增加同一个装饰器,也可以用同一个装饰器装饰不同的对象。

对象组合

现在在面向对象的设计中,有一条基本的规则就是“尽量使用对象组合,而不是对象继承”来扩展和复用功能。装饰模式的思考起点就是这个规则。

延伸

另外一点,各个装饰器之间最好是完全独立的功能,不要有依赖,这样在进行装饰组合的时候,才没有先后顺序的限制,也就是先装饰谁和后装饰谁都应该是一样的,否则会大大降低装饰器组合的灵活性。

装饰器和组件类的关系

装饰器是用来装饰组件的,装饰器一定要实现和组件类一致的接口,保证它们是同个类型,并具有同一个外观,这样组合完成的装饰才能够递归调用下去。

组件类是不知道装饰器的存在的,装饰器为组件添加功能是一种透明的包装,组件类毫不知情。需要改变的是外部使用组件类的地方,现在需要使用包装后的类,接口是一样的,但是具体的实现类发生了改变。

装饰模式和AOP

什么是AOP——面向方面编程

AOP是一种编程范式,提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。

从某个侧面来说,装饰模式和 AOP 要实现的功能是类似的,只不过 AOP 的实现方法不同,会更加灵活,更加可配置,另外 AOP 一个更重要的变化是思想上的变化一-“主从换位”,让原本主动调用的功能模块变成了被动等待,甚至在毫不知情的情况下被织入了很多新的功能。

装饰模式优缺点

  • 比继承更灵活
  • 更容易复用功能
  • 简化高层定义
  • 装饰模式的缺点是:会产生很多细粒度对象。

思考装饰模式

装饰模式的本质:动态组合。

何时选用装饰模式

  • 如果需要在不影响其他对象的情况下,以动态、透明的方式给对象添加职责,可以使用装饰模式,这几乎就是装饰模式的主要功能。
  • 如果不适合使用子类来进行扩展的时候,可以考虑使用装饰模式。因为装饰模式是使用的“对象组合”的方式。所谓不适合用子类扩展的方式,比如,扩展功能需要的子类太多,造成子类数目呈爆炸性增长。

标签:对象,Component,模式,public,组件,DateTime,装饰,Decorator
From: https://www.cnblogs.com/huiteresa/p/17772801.html

相关文章

  • TSINGSEE风电场可视化智能视频集控监管系统,助力风电场无人值守监管新模式
    一、方案背景风能作为一种清洁的可再生能源,对于我国实现“双碳”目标尤为重要。风电场一般地处偏远地区,占地广、面积大,并且风机分布区域广泛、现场运行设备巡视难度大、及时性差。原有的监管系统智能化水平低,满足不了日常的生产安全、财产保障、运维管理等需求,因此,迫切需要构建一......
  • Java 设计模式之七大原则
    Java设计模式之七大原则|Drunkbaby'sBlog(drun1baby.top)1、认识设计模式1.1什么是设计模式所谓设计模式,就是对经常出现的软件设计问题的成熟解决方案。很多人把设计模式想象成非常高深的概念,实际上设计模式仅仅是对特定问题的一种惯性思维。笔者见过一些学员喜欢抱着一......
  • 设计模式之~策略模式
    策略模式是属于设计模式中的行为模式中的一种,策略模式主要解决选项过多的问题,避免大量的ifelse和switch下有太多的case。策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。1.创建抽象策略接口publicinterfaceRe......
  • 10月18日元类、单例模式
    目录1.元类复习1.什么是元类:2.所有类的元类是谁?3.如何自定义元类呢?4.__init__和__new__和__call__这三者的关系:2.单例模式1.元类复习1.什么是元类:因为一切皆对象这个思想,所以类也是对象,元类构造类,类是由元类实例化得到的2.所有类的元类是谁?是type,它是所有......
  • 突破传统投资模式:发掘现货多元化投资平台的优势
    在过去,投资往往被视为许多人眼中的高门槛领域。传统的股票、债券等金融产品常常需要大量资金和专业知识,限制了一般投资者的参与度。然而,随着科技的发展和互联网的普及,现货多元化投资平台逐渐崭露头角,为投资者提供了更加灵活和开放的投资方式。现货多元化投资平台是指通过互联网平......
  • 10月18日单例模式
    目录单例模式值类的绑定方法,装饰器方式设计模式:实现单例模式的第一种方式第一种方式以类的绑定方法来实现实现单例模式的第二种方式实现单例模式的第三种方式单例模式的核心概念是:只有一个实例对象,而不管有多少人尝试访问它。第四种方法,通过模块导入(python的模块就是单例的体现)......
  • 设计模式(八)组合
    一、定义组合多个对象形成树形结构以表示具有部分-整体关系的层次结构。组合模式让客户端可以统一对待单个对象和组合对象。组合模式是一种结构型模式。二、描述包含以下三个角色:1、Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含......
  • go语言使用单例模式封装数据库连接池
    packagesingledbimport( "gorm.io/driver/mysql" "gorm.io/gorm" "sync")//数据库连接对象只有一个var( db*gorm.DB Oncesync.Once//只执行一次某个操作的机制)funcGetDbInstance()*gorm.DB{ Once.Do(func(){ varerrerror dsn:=&q......
  • 职责链模式
    职责链模式案例引入OA(OfficeAutomation)系统的采购审批项目,需求是1.采购员采购教学用品。2.price>=0&&price<=5000由教学主任审批3.price>5000&&price<=10000由原则审批4.price>10000&&price<=30000由副校长审批5.price>30000由校长审批传统方式......
  • c#设计模式-行为型模式 之 备忘录模式
    ......