设计模式

装饰者模式

​ 在不改变原有对象的基础之上,把功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)。

​ 属于结构型

适用场景

  • 扩展一个类的功能或给一个类添加附加职责

  • 动态的给一个对象添加功能,这些功能可以再动态的撤销

案例-购买煎饼

比如说我们购买煎饼,有时候需要加鸡蛋,有时候需要鸡蛋和火腿

原有设计

原有设计是这样的

基础煎饼类

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

    public int getPrice() {
        return 5;
    }
}

煎饼加鸡蛋

public class BattercakeWithEgg extends Battercake {
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1个鸡蛋";
    }

    @Override
    public int getPrice() {
        return super.getPrice() + 1;
    }
}

煎饼加鸡蛋和香肠

public class BattercakeWithEggAndSausage extends BattercakeWithEgg {
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1根香肠";
    }

    @Override
    public int getPrice() {
        return super.getPrice() + 2;
    }
}

测试类

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

        Battercake battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getMsg() + ",总价格" + battercakeWithEgg.getPrice());
    }

}

这种方式我们每次需要新购买一个产品都需要新增一个类

改良

使用装饰着模式

定义抽象类

public abstract class Battercake {
    protected abstract String getMsg();

    protected abstract int getPrice();

}

提供默认煎饼类

public class BaseBattercake extends Battercake {


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

    @Override
    protected int getPrice() {
        return 5;
    }
}

提供煎饼包装类

public class BattercakeDecorator extends Battercake {
    private Battercake battercake;

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

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

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

}

鸡蛋包装类

public class EggDecorator extends BattercakeDecorator {

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

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

    @Override
    protected int getPrice() {
        return super.getPrice() + 1;
    }
}

火腿包装类

public class SausageDecorator extends BattercakeDecorator {

    public SausageDecorator(Battercake battercake) {
        super(battercake);
    }
    @Override
    protected String getMsg() {
        return super.getMsg() + "加一个火腿";
    }

    @Override
    protected int getPrice() {
        return super.getPrice() + 5;
    }
}

测试

public class App {
    public static void main(String[] args) {
        Battercake battercake;
        battercake = new BaseBattercake();

        battercake = new EggDecorator(battercake);
        System.out.println(battercake.getMsg());
        System.out.println(battercake.getPrice());

        battercake = new EggDecorator(battercake);
        System.out.println(battercake.getMsg());
        System.out.println(battercake.getPrice());

        battercake = new SausageDecorator(battercake);
        System.out.println(battercake.getMsg());
        System.out.println(battercake.getPrice());
    }

}

这种方式每次新增都不需要新创类

类图

最初的类图

image-20190402103735596

现在的类图

image-20190402103710054

装饰器包装一定要满足is-a的关系

案例-巨魔

​ 在英雄联盟中有一个英雄-巨魔,他能攻击别人,也能吸取别人攻击力,还能够逃跑。不同的敌人攻击力不一样,所以可以使用装饰者模式来实现。

首先定义巨魔的接口

public interface Troll {

  void attack();

  int getAttackPower();

  void fleeBattle();

}

巨魔初始攻击力,和操作

public class SimpleTroll implements Troll {

  private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTroll.class);

  @Override
  public void attack() {
    LOGGER.info("The troll tries to grab you!");
  }

  @Override
  public int getAttackPower() {
    return 10;
  }

  @Override
  public void fleeBattle() {
    LOGGER.info("The troll shrieks in horror and runs away!");
  }
}

巨魔偷攻击力

public class ClubbedTroll implements Troll {

  private static final Logger LOGGER = LoggerFactory.getLogger(ClubbedTroll.class);

  private Troll decorated;

  public ClubbedTroll(Troll decorated) {
    this.decorated = decorated;
  }

  @Override
  public void attack() {
    decorated.attack();
    LOGGER.info("The troll swings at you with a club!");
  }

  @Override
  public int getAttackPower() {
    return decorated.getAttackPower() + 10;
  }

  @Override
  public void fleeBattle() {
    decorated.fleeBattle();
  }
}

测试

public class App {

  private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

  /**
   * Program entry point
   * 
   * @param args command line args
   */
  public static void main(String[] args) {

    // simple troll
    LOGGER.info("A simple looking troll approaches.");
    Troll troll = new SimpleTroll();
    troll.attack();
    troll.fleeBattle();
    LOGGER.info("Simple troll power {}.\n", troll.getAttackPower());

    // change the behavior of the simple troll by adding a decorator
    LOGGER.info("A troll with huge club surprises you.");
    Troll clubbedTroll = new ClubbedTroll(troll);
    clubbedTroll.attack();
    clubbedTroll.fleeBattle();
    LOGGER.info("Clubbed troll power {}.\n", clubbedTroll.getAttackPower());
  }
}

