设计模式06-门面模式与装饰器模式

1.6门面模式与装饰器模式详解

1.6.1.门面模式详解

时长:39min

内容预演:

》模式的特征和应用场景

》装饰器模式与代理模式的区别

》门面模式的优缺点

》装饰器模式的优缺点

1.6.1.1.什么是门面模式?

定义:

  Facade Pattern,又称外观模式。提供一个统一的接口,用来访问子系统中的一群接口。

属于结构型设计模式。

特征:

  门面模式定义一个高层接口,让子系统更容易使用。

1.门面模式的适用场景

  子系统越来越复杂,增加门面模式提供简单接口。

  构建多层系统结构,利用门面对象作为每层的入口,简化层间调用

2.生活中门面模式运用

  前台接待员【前台---为公司做导向】

  包工头

1.6.1.2.门面模式的实现

1.通用实现

  这里以测试驱动开发【TDD】的方式进行开发。

【1】定义测试类
package com.wf.facade.general;

/**
 * @ClassName FacadeTest
 * @Description 测试
 * @Author wf
 * @Date 2020/5/21 14:45
 * @Version 1.0
 */
public class FacadeTest {
    public static void main(String[] args) {
        //客户端调用,门面的实例
        Facade facade = new Facade();
        facade.doWorkA();
        facade.doWorkB();
        facade.doWorkC();
    }
}
[2]创建门面类Facade

 根据报错,开发子系统服务。具体代码实现如下:

package com.wf.facade.general;

/**
 * @ClassName Facade
 * @Description 门面类,这里只是简单调用子系统服务
 * @Author wf
 * @Date 2020/5/21 14:46
 * @Version 1.0
 */
public class Facade {
    private SubSystemA a = new SubSystemA();
    private SubSystemB b = new SubSystemB();
    private SubSystemC c = new SubSystemC();

    public void doWorkA() {
        this.a.doWorkA();
    }

    public void doWorkB() {
        this.b.doWorkB();
    }

    public void doWorkC() {
        this.c.doWorkC();
    }
}
[3]定义子系统服务

A.子系统A

package com.wf.facade.general;

/**
 * @ClassName SubSystemA
 * @Description 子系统服务
 * @Author wf
 * @Date 2020/5/21 14:57
 * @Version 1.0
 */
public class SubSystemA {
    public void doWorkA() {
        System.out.println("子系统A提供服务");
    }
}

 2.子系统服务B

package com.wf.facade.general;

/**
 * @ClassName SubSystemB
 * @Description 子系统服务
 * @Author wf
 * @Date 2020/5/21 14:58
 * @Version 1.0
 */
public class SubSystemB {
    public void doWorkB() {
        System.out.println("子系统B提供服务");
    }
}

3.子系统服务C 

package com.wf.facade.general;

/**
 * @ClassName SubSystemC
 * @Description 子系统服务
 * @Author wf
 * @Date 2020/5/21 14:58
 * @Version 1.0
 */
public class SubSystemC {
    public void doWorkC() {
        System.out.println("子系统C提供服务");
    }
}
 【4】.测试结果

说明:

  门面模式,只是把所有的工作都交给门面类来统一处理,客户端只和一个人进行交接,简化客户端的使用

  我们在web开发中,Controller就是一种门面模式的应用。

 【5】系统类图分析

2.实际场景实现

  有一个社区商城,它不是直接通过付费进行购买礼品。而是通过积分兑换,当对一个礼品有兴趣时,通常点击商品图片,

进入商品详情页,商品下边提供立即兑换功能。当点击兑换时,弹出表单,获取用户信息【自己填写表单】,如:姓名,电话,邮箱,备注,

然后确定兑换。会判断你的积分是否足够,如果积分足够,就发起一个支付动作,否则无法兑换。

  

  支付完成后,进入物流信息记录。发货时间,订单号。。。

  这样一个购物流程,如果全部由前端来控制,需要调用大量的接口,前端工作量会很大,调用会很复杂,那么,该怎么办呢?

为了提升开发效率,减轻前端开发工作量,后端可以统一封装一个接口,控制整个流程。

系统设计:

  分析业务bean【礼品GiftInfo】

  系统构成:支付系统PaymentService----》库存系统QualifyService【校验积分是否足够】-----》物流系统ShippingService

[1]业务bean定义
package com.wf.facade.points;

/**
 * @ClassName GiftInfo
 * @Description 业务类,礼品信息
 * @Author wf
 * @Date 2020/5/21 15:26
 * @Version 1.0
 */
