设计模式学习笔记(十二)装饰模式

概念

装饰模式是一种结构型设计模式,它允许在不改变现有对象结构的前提下,动态地将新行为添加到对象中。装饰模式通过将对象包装在装饰器类的实例中,来扩展其功能。
在这里插入图片描述
抽象组件(Component):定义了被装饰对象和装饰对象之间的共同接口,可以是抽象类或接口。

具体组件(ConcreteComponent):实现了抽象组件的接口,并且是被装饰的目标对象,即原始对象。

抽象装饰器(Decorator):继承自抽象组件,具有一个与抽象组件一致的接口,同时持有一个抽象组件的引用。其内部通常会包含一个构造方法,用于接收被装饰的对象。

具体装饰器(ConcreteDecorator):继承自抽象装饰器,负责给具体组件添加额外的功能。它在调用原始对象的操作之前或之后,执行自己的附加操作,从而扩展了原始对象的功能。

装饰模式的核心思想是利用组合而非继承来实现功能的扩展。通过将对象包装在装饰器类中,可以在运行时动态地给对象添加新的行为,而无需修改原始对象的结构或代码。这种灵活性使得装饰模式在需要动态添加功能、避免类膨胀以及保持代码可扩展性的场景下非常有用。

总结而言,装饰模式允许通过将对象包装在装饰器类中来动态地添加新功能,而不需要修改原始对象的结构。它提供了一种灵活、可扩展的方式来实现功能的组合,同时遵循开闭原则,使得系统更加灵活和可维护。

示例

我们可以在绘制的图形上进行装饰,比如填充、描边等方法,下面就是结构图及代码示例
在这里插入图片描述

//抽象界面构件类:抽象构件类
abstract class Component {
    public  abstract void display();
}

//圆形类:具体构件类
class CircleShape extends Component {
       @Override
       public  void display() {
              System.out.println("绘制圆形!");
       }
}

//矩形类:具体构件类
class RectangleShape extends Component {
       @Override
       public  void display() {
              System.out.println("绘制矩形!");
       }
}

//三角形:具体构件类
class TriangleShape extends Component {
       @Override
       public  void display() {
              System.out.println("绘制三角形形!");
       }

}

//构件装饰类:抽象装饰类
class ComponentDecorator extends Component {
       private Component component;  //维持对抽象构件类型对象的引用
       public ComponentDecorator(Component  component) {  //注入抽象构件类型的对象
              this.component = component;
       }
       @Override
       public void display() {
              component.display();
       }

}

//填充颜色装饰类:具体装饰类
class FillColorDecorator extends  ComponentDecorator {
       public FillColorDecorator(Component  component) {
              super(component);
       }
       @Override
       public void display() {
              super.display();
              this.setFillColor();
       }
       public  void setFillColor() {
              System.out.println("为构件填充颜色!");
       }

}

//黑色描边饰类:具体装饰类
class BlackBorderDecorator extends  ComponentDecorator
{
       public BlackBorderDecorator(Component  component) {
              super(component);
       }
       @Override
       public void display() {
              super.display();
              this.setBlackBorder();
       }
       public  void setBlackBorder() {
              System.out.println("为构件增加黑色描边!");
       }
}

客户端

Component component,componentFC;  //使用抽象构件定义
        component = new CircleShape(); //定义具体构件
        componentFC = new  FillColorDecorator(component); //定义装饰后的构件
        componentFC.display();

输出结果

绘制圆形!
为构件填充颜色!

二次装饰

Component component,componentFC,componentDB;  //使用抽象构件定义
        component = new CircleShape(); //定义具体圆形构件component
        componentFC = new  FillColorDecorator(component); //将圆形构件component装入FillColorDecorator进行装饰
        componentDB = new  BlackBorderDecorator(componentFC); //将装饰完的部件再次装入BlackBorderDecorator进行装饰
        componentDB.display();

运行结果

绘制圆形!
为构件填充颜色!
为构件增加黑色描边!

二次装饰解释:
首先理解第一次装饰component和componentFC都属于Component类,component定义的是Component的子类CircleShape,而componentFC定义的是Component的孙子类FillColorDecorator,而且在FillColorDecorator类构造器可以传入一个Component变量并为其赋值,因此new FillColorDecorator(component)的时候可以将此实例构造,并将内部的Component变量赋值,因为FillColorDecorator是Component的孙子辈,所以依然可以赋值给componentFC,至此,第一次装饰我们理解了。
第二次装饰BlackBorderDecorator类内部同样也是有Component变量,同样也是构造器为其赋值,因此传入的componentFC赋值给BlackBorderDecorator内部的Component,并创建变量componentDB,因此我们调用componentDB的display时,是这样的:componentDB的display包含componentFC的display,和本身的setBlackBorder;componentFC的display又包含着component的display和setFillColor;因此首先打印了component的display,然后是setFillColor,最后是setBlackBorder。
图示:
在这里插入图片描述