优点

  • 是继承的有利补充,比继承灵活,不改变原有对象的情况下动态的给一个对象扩展功能,即插即用

  • 通过不同装饰类以及这些装饰类的排列组合,可以实现不同的效果。

  • 装饰者完全遵循开闭原则

缺点

  • 会出现更多的代码,增加程序的复杂性

  • 动态装饰时,多层装饰会更复杂

对比

装饰者模式
适配器模式

形式

是一种非常特别的适配器模式

没有层级关系,装饰器模式有层级关系

定义

装饰者和被装饰者都实现同一个接 口,主要目的是为了扩展之后依旧保 留 OOP 关系

适配器和被适配者没有必然的联系,通 常是采用继承或代理的形式进行包装

关系

满足 is-a 的关系

满足 has-a 的关系

功能

注重覆盖、扩展

注重兼容、转换

设计

前置考虑

后置考虑

源码中的应用

其在源码中应用的非常多,JDk中最明显的类就是IO中的BufferedReader,InputStream,OutputStream,看一下常用的InputStream结构图

image-20190403095607124
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }

可以看到fileterinputstream的构造方法中传入了inputstream

在Spring 中的TransactionAwareCacheDecorator

image-20190403191538314

观察者模式

应用场景

​ 观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化是,他的所有依赖者(观察者)都会受到通知并且更新 ,属于行为型模式。

​ 观察者模式有时也叫做发布订阅模式。观察者模式主要用于在关联行为之间建立一套触发机制的场景。

​ 生活中比如说朋友圈动态通知,邮件通知,广播通知,桌面程序等。

优点

  • 观察者和呗观察者之间建立了一个抽象的耦合

  • 观察者模式支持广播通信

缺点

  • 观察者之间有过多的细节依赖,提高实践消耗以及程序的复杂度

  • 使用要得当,避免循环调用

设计模式对比

分类
设计模式

创建型

工厂方法,抽象工厂,建造者,原型模式,单例模式

结构型

适配器,桥接,组合,装饰者,门面模式,享元,代理

行为型

解释器,末班方法,责任链,命令,迭代器,调节者,备忘录,观察者,状态,策略,访问

关联关系和对比

单例和工厂

​ 工厂类一般设计为单例

​ ApplicationContext

装饰者和静态代理

​ 装饰者关注点在于给对象动态添加方法,但是代理更加注重对对象的访问

​ 代理模式通常在代理类中创建被代理对象的实例,而装饰者模式通常吧被装饰者作为构造参数

策略和模板

​ 都封装了算法

​ 策略模式是使不同算法可以相互替换,不影响客户端应用层的使用

​ 模板方法是针对定义一个算法的流程,将一些有细微差异的部分交给子类来实现。

模板和原型

代理和委派

代理和策略

策略和工厂

​ 工厂模式主要是封装好创建逻辑,策略模式接受工厂创建好的对象,从而实现不同的行为

策略和委派

​ 策略模式是委派模式内部的一宗实现,策略模式关注的结果是否是能否相互替代

​ 策略:

​ 委派:侧重关注和分发调度的过程,有可能采用if,else的条件来分发,内部也可以使用策略模式

代理和适配器

装饰器和适配器

模板方法和工厂方法

​ 工厂方法是模板方法的一种特殊实现

适配器和策略

​ 策略模式优化适配器

Spring中常用的设计模式

设计模式
一句话归纳
举例

工厂模式

只对结果负责,封装创建过程

BeanFactory,Calender,Logger

单例模式

保证独一无二

ApplicationContext,Calender

原型模式

拔一根猴毛,吹出千万个

ArrayList,PrototypeBean

代理模式

找人办事,增强职责

ProxyFactoryBean,JdkDynamicAopProxy,CglibAopProxy

委派模式

干活算你的(普通员工),功劳算我的

DispatcherServlet,BeanDefinitionParserDelegate

策略模式

用户选择,结果统一

InstantiationStrategy,HandlerMapping

模板模式

流程标准化,自己实现定制

JDBCTemplate,httpServlet

适配器模式

兼容转换头

AdvisorAdapter,HandlerAdapter

装饰者模式

包装,同宗同源

BufferedReader,InputStream,OutputStream,HttpHeadResponseDecorator,beanWrapper

观察者模式

任务完成时通知

ContextLoaderLitener

Last updated