public class GiftInfo {
    private String name;

    public GiftInfo(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
【2】库存系统
package com.wf.facade.points;

/**
 * @ClassName QualifyService
 * @Description 库存系统
 * @Author wf
 * @Date 2020/5/21 15:29
 * @Version 1.0
 */
public class QualifyService {
    public boolean isAvailable(GiftInfo giftInfo){
        System.out.println("校验 " + giftInfo.getName()+" 积分足够,库存足够,允许兑换");
        return true;
    }
}
【3】支付系统 
package com.wf.facade.points;

/**
 * @ClassName PaymentService
 * @Description 支付系统
 * @Author wf
 * @Date 2020/5/21 15:34
 * @Version 1.0
 */
public class PaymentService {
    public boolean pay(GiftInfo giftInfo){
        System.out.println("扣减 "+giftInfo.getName() + " 积分成功");
        return true;
    }
}
 【4】物流系统
package com.wf.facade.points;

/**
 * @ClassName ShippingService
 * @Description 物流系统
 * @Author wf
 * @Date 2020/5/21 15:32
 * @Version 1.0
 */
public class ShippingService {
    public String delivery(GiftInfo giftInfo){
        System.out.println(giftInfo.getName() + " 进入物流系统");
        String shippingNO= "666";
        return shippingNO;
    }
}
【5】模拟客户端调用
package com.wf.facade.points;

/**
 * @ClassName Test
 * @Description 测试,模拟前端调用
 * @Author wf
 * @Date 2020/5/21 15:42
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        QualifyService qualifyService = new QualifyService();
        PaymentService paymentService = new PaymentService();
        ShippingService shippingService = new ShippingService();

        //判断积分是否足够
        GiftInfo giftInfo = new GiftInfo("《spring5 核心原理解析》");
        if(qualifyService.isAvailable(giftInfo)){
            if(paymentService.pay(giftInfo)){
                String shippingNO = shippingService.delivery(giftInfo);
                System.out.println("物流系统下单成功,物流单号是:"+shippingNO);
            }
        }
    }
}
 测试结果如下:

说明:

  整个系统功能开发完成,但是客户端调用复杂,为简化调用,使用门面模式,增加门面类:

【5】增加门面类
package com.wf.facade.points;

/**
 * @ClassName FacadeService
 * @Description 门面类
 * @Author wf
 * @Date 2020/5/21 17:47
 * @Version 1.0
 */
public class FacadeService {
    private QualifyService qualifyService = new QualifyService();
    private PaymentService paymentService = new PaymentService();
    private ShippingService shippingService = new ShippingService();

    public void exchange(GiftInfo giftInfo){
        //判断积分是否足够
        if(qualifyService.isAvailable(giftInfo)) {
            if (paymentService.pay(giftInfo)) {
                String shippingNO = shippingService.delivery(giftInfo);
                System.out.println("物流系统下单成功,物流单号是:" + shippingNO);
            }
        }
    }
}
【6】修改测试类
 public static void main(String[] args) {
        FacadeService facadeService = new FacadeService();
        //判断积分是否足够
        GiftInfo giftInfo = new GiftInfo("《spring5 核心原理解析》");
       facadeService.exchange(giftInfo);
    }
说明:
  运行结果一致,客户端代码调用,变得简单。
  这就是门面模式,我们的代码中基本每天都在使用它。
【7】系统类图

1.6.1.3.门面模式在源码中运用

Mybatis中JdbcUtils【典型的门面模式】

Mybatis中Configration大量使用门面模式。

Tomcat源码中RequestFacade,ResponseFacade,StandardSessionFacade.

1.6.1.4.门面模式的总结

1.门面模式与代理模式的关系

  门面模式,就是一种特殊的静态代理。

  门面模式的重点,是在于封装。静态代理的重点是功能增强。不做增强的静态代理就是门面模式。

  委派模式,也是一种静态代理,代理属于结构型模式。而委派【行为型模式】,不属于GOF 23之一。

2.门面模式与单例模式的关系

  门面类,很多时候做成单例类,如工具类。

  

3.门面模式的优点

  》简化客户端调用,调用者无需关注子系统,以防给子系统带来风险。

  》减少系统依赖,松散耦合。

  》更好地划分访问层次,提高安全性

  》遵循迪米特法则,即最少知道原则。

  》符合组合原则

4.门面模式的缺点

  》当增加子系统和扩展子系统行为时,可能带来未知风险。

  》不符合开闭原则

  》某些情况下可能违背单一职责原则。导致门面类职责较重。

1.6.2.装饰器模式详解

时长:55min

1.6.2.1.装饰器模式的定义

官方定义

  Decorator pattern,装饰器模式。也叫包装器模式(Wrapper Pattern),是指在不改变原有对象的基础上,将功能附加到对象上,

提供了比继承更有弹性的替代方案(扩展原有对象的功能

  属于结构型模式。

1.生活中的装饰器模式:

  煎饼【可以加各种辅助材料,本质不变】

  蛋糕。

2.适用场景

》用于扩展一个类的功能或给一个类添加附加职责。

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

1.6.2.2.装饰器模式的代码实现

1.通用实现

【1】顶层功能组件

  可以定义为接口或抽象类。这里定义为抽象类。

package com.wf.decorator.general;

/**
 * @ClassName Component
 * @Description 顶层抽象功能组件
 * @Author wf
 * @Date 2020/5/21 19:12
 * @Version 1.0
 */
public abstract class Component {
    public abstract void operation();
}

【2】组件的原生实现类

package com.wf.decorator.general;

/**
 * @ClassName ConcreteComponent
 * @Description 组件原生具体实现
 * @Author wf
 * @Date 2020/5/21 19:14
 * @Version 1.0
 */
public class ConcreteComponent extends Component {
    @Override
    public void operation() {
        System.out.println("处理具体的业务逻辑");
    }
}

【3】装饰器抽象接口

package com.wf.decorator.general;

/**
 * @ClassName Decorator
 * @Description 装饰器抽象接口
 * @Author wf
 * @Date 2020/5/21 19:16
 * @Version 1.0
 */
public abstract class Decorator extends Component{
    //持有组件引用
    protected  Component component;

    public Decorator(Component component){
        this.component = component;
    }

    @Override
    public void operation() {
        //转发请求给组件对象,可以在转发前后执行一些附加动作【增强组件】
        component.operation();
    }
}

  下面可以实现不同的装饰器:

package com.wf.decorator.general;

/**
 * @ClassName ConcreteDecoratorA
 * @Description 装饰器实现子类A
 * @Author wf
 * @Date 2020/5/21 19:21
 * @Version 1.0
 */
public class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        //此前添加功能
        operationFirst();
        super.operation();
        operationLast();
    }

    private void operationLast() {
        System.out.println("装饰器A 在调用父类方法之后执行");
    }

    private void operationFirst() {
        System.out.println("装饰器A 在调用父类方法之前执行");
    }
}
package com.wf.decorator.general;

/**
 * @ClassName ConcreteDecoratorB
 * @Description 装饰器实现子类B
 * @Author wf
 * @Date 2020/5/21 19:21
 * @Version 1.0
 */
public class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        //此前添加功能
        operationFirst();
        super.operation();
        operationLast();
    }

    private void operationLast() {
        System.out.println("装饰器B 在调用父类方法之后执行");
    }

    private void operationFirst() {
        System.out.println("装饰器B 在调用父类方法之前执行");
    }
}

