设计模式之——decorator模式

博客王大锤 博客王大锤     2022-09-23     390

关键词:

Decorator模式又叫装饰者模式,这种模式是为了满足Java开发的“面向扩展开放,面向修改闭源”的开发原则设计出来的。

在装饰者模式中,不修改源类的代码,却能修改源类中方法的功能。下面就以Angelababy化妆为例,详细介绍一下为什么需要装饰者模式,以及装饰者模式怎么实现:

  • 先介绍angelababy类
package site.wangxin520.gof.decorator;

/**
 * 装饰者模式
 * baby化妆类
 * @author wangXgnaw
 *
 */
public class Angelababy {

    /**
     * 画眉毛
     */
    public void meimao(){
        System.out.println("baby自己画眉毛");
    }
    
    /**
     * 画眼影
     */
    public void yanying(){
        System.out.println("baby自己画眼影");
    }
    
    /**
     * 点腮红
     */
    public void saihong(){
        System.out.println("baby自己画腮红");
    }
    
    /**
     * 画口红
     */
    public void kouhong(){
        System.out.println("baby自己涂口红");
    }
    
}

在baby类里面,有四个方法,分别对应着baby自己给自己化妆的步骤。

  • 装饰者模式测试类,首先是baby给自己化妆
package site.wangxin520.gof.decorator;

/**
 * 装饰者模式的测试类
 * @author wangXgnaw
 *
 */
public class Test {

    public static void main(String[] args) {
        
        Angelababy ab =new Angelababy();
        
        //baby给自己化妆
        ab.meimao();
        ab.yanying();
        ab.saihong();
        ab.kouhong();
        
    }
    
}
  • 结果是:

image

由此可见,baby自己给自己化妆成功。

虽然这样自己是能给自己化妆的,不过怎么也没有专业的化妆师给自己化妆的效果好。所以baby找来了一个专业的化妆师A。

  • DresserA装饰者模式的的第一种实现,继承
package site.wangxin520.gof.decorator;

/**
 * 装饰者模式的装饰者
 * 化妆师A
 * @author wangXgnaw
 *
 */
public class DresserA extends Angelababy{

    /**
     * 重写了画眼影的方法,在画眼影之后,又加上了刷睫毛
     */
    @Override
    public void yanying() {
        super.yanying();
        //后面输出有误,注意
        System.out.println("给anglebaby刷睫毛");
    }

    /**
     * 重写了涂口红的方法
     * 在涂口红之前,给她涂了桃红色红
     */
    @Override
    public void kouhong() {
        System.out.println("给anglebaby涂了桃红色的口红");
        super.kouhong();
    }

    
    
}
  • 测试类
package site.wangxin520.gof.decorator;

/**
 * 装饰者模式的测试类
 * @author wangXgnaw
 *
 */
public class Test {

    public static void main(String[] args) {
        
        //化妆师a给baby化妆
        Angelababy ab=new DresserA();
        
        //anglebaby化妆
        ab.meimao();
        ab.yanying();
        ab.saihong();
        ab.kouhong();
        
    }
    
}
  • 结果:

image

由此可见,baby的眼影和口红都不是自己画的了,而是别人帮她画的。这就是第一种继承的方法。不过这种方法是有局限性的,如果anglebaby类是final修饰的不可继承的话,那就尴尬了。另外,化妆师实际上是不能去继承被化妆的那个人的,如果化妆师继承了anglebaby而不是小海绵继承的话,黄晓明可能会哭晕在厕所!

下面就介绍第二种方法:面向接口开发

首先提取出一个公共接口,化妆的interface接口

  • MakeUp 化妆的接口
package site.wangxin520.gof.decorator;

/**
 * 化妆的接口
 * @author wangXgnaw
 *
 */
public interface MakeUp {

    /**
     * 画眉毛
     */
    public void meimao();
    
    /**
     * 画眼影
     */
    public void yanying();
    
    /**
     * 点腮红
     */
    public void saihong();
    /**
     * 画口红
     */
    public void kouhong();
    
}
  • 让anglebaby实现该接口