透明装饰模式与半透明装饰模式

透明装饰模式和半透明装饰模式是装饰模式的两种变体,它们在对被装饰对象的接口暴露方面有所不同:

透明装饰模式(Transparent Decorator Pattern):在透明装饰模式中,装饰器类和被装饰对象实现相同的接口。这意味着客户端可以将装饰器视为被装饰对象的类型来使用,而无需关心装饰器和被装饰对象之间的差异。透明装饰模式要求装饰器具有和被装饰对象相同的接口,以便能够替代被装饰对象。这种方式使得装饰器和被装饰对象对客户端来说是透明的。

半透明装饰模式(Semi-Transparent Decorator Pattern):在半透明装饰模式中,装饰器类和被装饰对象之间存在差异,装饰器类通常会引入新的方法或属性。客户端在使用装饰器时,需要明确知道自己是在使用装饰器,而不是原始的被装饰对象。客户端只能通过特定的方式来使用装饰器,不能将其视为被装饰对象的类型来使用。

透明装饰模式

上述我们提到的例子则为透明装饰模式,component,componentFC,componentDB都定义为Component,想要实现这样的效果,装饰类与被装饰类都要覆写display方法。

半透明装饰模式

有时候我们的装饰类比较特殊,比如有一个装饰类有一个setText方法,而这个方法并没有出现在Component类中,因此在客户端使用时,就不能将此装饰类定义为Component,而是要单独定义
比如:

//填充颜色装饰类:具体装饰类
class FillColorDecorator extends  ComponentDecorator {
       public FillColorDecorator(Component  component) {
              super(component);
       }
       @Override
       public void display() {
              super.display();
              this.setFillColor();
       }
       public  void setFillColor() {
              System.out.println("为构件填充颜色!");
       }
       //此方法没有被display调用,但是我们还想使用此方法
       public  void setText() {
              System.out.println("为构件输入文本!");
       }
}

客户端

		Component component = new CircleShape(); //定义具体圆形构件component;使用抽象构件定义
		FillColorDecorator componentFC = new  FillColorDecorator(component); //将圆形构件component装入FillColorDecorator进行装饰;单独为修饰类定义
        componentFC.display();//调用原有业务方法
        componentFC.setText();//调用新增业务方法

注意:半透明装饰模式无法进行二次装饰,因此,如果某个装饰类有特殊的方法,尽量在装饰抽象类中声明扩展,如果只有一个具体构件类,那么抽象装饰类可以作为该具体构件类的直接子类。因此我们应该尽量使用透明装饰模式

总结

优点:
1、动态地扩展对象的功能:可以在不修改原始对象的情况下,动态地给对象添加新的行为和功能。
2、遵循开闭原则:通过对原始对象进行包装,实现了对扩展开放、对修改关闭的原则。可以轻松地新增装饰器类,而不需要修改已有的代码,提高了代码的可维护性和可扩展性。
3、符合单一职责原则:装饰模式将功能划分到不同的装饰器类中,每个装饰器类只关注自己的特定功能,使得类的设计更加清晰,提高了代码的可读性和可理解性。
4、避免使用子类的过多继承:通过组合多个装饰器对象来扩展功能,避免了使用过多的子类继承。相比于使用继承创建子类,装饰模式更加灵活,可以根据需要动态地组合装饰器。
5、保持代码整洁性:装饰模式使得每个类只关注自己的核心功能,而把其他功能交给相应的装饰器来实现。这种解耦可以使得代码更加清晰、易于理解和维护。

缺点:
1、可能引入过多的对象:随着功能的扩展,可能会引入大量的装饰器对象,导致对象数量过多,复杂度增加。
2、容易出错:如果装饰器的使用不当,可能会导致装饰器的顺序错误或者产生意想不到的结果。

适用场景:
1、需要动态地给对象添加功能:当需要在不修改原始对象的情况下,动态地给对象添加额外的行为或功能时,可以考虑使用装饰模式。
2、避免使用过多的子类继承:当使用继承会导致类层次结构爆炸性增长的情况下,可以考虑使用装饰模式来替代继承,以实现更灵活的功能扩展。
3、需要对对象的功能进行动态组合:当需要根据不同的需求动态地组合对象的功能时,可以使用装饰模式,通过组合不同的装饰器对象实现灵活的功能组合。

总而言之,装饰模式适用于需要动态地扩展对象功能、避免过度继承以及动态组合对象功能的场景。它可以提供一种灵活、可维护和可扩展的设计方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值