【4】客户端调用

package com.wf.decorator.general;

/**
 * @ClassName Client
 * @Description 客户端调用
 * @Author wf
 * @Date 2020/5/21 19:24
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Decorator decoratorA = new ConcreteDecoratorA(component);
        decoratorA.operation();
        System.out.println("----------------------------------");
        Decorator decoratorB = new ConcreteDecoratorB(component);
        decoratorB.operation();
        System.out.println("----------------------------------");
        Decorator decoratorBandA = new ConcreteDecoratorB(decoratorA);
        decoratorBandA.operation();
    }
}

 执行结果如下:

 说明:

  客户端通过,构建器封装,子类调用父类的方法,进行功能增强。

【5】系统类图

这个示例,是装饰者模式的通用写法,因为没有针对具体需求,进行编码。所以,理解起来,还是有些抽象

下面使用“煎饼”这个生活实例,来说明,先使用普通编码,然后再使用装饰器模式改写。对比两种实现:

2.煎饼的普通实现
【1】业务bean,煎饼类
package com.wf.decorator.battercake.v1;

/**
 * @ClassName Battercake
 * @Description 业务bean,煎饼类
 * @Author wf
 * @Date 2020/5/22 10:18
 * @Version 1.0
 */
public class Battercake {
    protected String getMsg(){
        return "这是一个煎饼";
    }

