设计模式之装饰器模式


1.定义

装饰器模式属于结构型模式的一种,目的是为了在不破坏原有对象结构的情况下,对原有对象的功能进行扩展增强,添加新的职责。表面上看与代理模式有一点像,但实际上面向应用场景不一样,具体会在后边的代理模式中说明。
谈到对对象功能进行增强,使用语言的继承特性也可以实现,即通过创建子类对父类原有功能进行增强,但是继承存在一定的耦合性,并且随着功能增加,子类的数量也会随之增加。而使用装饰器模式虽然也需要创建新的装饰器类来对原始目标类进行增强,但是从扩展方向上来看,继承是垂直扩展,而装饰器模式是在水平方向扩展,目标类和装饰类可以各自独立发展,不会相互耦合。
从表现上看,继承在垂直方向上通过向下延伸子类来获取扩展的机会,而装饰器模式是在水平方向上与原始类实现相同接口,他们是平等的,因此他们具备相同行为,通过在装饰类中持有原始类引用,进而可以做到在装饰类中获得对目标类的扩展机会。

当然要举个栗子:既然是装饰器模式,就离不开装饰,就以照片和相框为例,王大爷有很多的照片,艺术照、写真照、家庭照等等,一开始只有照片,王大爷觉得这么beautiful的照片只有光秃秃的照片不合适,想要给他们装上相框,比如红色的相框,白色的相框等等,而安装相框这个步骤就是对原来的照片增强,需要注意的是增强之后它们依然是照片,只是多了一个相框而已,它的本质属性并没有发生变化。

装饰器模式中有几个角色:

  • 抽象构件(Component):抽象类或者接口,也就是原始目标类的抽象描述,类比照片
  • 具体构件(ConcreteComponent):对抽象构件的具体实现,比如上边的艺术照,写真照等
  • 抽象装饰角色(Decorator):装饰器的抽象,描述了对原始目标类的增强的抽象,它应持有原始目标的对象引用,类比带相框的照片
  • 具体装饰角色(ConcreteDecorator):具体的装饰器对象,描述了对原始目标对象增强的具体,类比带白色/红色相框的照片

贴一个UML:
在这里插入图片描述
图片来源:知乎


2.示例

以王大爷的相册为例,简单通过代码说明装饰器模式,首先就是最顶层的抽象目标接口Photo

/**
 * @description: 照片
 * @version: 1.0
 */
public interface Photo {

    void getPhoto();
}

对照片的各种不同实现,这里以艺术照和写真照为例

/**
 * @description: 艺术照
 * @version: 1.0
 */
public class ArtPhoto implements Photo{
    @Override
    public void getPhoto() {
        System.out.println("Art Photo ....");
    }
}
/**
 * @description: 写真照
 * @version: 1.0
 */
public class RealisticPhoto implements Photo{
    @Override
    public void getPhoto() {
        System.out.println("Realistic Photo ...");
    }
}

王大爷对光秃秃的照片不满意,准备对他们进行装饰,于是想到了相框,到这一步王大爷只是确定了需要相框,但是还不知道具体搭配那种颜色风格的,所以这还是一个抽象的层面。
关键点也还在这里,这里装饰器同样实现了原始目标类的接口,它本质也是照片,是带有装饰相框的照片

/**
 * @description: 装饰器(相框)抽象 
 * @version: 1.0
 */
public  abstract class PhotoDecorator implements Photo {

    protected Photo photo;

    public PhotoDecorator(Photo photo){
        this.photo = photo;
    }

    @Override
    public void getPhoto() {
        photo.getPhoto();
    }
}

接来下王大爷就要选择具体的相框样式了,看到红色,他果断的选择了红色,并表示不再回头,于是最后就有了带有红色相框的照片,也就是通过红色相框对照片进行装饰。

/**
 * @description: 装饰器具体类,带有红色相框的照片
 * @version: 1.0
 */
public class PhotoRedStyleFrameDecorator extends PhotoDecorator{


    public PhotoRedStyleFrameDecorator(Photo photo) {
        super(photo);
    }

    @Override
    public void getPhoto() {
        super.getPhoto();
        this.setFrame(photo);
    }

    private void setFrame(Photo photo){
        System.out.println("Set Frame To Photo ...");
    }
}

测试:

/**
 * @description: test
 * @version: 1.0
 */
public class Test {
    public static void main(String[] args) {
        System.out.println("===============Decorate frame before==============");
        Photo artPhoto = new ArtPhoto();
        Photo realisticPhoto = new RealisticPhoto();

        artPhoto.getPhoto();
        realisticPhoto.getPhoto();

        //对照片进行增强装饰
        System.out.println("================Decorate frame after==============");
        Photo artPhotoWithFrame = new PhotoRedStyleFrameDecorator(new ArtPhoto());
        Photo realisticPhotoWithFrame = new PhotoRedStyleFrameDecorator(new ArtPhoto());

        artPhotoWithFrame.getPhoto();
        realisticPhotoWithFrame.getPhoto();
    }
}

input:

===========================Decorate frame before============================
Art Photo ....
Realistic Photo ...
===========================Decorate frame after============================
Art Photo ....
Set Frame To Photo ...
Art Photo ....
Set Frame To Photo ...
3.总结

装饰器的优点就是更为灵活,耦合性低,符合开闭原则,能够在不影响原有对象的情况下实现横向扩展,缺点自然就是在装饰比较多的情况下,会出现多层的包装,增加程序的复杂性,装饰器应用场景有很多,比如最经典的JavaIO各种流对象,如果没记错的话,HttpServletRequest/Response也采用了装饰器模式。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值