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>