    public int getPrice(){
        return 5;
    }
}
【2】编写测试类
package com.wf.decorator.battercake.v1;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/5/22 10:20
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        Battercake battercake = new Battercake();
        System.out.println(battercake.getMsg()+",总价:"+battercake.getPrice()+"元钱");
    }
}

执行结果如下:

说明:

  这里只是简单测试下,普通煎饼的功能。下面要对煎饼进行加料,使用继承来扩展。

【3】加蛋煎饼
package com.wf.decorator.battercake.v1;

/**
 * @ClassName BattercakeWithEgg
 * @Description 加蛋煎饼
 * @Author wf
 * @Date 2020/5/22 10:26
 * @Version 1.0
 */
public class BattercakeWithEgg extends Battercake {
    @Override
    protected String getMsg() {
        return super.getMsg() +",外加一个鸡蛋";
    }

    @Override
    public int getPrice() {
        return super.getPrice() + 1;
    }
}
 【4】修改测试类
package com.wf.decorator.battercake.v1;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/5/22 10:20
 * @Version 1.0
 */
public class Test {
    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()+"元钱");
    }
}

测试结果如下:

说明:

  通过继承结构,功能得到增强。如果想再加料,加蛋基础上,再加一根香肠。又得需要深层继承。

[5]加蛋加香肠煎饼
package com.wf.decorator.battercake.v1;

/**
 * @ClassName BattercakeWithEggAndSausage
 * @Description 加蛋加香肠煎饼
 * @Author wf
 * @Date 2020/5/22 10:33
 * @Version 1.0
 */
public class BattercakeWithEggAndSausage extends BattercakeWithEgg {
    @Override
    protected String getMsg() {
        return super.getMsg()+",再加一根香肠";
    }

    @Override
    public int getPrice() {
        return super.getPrice()+2;
    }
}
【6】再次修改测试类
package com.wf.decorator.battercake.v1;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/5/22 10:20
 * @Version 1.0
 */
public class Test {
    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()+"元钱");

        Battercake battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
        System.out.println(battercakeWithEggAndSausage.getMsg()+",总价:"+battercakeWithEggAndSausage.getPrice()+"元钱");
    }
}
 运行结果如下:

 说明:  

  如果想再加料,如:加青菜,加肉,不是得再次深层继承,造成这种继承结构复杂,层次太深。

  显然,使用继承处理这种需求,是不合适的,这会造成类大量生成,顶层类一直得不到回收。

  

  前辈们,针对这种需求,总结出装饰器模式,下面使用装饰器模式改写系统。

3.装饰器模式改写煎饼系统
【1】抽象化顶层组件

  系统中,煎饼类,需要抽象成抽象组件,方法也抽象化。如下所示:

package com.wf.decorator.battercake.v2;

/**
 * @ClassName AbstractBattercake
 * @Description 煎饼类,抽象顶层组件
 * @Author wf
 * @Date 2020/5/22 10:18
 * @Version 1.0
 */
public abstract class AbstractBattercake {
    protected abstract String getMsg();

    public abstract int getPrice();
}

【2】具体化抽象组件

  提供一个基本的功能。

package com.wf.decorator.battercake.v2;

/**
 * @ClassName BaseBattercake
 * @Description 具体化煎饼功能
 * @Author wf
 * @Date 2020/5/22 10:50
 * @Version 1.0
 */
public class BaseBattercake extends AbstractBattercake {
    @Override
    protected String getMsg() {
        return "这是一个煎饼";
    }

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

【3】提供一个装饰器的抽象接口

package com.wf.decorator.battercake.v2;

/**
 * @ClassName BattercakeDecorator
 * @Description 顶层装饰器
 * @Author wf
 * @Date 2020/5/22 10:53
 * @Version 1.0
 */
public abstract class BattercakeDecorator extends AbstractBattercake {
    private AbstractBattercake battercake;

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

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

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

说明:

  这个顶层装饰器很重要,必须要组合引用抽象组件。通过构造器封装。

  实现方法,调用父类方法。

【4】创建装饰器子类实现

1.加蛋煎饼装饰器

package com.wf.decorator.battercake.v2;

/**
 * @ClassName EggDecorator
 * @Description 加蛋装饰器
 * @Author wf
 * @Date 2020/5/22 10:59
 * @Version 1.0
 */
public class EggDecorator extends BattercakeDecorator {
    public EggDecorator(AbstractBattercake battercake) {
        super(battercake);
    }

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

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

2.加香肠装饰器

package com.wf.decorator.battercake.v2;

/**
 * @ClassName SausageDecorator
 * @Description 加香肠装饰器
 * @Author wf
 * @Date 2020/5/22 11:11
 * @Version 1.0
 */
public class SausageDecorator extends BattercakeDecorator{
    public SausageDecorator(AbstractBattercake battercake) {
        super(battercake);
    }

