设计模式(六)之装饰器模式(Decorator Pattern)深入浅出

内容定位:有重构项目需求的人群一定要掌握装饰者模式。

装饰者模式的定义:

  • 装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)。
  • 属于结构型模式

装饰者模式的适用场景:

  • 用于扩展一个类的功能或给一个类添加附加职责。
  • 动态的给一个对象添加功能,这些功能可以再动态的撤销。

装饰者模式源码案例:

  • JDK源码中BuffteredReader、LineNumberReader装饰Reader抽象类;FileInputStream、FilterInputStream、ByteArrayInputStream、DataInputStream、BufferedInputStream装饰的是InputStream接口,实现可扩展

  •  Spring源码中TransactionAwareCacheDecorator装饰Cache接口,主要用来处理事务缓存

 

  •  SpringMvc源码中HttpHeadResponseDecorator装饰ServerHttpResponse接口,这个接口负责委派

 去父类中查看

  •  Mybatis源码中对org.apache.ibatis.cache.Cache接口进行包装

包装类有:

 

 生活场景案例

案例:我们在买煎饼时需要添加鸡蛋、香肠、菜....等等

(一)首先不用装饰者实现,通过继承实现一下

煎饼类

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:18
 * @description: 煎饼类
 */
public class Battercake {

    public String getMsg(){
        return "煎饼";
    }


    public int price(){
        return 5;
    }
}

煎饼加一个鸡蛋

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:20
 * @description: 煎饼加一个鸡蛋
 */
public class BattercakeWithEgg extends Battercake {

    @Override
    public String getMsg() {
        return super.getMsg() + " +1个鸡蛋";
    }

    /**
     * 加一个鸡蛋加2元
     */
    @Override
    public int price() {
        return super.price() + 2;
    }
}

煎饼加一个香肠

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:22
 * @description:
 */
public class BattercakeWithEggAndSausage extends BattercakeWithEgg {

    @Override
    public String getMsg() {
        return super.getMsg() + " +1个香肠";
    }

    /**
     * 1个香肠价格加2元
     */
    @Override
    public int price() {
        return super.price() + 2;
    }
}

测试

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:26
 * @description:
 */
public class BattercakeTest {

    public static void main(String[] args) {
        Battercake battercake = new Battercake();
        System.out.println(battercake.getMsg() + ",总价格:" + battercake.price());

        Battercake battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getMsg() + ",总价格:" + battercakeWithEgg.price());
        Battercake battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
        System.out.println(battercakeWithEggAndSausage.getMsg() + ",总价格:" + battercakeWithEggAndSausage.price());

    }
}

类结构图

我们会发现,如果需求频繁变动,比如加两个鸡蛋就需要不停的创建类,然后继承煎饼类。

(二)下面用装饰者模式实现

煎饼抽象类

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:44
 * @description: 煎饼抽象类
 */
public abstract class Battercake {

    protected abstract String getMsg();


    protected abstract int price();
}

基础煎饼类

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:45
 * @description: 基础煎饼(什么也不加)
 */
public class BaseBattercake extends Battercake {

    @Override
    public String getMsg(){
        return "煎饼";
    }

    @Override
    public int price(){
        return 5;
    }
}

装饰器抽象类(可有可无)

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:46
 * @description: 包装抽象类,继承煎饼抽象类,再怎么包装也是煎饼
 */
public abstract class BattercakeDecorator extends Battercake {

//    静态代理,委派模式:将修改的权力交给了装饰器
    private Battercake battercake;

    public BattercakeDecorator(Battercake battercake) {
        this.battercake = battercake;
    }

    @Override
    protected String getMsg() {
        return this.battercake.getMsg();
    }

    @Override
    protected int price() {
        return this.battercake.price();
    }
}

加鸡蛋的装饰器

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:50
 * @description: 鸡蛋包装类
 */
public class EggDecorator extends BattercakeDecorator {

    public EggDecorator(Battercake battercake) {
        super(battercake);
    }

    @Override
    protected String getMsg() {
        return super.getMsg() + " +1个鸡蛋";
    }

    @Override
    protected int price() {
        return super.price() + 2;
    }
}

加香肠的装饰器

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:52
 * @description:
 */
public class SausageDecorator extends BattercakeDecorator {

    public SausageDecorator(Battercake battercake) {
        super(battercake);
    }

    @Override
    public String getMsg() {
        return super.getMsg() + " +1个香肠";
    }

    /**
     * 1个香肠价格加2元
     */
    @Override
    public int price() {
        return super.price() + 2;
    }
}

测试用例

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 17:54
 * @description:
 */
public class BattercakeTest {