package site.wangxin520.gof.decorator;

/**
 * 装饰者模式
 * baby化妆类
 * @author wangXgnaw
 *
 */
public class Angelababy implements MakeUp{

    /**
     * 画眉毛
     */
    @Override
    public void meimao(){
        System.out.println("baby自己画眉毛");
    }
    
    /**
     * 画眼影
     */
    @Override
    public void yanying(){
        System.out.println("baby自己画眼影");
    }
    
    /**
     * 点腮红
     */
    @Override
    public void saihong(){
        System.out.println("baby自己画腮红");
    }
    
    /**
     * 画口红
     */
    @Override
    public void kouhong(){
        System.out.println("baby自己涂口红");
    }
    
}
  • DresserB 化妆师b,同样也实现这个接口。不过在构造化妆师B的时候,得把anglebaby传进来
package site.wangxin520.gof.decorator;

/**
 * 化妆师B
 * @author wangXgnaw
 *
 */
public class DresserB implements MakeUp{

    //用于保存化妆师B的顾客
    private MakeUp makeUp=null;
    
    /**
     * 在构造化妆师B的类的时候,传入顾客
     * @param makeUp
     */
    public DresserB(MakeUp makeUp) {
        this.makeUp=makeUp;        
    }
    
    /**
     * 实现化妆的类,当调用顾客对应的类的时候,实现的还是顾客的功能
     * 当需要增加新功能时候,只需要在对应的方法前面或者后面实现即可
     */
    @Override
    public void meimao() {
        makeUp.meimao();
    }

    
    /**
     * 化妆师B觉得涂眼影不好看,所以不涂了
     * 只给baby贴了双眼皮贴
     */
    @Override
    public void yanying() {
        System.out.println("贴双眼皮贴");
    }

    /**
     * 化妆师B想在baby涂腮红之前,就加上摸粉底
     */
    @Override
    public void saihong() {
        System.out.println("摸粉底");
        makeUp.saihong();
    }

    @Override
    public void kouhong() {
        makeUp.kouhong();
    }

}
  • 测试类
package site.wangxin520.gof.decorator;

/**
 * 装饰者模式的测试类
 * @author wangXgnaw
 *
 */
public class Test {

    public static void main(String[] args) {
        
        //化妆师B给baby化妆
        
        Angelababy ab=new Angelababy();
        
        DresserB db=new DresserB(ab); 
        
        db.kouhong();
        db.yanying();
        db.saihong();
        db.meimao();
        
    }
    
}
  • 结果为:

image

由此可见,装饰者模式生效了。

不过面向接口的这种装饰者模式也有一个小问题,那就是必须重写接口的实现类,并且在实现类里面调用传入参数的所有方法。

当接口中抽象方法很多的时候,这就很麻烦。

为了解决这种麻烦,就提出了一种新的设计模式,即当需要使用哪种方法的时候,就修饰哪种方法,否则就不改变。这种新的模式就是动态代理模式。后面我们将详细介绍!

设计模式之——builder建造模式

Builder模式又叫建造模式,是用于组装具有复杂结构的实例的模式。示例程序是编写一个文档,并且写入到文件中,该文档具有以下结构,含有标题,字符串,一些条目。Builder抽象类,为建造模式的核心packagesite.wangxin520.gof.builder... 查看详情

设计模式之——浅谈strategy模式(策略模式)