    @Override
    protected String getMsg() {
        return super.getMsg()+",再加一根香肠";
    }

    @Override
    public int getPrice() {
        return super.getPrice() + 2;
    }
}
【5】客户端代码
package com.wf.decorator.battercake.v2;
/**
 * @ClassName Client
 * @Description 客户端调用
 * @Author wf
 * @Date 2020/5/22 11:16
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        AbstractBattercake battercake = new BaseBattercake();
        System.out.println(battercake.getMsg() +",总价是:"+battercake.getPrice());

        //加鸡蛋
        battercake = new EggDecorator(battercake);
        System.out.println(battercake.getMsg() +",总价是:"+battercake.getPrice());

        //加2个鸡蛋
        battercake = new EggDecorator(battercake);
        System.out.println(battercake.getMsg() +",总价是:"+battercake.getPrice());

        //加香肠
        battercake = new SausageDecorator(battercake);
        System.out.println(battercake.getMsg() +",总价是:"+battercake.getPrice());
    }
}

测试结果:

 说明:

  装饰器可以实现,动态增强的作用。

4.项目实战需求应用

  假设开发了一个系统,系统各方面功能都不错。使用log4j和sl4j做日志处理,但是打印的字符串,太难解析。

希望日志输出格式,进行一个调整,输出json格式

  但是,不希望去修改原来的代码,如果使用继承的话,会改变原有代码的功能。

  所以,选择使用装饰器模式,处理日志输出格式调整需求。

【1】.引入maven依赖
【2】测试类打印日志
package com.wf.decorator.logger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/5/22 11:35
 * @Version 1.0
 */
public class Test {
    private static final Logger logger = LoggerFactory.getLogger(Test.class);

    public static void main(String[] args) {
        logger.error("系统错误");
    }
}

测试结果如下:

说明:

  控制台成功。打印日志。是以字符串的形式,不方便解析。希望打印成json格式。

  根据装饰器模式,需要定义顶层抽象接口,日志处理框架是Logger就是这个顶层抽象接口。下面就是定义具体的Logger实现。

【3】定义Logger日志处理装饰器
package com.wf.decorator.logger;

import org.slf4j.Logger;
import org.slf4j.Marker;

/**
 * @ClassName LoggerDecorator
 * @Description 日志装饰器抽象接口
 * @Author wf
 * @Date 2020/5/22 12:48
 * @Version 1.0
 */
public class LoggerDecorator implements Logger {
    protected Logger logger;

    public LoggerDecorator(Logger logger) {
        this.logger = logger;
    }

    @Override
    public String getName() {
        return null;
    }

    @Override
    public boolean isTraceEnabled() {
        return false;
    }

    @Override
    public void trace(String s) {

    }

    @Override
    public void trace(String s, Object o) {

    }

    @Override
    public void trace(String s, Object o, Object o1) {

    }

    @Override
    public void trace(String s, Object... objects) {

    }

    @Override
    public void trace(String s, Throwable throwable) {

    }

    @Override
    public boolean isTraceEnabled(Marker marker) {
        return false;
    }

    @Override
    public void trace(Marker marker, String s) {

    }

    @Override
    public void trace(Marker marker, String s, Object o) {

    }