    public static void main(String[] args) {

        Battercake battercake;
//    路边买一个煎饼
        battercake = new BaseBattercake();
//        煎饼有点小,再想加个鸡蛋
        battercake = new EggDecorator(battercake);
//        再加一个鸡蛋
        battercake = new EggDecorator(battercake);
//        再加一跟香肠
        battercake = new SausageDecorator(battercake);

        System.out.println(battercake.getMsg() + "总价:" + battercake.price());

    }

}

运行结果

 类结构图

 对比继承可以看出,将添加的行为交给了装饰器去做,有点类似静态代理和委派模式

 简单来说:装饰器模式就是通过构造器入参,代替继承实现可扩展的一种方式

业务场景案例

案例:系统需要在原有的登录方式上,添加QQ、微信等多种登录方式(这里拿适配器模式讲解的业务场景案例: 连接地址

 登录注册接口

public interface ISiginService {

    /**
     * 注册方法
     */
    ResultMsg regist(String username,String password);

    /**
     * 登录方法
     */
    ResultMsg login(String username,String password);
}

返回对象类

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 11:55
 * @description:
 */
public class ResultMsg {

    private int code;
    private String msg;
    private String data;

    public ResultMsg(int code, String msg, String data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}

登录注册实现类

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 18:40
 * @description: 登录注册实现
 */
public class SiginService implements ISiginService {

        /**
         * 注册方法
         */
        @Override
        public ResultMsg regist(String username,String password){
            return new ResultMsg(200,"注册成功",null);
        }

        /**
         * 登录方法
         */
        @Override
        public ResultMsg login(String username,String password){
            return new ResultMsg(200,"登录成功",null);
        }

}

扩展三方登录的接口

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 18:43
 * @description: 三方登录接口
 */
public interface ISiginForThirdService extends ISiginService {
    /**
     * QQ登录
     */
    ResultMsg loginForQQ(String openId);

    /**
     * 微信登录
     */
    ResultMsg loginForWechat(String openId);

    /**
     * 新浪登录
     */
    ResultMsg loginForSina(String openId);

    /**
     * token登录
     */
    ResultMsg loginForToken(String token);

    /**
     * 手机验证登录
     */
    ResultMsg loginForTelphone(String telphone,String code);

    /**
     * 注册账号后自动登录
     */
    ResultMsg loginForRegister(String username,String passport);


}

三方登录包装器

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/12 18:46
 * @description: 三方登录服务 包装器
 */
public class SiginForThirdService implements ISiginForThirdService {

    private ISiginService siginService;

    public SiginForThirdService(ISiginService siginService) {
        this.siginService = siginService;
    }

    @Override
    public ResultMsg loginForQQ(String openId) {
        return null;
    }

    @Override
    public ResultMsg loginForWechat(String openId) {
        return null;
    }

    @Override
    public ResultMsg loginForSina(String openId) {
        return null;
    }

    @Override
    public ResultMsg loginForToken(String token) {
        return null;
    }

    @Override
    public ResultMsg loginForTelphone(String telphone, String code) {
        return null;
    }

    @Override
    public ResultMsg loginForRegister(String username, String passport) {
        return null;
    }

    @Override
    public ResultMsg regist(String username, String password) {
        return this.siginService.regist(username,password);
    }

    @Override
    public ResultMsg login(String username, String password) {
        return this.siginService.login(username,password);
    }
}

测试用例

/**
 * @author: ZhouCong
 * @date: Create in 2021/1/13 10:34
 * @description:
 */
public class DecoratorTest {

    public static void main(String[] args) {
        SiginForThirdService siginForThirdService = new SiginForThirdService(new SiginService());
        siginForThirdService.loginForQQ("fdfdfasd");
    }
}

类结构图

 

 

 总结

模式对比:

  装饰器模式 适配器模式
形式 是一种非常特别的适配器模式 没有层级关系,装饰器模式有层级关系
定义 装饰者和被装饰者都实现同一个接口,主要目的是为了扩展之后依旧保留OOP关系 适配者和被适配者没有必然的联系,通常是采用继承或代理的形式进行包装
关系 满足is-a关系 满足has-a关系
功能 注重覆盖、扩展 注重兼容、转换
设计 前置考虑,架构设计之初考虑 后置考虑,亡羊补牢

 装饰器模式的优点:

  • 装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
  • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
  • 装饰者完全遵守开闭原则。

装饰器模式的缺点:

  • 会出现更多的代码,更多的类,增加程序复杂性。
  • 动态装饰时,多层装饰时会更复杂。

以上对装饰器模式的介绍到此结束,欢迎批评指正。 附:源码地址

posted @ 2021-01-13 17:10  IT学无止境99  阅读(184)  评论(0编辑  收藏  举报