strategy模式,即策略模式。个人觉得吧,策略模式更多的是一种思维方式。首先我们要知道,为什么需要策略模式。举个例子,比如用程序输出今天下午去玩什么。PlayGame玩游戏packagesite.wangxin520.gof.strategy.demo;/***玩游戏的类*@author... 查看详情

设计模式之——迭代器模式

设计模式是开发者前辈们给我们后背的一个经验总结。有效的使用设计模式,能够帮助我们编写可复用的类。所谓“可复用”,就是指将类实现为一个组件,当一个组件发生改变时,不需要对其他组件进行修改,或者只需要修改... 查看详情

设计模式之——template模板模式

Template模式又叫模板模式,是在父类中定义处理流程的框架,在子类中实现具体处理逻辑的模式。当父类的模板方法被调用时程序行为也会不同,但是,不论子类的具体实现如何,处理的流程都会按照父类中所定义的那样进行。... 查看详情

设计模式:命令模式——命令模式扩展之日志请求(代码片段)

前言命令模式的讲解分为四篇:设计模式(一):命令模式(1)——基本的命令模式设计模式(一):命令模式(2)——命令模式扩展之宏命令设计模式(一):命令模式(3)——命令模式扩展之队列请求设... 查看详情

设计模式:命令模式——命令模式扩展之队列请求(代码片段)

前言命令模式的讲解分为四篇:设计模式(一):命令模式(1)——基本的命令模式设计模式(一):命令模式(2)——命令模式扩展之宏命令设计模式(一):命令模式(3)——命令模式扩展之队列请求设... 查看详情

文本处理三剑客之sed——高级编辑命令

本篇介绍sed的高级编辑命令高级编辑命令P:打印模式空间开端至 内容,并追加到默认输出之前n:读取匹配到的行的下一行覆盖至模式空间N:读取匹配到的行的下一行追加至模式空间h:把模式空间中的内容覆盖至保持空间中H:把... 查看详情

文本处理三剑客之sed——一般编辑命令

...作原理1.sed命令开始执行后2.先从文本中读取第一行,放在模式空间(patternspace)中3.判断这行文本是否符合指定模式,符合则进行编辑,然后把结果输出到标准输出 查看详情

设计模式:命令模式——命令模式扩展之宏命令(代码片段)

前言命令模式的讲解分为四篇:设计模式(一):命令模式(1)——基本的命令模式设计模式(一):命令模式(2)——命令模式扩展之宏命令设计模式(一):命令模式(3)——命令模式扩展之队列请求设... 查看详情

二分图之匈牙利算法

(⊙o⊙)…——————————————————————————&mdas 查看详情

创建型设计模式之原型模式(代码片段)

...式的原型模式与单例模式是密不可分的,这也是最常用的设计模式之一。原型模式是一种非常简单的设计模式。这里除了基本介绍和演示,还详细介绍了Java中原型模式的本质。一、介绍  同样,先来看一下《研磨设计模式》... 查看详情

设计模式之---装饰器设计模式(代码片段)

职责:动态的为一个对象增加新的功能   装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。实现细节:&md... 查看详情

博客目录

【第一篇】python学习——基础篇之基本数据类型(一)【第二篇】python学习——基础篇之基本数据类型(二)【第三篇】python学习——基础篇之文件操作【第四篇】python学习——基础篇之常用模块【第五篇】pyt... 查看详情

安装卸载gnome

...defaultgraphical.targetsudoreboot如果你想要开机默认进入命令行模式:echo"execgnome-session">>~/.xinitrc   1之后你可以通过startx命令选择启动gnome如何卸载GNOMEsudoyumgroupremove"GNOMEDesktop"   1如果你在~/目录下面创建了.xi... 查看详情

matlab——程序设计(代码片段)

M文件 我们之前所做的运算————>算式不太长,或想以交谈式方式进行运算如果算式很长或是需要一再执行的算式————>采用M文件的方式【将指令及算式写成巨集程式然后储存成一个特别的... 查看详情

javaweb之tomcat

1. tomcat下载、解压2.tomcat解压后目录  bin——存放可执行文件,startup.bat,shutdown.bat  conf——配置文件server.xml  lib——tomcat依赖的jar包文件  log——日志文件  temp——临时文件   查看详情

javascript构造函数模式原型模式

...继承,打算总结一下原型继承的,不过今天看了一下工厂模式、构造函数模式和原型模式,觉得有必要总结一下以加深印象。———————————————————&m... 查看详情

mvvm设计思想

view ——————————————> viewModel —————————&mda 查看详情