门面模式与装饰模式
目录
1、门面模式
1.1 什么是门面模式
门面模式又叫外观模式,提供一个统一的接口,用来访问子系统中的一群接口。其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构性模式。
1.2 结构图
- 门面模式共分为四种角色:
- Facade(外观角色):客户端调用子系统功能的入口,正常情况下,它将所有客户端发过来的请求委托给相应的子系统处理
- SubSystem(子系统):可以是一个类、一组类、一个模块或者是一个系统,可以被客户端直接调用或者被外观类调用。子系统并不知道外观的存在,对于它而言外观也是客户端
1.3 实例演示
//外观角色
public class Facade {
private SubSystemA sa;
private SubSystemB sb;
private SubSystemC sc;
public Facade() {
sa = new SubSystemA();
sb = new SubSystemB();
sc = new SubSystemC();
}
public void show() {
sa.show();
sb.show();
sc.show();
}
}
//子系统
public class SubSystemA {
public void show() {
System.out.println("aaa");
}
}
public class SubSystemB {
public void show() {
System.out.println("bbb");
}
}
public class SubSystemC {
public void show() {
System.out.println("ccc");
}
}
//客户端
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.show();
}
}
1.4 小总结
门面模式相对而言是相对容易理解的。如果善于留意,我们经常有用到。
门面模式的主要优点如下:
- 通过引入门面模式,客户端代码将变得很简单,与之关联的对象也很少。
- 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。
- 一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
外观模式的主要缺点如下:
- 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性。
- 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则。
适用场景:
- 当要为访问一系列复杂的子系统提供一个简单入口时可以使用外观模式。
- 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性。
- 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。
门面模式与代理模式对比
- 门面模式可以认为就是静态代理模式。
- 门面模式关注的重点是对子系统的调用;而代理模式关注的重点是对目标类的扩展和增强。
门面模式与单例模式的关系
- 通常将门面模式类设计成单例类,比如各种工具包的包装。
2、装饰模式
2.1 什么是装饰模式
装饰者模式又叫包装模式,是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案,属于结构性模式。
2.2 结构图
- 装饰模式共分为四种角色:
- Component(抽象构件):具体构件与抽象装饰类的共同父类,声明具体构件中实现的业务方法,它的出现能够让客户端一致的透明的对待装饰前和装饰后的类
- ConcreteComponent(具体构件):抽象构件的子类,实现具体的业务方法
- Decorator(抽象装饰类):抽象构件的子类,内部维持一个抽象构件的引用,通过该引用调用具体构件的业务方法
- ConcreteDecorator(具体装饰类):抽象装饰类的实现类,声明并实现各种装饰方法实现对具体构件的装饰
- 具体构件和装饰具体构件的抽象装饰类通过注入的方式被抽象装饰类继续装饰
- 因为具体构件和抽象装饰类都继承抽象构件,所以被装饰时可以被同样的对待,更加灵活。
2.3 实例演示
/**
* @description: 姜饼抽象构建类
* @author: zps
* @create: 2020-05-03 14:46
**/
public abstract class Battercake {
protected abstract String getMsg();
protected abstract int getPrice();
}
/**
* @description: 基类
* @author: zps
* @create: 2020-05-03 15:23
**/
public class BaseBattercake extends Battercake{
@Override
protected String getMsg() {
return "煎饼";
}
@Override
protected int getPrice() {
return 6;
}
}
/**
* @description: 加鸡蛋
* @author: zps
* @create: 2020-05-03 16:11
**/
public class EggDecorator extends BattercakeDirect{
public EggDecorator(Battercake battercake) {
super(battercake);
}
public String getMsg(){
return super.getMsg() + "加了一个鸡蛋";
}
public int getPrice(){
return (int) (super.getPrice() + 1);
}
}
/**
* @description: 加一块肉
* @author: zps
* @create: 2020-05-03 16:14
**/
public class PorkDecrator extends BattercakeDirect{
public PorkDecrator(Battercake battercake) {
super(battercake);
}
public String getMsg(){
return super.getMsg() + "加了一块肉";
}
public int getPrice(){
return super.getPrice() + 4;
}
}
/**
* @description:
* @author: zps
* @create: 2020-05-03 15:24
**/
public class BattercakeDirect extends Battercake {
private Battercake battercake;
public BattercakeDirect(Battercake battercake){
this.battercake = battercake;
}
@Override
protected String getMsg() {
return this.battercake.getMsg();
}
@Override
protected int getPrice() {
return this.battercake.getPrice();
}
}
/**
* @description: 测试
* @author: zps
* @create: 2020-05-03 16:16
**/
public class Test {
public static void main(String[] args) {
Battercake battercake = new BaseBattercake();
battercake = new EggDecorator(battercake);
battercake = new PorkDecrator(battercake);
System.out.println(battercake.getMsg() +"一共花了:" + battercake.getPrice() + "元");
}
}
结果:
2.4 小总结
装饰模式的主要优点如下:
- 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。
- 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合 “开闭原则”。
装饰模式的主要缺点如下:
- 使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。
- 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
适用场景:
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如Java语言中的final类)。
装饰者模式的应用场景:
- java I/O流中的就运用了装饰者模式
- mybatis中缓存也运用了装饰者模式