    @Override
    public void trace(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void trace(Marker marker, String s, Object... objects) {

    }

    @Override
    public void trace(Marker marker, String s, Throwable throwable) {

    }

    @Override
    public boolean isDebugEnabled() {
        return false;
    }

    @Override
    public void debug(String s) {

    }

    @Override
    public void debug(String s, Object o) {

    }

    @Override
    public void debug(String s, Object o, Object o1) {

    }

    @Override
    public void debug(String s, Object... objects) {

    }

    @Override
    public void debug(String s, Throwable throwable) {

    }

    @Override
    public boolean isDebugEnabled(Marker marker) {
        return false;
    }

    @Override
    public void debug(Marker marker, String s) {

    }

    @Override
    public void debug(Marker marker, String s, Object o) {

    }

    @Override
    public void debug(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void debug(Marker marker, String s, Object... objects) {

    }

    @Override
    public void debug(Marker marker, String s, Throwable throwable) {

    }

    @Override
    public boolean isInfoEnabled() {
        return false;
    }

    @Override
    public void info(String s) {

    }

    @Override
    public void info(String s, Object o) {

    }

    @Override
    public void info(String s, Object o, Object o1) {

    }

    @Override
    public void info(String s, Object... objects) {

    }

    @Override
    public void info(String s, Throwable throwable) {

    }

    @Override
    public boolean isInfoEnabled(Marker marker) {
        return false;
    }

    @Override
    public void info(Marker marker, String s) {

    }

    @Override
    public void info(Marker marker, String s, Object o) {

    }

    @Override
    public void info(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void info(Marker marker, String s, Object... objects) {

    }

    @Override
    public void info(Marker marker, String s, Throwable throwable) {

    }

    @Override
    public boolean isWarnEnabled() {
        return false;
    }

    @Override
    public void warn(String s) {

    }

    @Override
    public void warn(String s, Object o) {

    }

    @Override
    public void warn(String s, Object... objects) {

    }

    @Override
    public void warn(String s, Object o, Object o1) {

    }

    @Override
    public void warn(String s, Throwable throwable) {

    }

    @Override
    public boolean isWarnEnabled(Marker marker) {
        return false;
    }

    @Override
    public void warn(Marker marker, String s) {

    }

    @Override
    public void warn(Marker marker, String s, Object o) {

    }

    @Override
    public void warn(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void warn(Marker marker, String s, Object... objects) {

    }

    @Override
    public void warn(Marker marker, String s, Throwable throwable) {

    }

    @Override
    public boolean isErrorEnabled() {
        return false;
    }

    @Override
    public void error(String s) {

    }

    @Override
    public void error(String s, Object o) {

    }

    @Override
    public void error(String s, Object o, Object o1) {

    }

    @Override
    public void error(String s, Object... objects) {

    }

    @Override
    public void error(String s, Throwable throwable) {

    }

    @Override
    public boolean isErrorEnabled(Marker marker) {
        return false;
    }

    @Override
    public void error(Marker marker, String s) {

    }

    @Override
    public void error(Marker marker, String s, Object o) {

    }

    @Override
    public void error(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void error(Marker marker, String s, Object... objects) {

    }

    @Override
    public void error(Marker marker, String s, Throwable throwable) {

    }
}
【4】实现装饰器
package com.wf.decorator.logger;

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;

import java.util.Arrays;

/**
 * @ClassName JsonLogger
 * @Description 输出json格式的日志组件实现
 * @Author wf
 * @Date 2020/5/22 12:46
 * @Version 1.0
 */
public class JsonLogger extends LoggerDecorator {

    public JsonLogger(Logger logger) {
        super(logger);
    }

    @Override
    public void error(String s) {
        JSONObject result = newJSONObject();
        result.put("msg",s);
        logger.info(result.toString());
    }

    @Override
    public void info(String s) {
        JSONObject result = newJSONObject();
        result.put("msg",s);
        logger.info(result.toString());
    }
    public void error(Exception e){
        JSONObject result = newJSONObject();
        result.put("exception",e.getClass().getName());
        String trace = Arrays.toString(e.getStackTrace());
        result.put("exTrace",trace);
        logger.info(result.toString());

    }
    private JSONObject newJSONObject(){
        return new JSONObject();
    }
}

注意:

  这里引入JSONObject工具,需要添加依赖:

<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.32</version>
    </dependency>
【5】定义Logger工厂
package com.wf.decorator.logger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @ClassName JsonLoggerFactory
 * @Description 创建Logger的工厂类
 * @Author wf
 * @Date 2020/5/22 13:02
 * @Version 1.0
 */
public class JsonLoggerFactory {
    public static JsonLogger getLogger(Class clazz){
        Logger logger = LoggerFactory.getLogger(clazz);
        return new JsonLogger(logger);
    }
}
主要是为了生成JsonLogger实例。
【6】测试类修改
package com.wf.decorator.logger;

import org.slf4j.Logger;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/5/22 11:35
 * @Version 1.0
 */
public class Test {
    //private static final Logger logger = LoggerFactory.getLogger(Test.class);
    private static final Logger logger = JsonLoggerFactory.getLogger(Test.class);

    public static void main(String[] args) {
        logger.error("系统错误");
    }
}
测试结果如下:

 功能完成。

【7】查看系统类图

1.6.2.3.装饰器模式总结

1.装饰器模式在源码中应用

IO流中字节流,封装成字符流,缓冲流。 

package com.wf.decorator.logger;

import org.slf4j.Logger;

import java.io.*;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/5/22 11:35
 * @Version 1.0
 */
public class Test {
    //private static final Logger logger = LoggerFactory.getLogger(Test.class);
    private static final Logger logger = JsonLoggerFactory.getLogger(Test.class);

    public static void main(String[] args) {
        logger.error("系统错误");

        //IO流中装饰器模式应用
        try {
            InputStream in = new FileInputStream("");

            BufferedInputStream bis = new BufferedInputStream(in);

            bis.read();
            bis.close();

            BufferedReader br = new BufferedReader(new FileReader(""));
            br.readLine();

            BufferedReader bs = new BufferedReader(new StringReader(""));
            bs.readLine();

        } catch (Exception e) {
            e.printStackTrace();
            logger.error(String.valueOf(e));
        }
    }
}
2.装饰器模式与代理模式的对比

装饰器模式:

  是一种特殊的静态代理模式。

  强调自身功能的扩展透明【用户知道扩展细节】的扩展,可动态定制的扩展。

代理模式:

  强调代理过程的控制

3.装饰器模式的优缺点

优点:

  装饰器是继承的有力补充,比继承更灵活。不改变原有对象的情况下,动态地给一个对象扩展功能,即插即用。

  通过使用不同的装饰类,以及这些装饰类的排列组合,可实现更为强大的功能。 

  装饰器完全遵循开闭原则

缺点:

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

  动态装饰时,多层装饰会比较复杂。客户端调用复杂。

1.6.2.4.相关补充

1.列举3~5个使用门面模式的应用场景

1.项目中使用不同分层,如:mvc模式,三层架构。

2.分布式rpc调用时,通过门面模式将不同子系统提供服务整合。

3.日志框架将不同的日志级别,debug,info,warn,errror通过一个接口对外暴露。

4.web开发中Controller类。

2.使用装饰者模式实现一个可以根据权限动态扩展功能的导航条展示。
【1】需求分析

社区网站首页,导航条展示所有条目有:

1.问答

2.文章

3.精品课

4.冒泡

5.商城

6.作业

7.题库

8.成长墙

9.用户管理 

用户权限:

A.未登录 

B.已登录普通用户

C.已登录会员vip用户

D.管理员

根据不同权限,展示导航条目有:

A ->1,2,3,4,5

B->1,2,3,4,5,7

C->1,2,3,4,5,7,6,8

D->1,2,3,4,5,7,6,8,9

可以认为,前5种展示条目,所有权限都可以展示,处理成初始化时,组件具备基本功能。

而后面的每一种展示条目,分别设计成一个装饰器类。

客户端根据不同权限,组合装饰器功能,完成导航条的展示设计。

为此,也方便以后出现其他展示条目,进行动态扩展。

【2】设计类图

A.用户权限设计

 

 B.导航条展示设置

 C.系统类图

【3】代码编写

1.导航条展示组件顶层抽象接口

package com.wf.decorator.navigationbar;

/**
 * @ClassName AbstractNavigationBar
 * @Description 导航条抽象顶层接口
 * @Author wf
 * @Date 2020/5/22 14:48
 * @Version 1.0
 */
public abstract class AbstractNavigationBar {
    public abstract String showItems();
}

2.组件基本功能实现

package com.wf.decorator.navigationbar;

/**
 * @ClassName DefaultNavigationBar
 * @Description 组件默认实现
 * @Author wf
 * @Date 2020/5/22 14:52
 * @Version 1.0
 */
public class DefaultNavigationBar extends AbstractNavigationBar {
    @Override
    public String showItems() {
        return "问答,文章,精品课,冒泡,商城";
    }
}

3.定义装饰器抽象接口

package com.wf.decorator.navigationbar;

/**
 * @ClassName NavigationBarDecorator
 * @Description 导航条条目展示装饰器抽象接口
 * @Author wf
 * @Date 2020/5/22 14:55
 * @Version 1.0
 */
public abstract class NavigationBarDecorator extends AbstractNavigationBar{
    private AbstractNavigationBar navigationBar;

    public NavigationBarDecorator(AbstractNavigationBar navigationBar) {
        this.navigationBar = navigationBar;
    }

    @Override
    public String showItems() {
        return this.navigationBar.showItems();
    }
}

4.定义3个装饰器子类

package com.wf.decorator.navigationbar;

/**
 * @ClassName LoginDecorator
 * @Description 登录普通用户装饰器
 * @Author wf
 * @Date 2020/5/22 14:58
 * @Version 1.0
 */
public class LoginDecorator extends NavigationBarDecorator {
    public LoginDecorator(AbstractNavigationBar navigationBar) {
        super(navigationBar);
    }

    @Override
    public String showItems() {
       return super.showItems() +",题库";
    }
}

package com.wf.decorator.navigationbar;

import sun.plugin2.message.ShowDocumentMessage;

/**
 * @ClassName VipDecorator
 * @Description 已登录会员用户,导航条装饰器
 * @Author wf
 * @Date 2020/5/22 15:00
 * @Version 1.0
 */
public class VipDecorator extends NavigationBarDecorator {
    public VipDecorator(AbstractNavigationBar navigationBar) {
        super(navigationBar);
    }

    @Override
    public String showItems() {
        return super.showItems() +",作业,成长墙";
    }
}

package com.wf.decorator.navigationbar;

/**
 * @ClassName ManagerDecorator
 * @Description 管理员装饰器
 * @Author wf
 * @Date 2020/5/22 15:02
 * @Version 1.0
 */
public class ManagerDecorator extends NavigationBarDecorator {
    public ManagerDecorator(AbstractNavigationBar navigationBar) {
        super(navigationBar);
    }

    @Override
    public String showItems() {
        return super.showItems()+",用户管理";
    }
}

5.定义用户权限处理抽象接口

package com.wf.decorator.navigationbar;

/**
 * @ClassName IPermission
 * @Description 权限顶层接口
 * @Author wf
 * @Date 2020/5/22 15:25
 * @Version 1.0
 */
public abstract class AbstractPermission {
    protected AbstractNavigationBar navigationBar;

    protected void showItems(){
        System.out.println(navigationBar.showItems());
    }
}

6.定义四种不同权限处理实现类

package com.wf.decorator.navigationbar;

/**
 * @ClassName Visitor
 * @Description 未登录用户权限,访问权限
 * @Author wf
 * @Date 2020/5/22 15:29
 * @Version 1.0
 */
public class Visitor extends AbstractPermission {

    @Override
    protected void showItems() {
        navigationBar = new DefaultNavigationBar();
        super.showItems();
    }
}

package com.wf.decorator.navigationbar;

/**
 * @ClassName LoginUser
 * @Description TODO
 * @Author wf
 * @Date 2020/5/22 15:36
 * @Version 1.0
 */
public class LoginUser extends AbstractPermission {

    @Override
    protected void showItems() {
        navigationBar = new DefaultNavigationBar();
        navigationBar = new LoginDecorator(navigationBar);
        super.showItems();
    }
}

package com.wf.decorator.navigationbar;

/**
 * @ClassName VipUser
 * @Description 已登录会员权限
 * @Author wf
 * @Date 2020/5/22 15:40
 * @Version 1.0
 */
public class VipUser extends AbstractPermission {

    @Override
    protected void showItems() {
        navigationBar = new DefaultNavigationBar();
        navigationBar = new LoginDecorator(navigationBar);
        navigationBar = new VipDecorator(navigationBar);
        super.showItems();
    }
}

package com.wf.decorator.navigationbar;

/**
 * @ClassName Admin
 * @Description 管理员权限
 * @Author wf
 * @Date 2020/5/22 15:42
 * @Version 1.0
 */
public class Admin extends AbstractPermission {

    @Override
    protected void showItems() {
        navigationBar = new DefaultNavigationBar();
        navigationBar = new LoginDecorator(navigationBar);
        navigationBar = new VipDecorator(navigationBar);
        navigationBar = new ManagerDecorator(navigationBar);
        super.showItems();
    }
}

7.客户端调用

package com.wf.decorator.navigationbar;

import com.wf.decorator.general.Decorator;
import org.omg.CORBA.NVList;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/5/22 15:06
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        AbstractPermission permission = new Visitor();
        permission.showItems();

        permission = new LoginUser();
        permission.showItems();

        permission = new VipUser();
        permission.showItems();

        permission = new Admin();
        permission.showItems();

    }
}

测试结果如下:

  

附录:

1.log4j结合slf4j输出日志到控制台

1.1.maven引入依赖

 <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.25</version>
    </dependency>

    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
    </dependency>
原文地址:https://www.cnblogs.com/wfdespace/p/12925477.html