专业编程基础技术教程

网站首页 > 基础教程 正文

门面模式和装饰器模式详解

ccvgpt 2024-08-04 12:15:41 基础教程 23 ℃

门面模式

定义:门面模式(Facade Pattern)又叫外观模式,提供了一个统一的接口,用来访问子系统中的一群接口,其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构模式。

比如通常app首页的展示,涉及到多个模块,如果每个模块单独请求接口,就需要调多个接口。而后端可以先将多个模块的数据集成好,统一返回给前端。这个就是运用了门面模式。还比如购物车的信息,前端需要展示库存,金额,优惠,供应商,等等相关信息。如果把数据整合处理放在前端就会很麻烦,我们由后台将数据处理好,返回给前端,这样就精简了前端的接口调用处理。

门面模式和装饰器模式详解

门面模式应用场景:

  1. 子系统过于复杂,前端调用牵扯比较多,可以使用门面模式整合,提供简单的接口。
  2. 例如mvc,每层调用时,可以使用门面模式作为入口,简化调用。比如dao层,service层,controller层,在service层做逻辑处理,只通过一个接口提供给controller,在dao层做好数据库交互,也利用门面模式给service提供一个入口。

门面模式在源码中的应用:

1. Spring JDBC模块下的JdbcUtils类,它封装了和JDBC相关的所有操作

2. Mybatis中的Configuration类就封装了JDBC中关键组件的操作

门面模式的优缺点:

门面模式虽然对调用者来说更简单,减少了系统间的依赖。但是当子系统出现扩展时,调用者存在未知风险。不符合开闭原则。

装饰器模式

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

装饰器模式主要用于透明且动态地扩展类的功能。其实现原理为:让装饰器实现被包装类(ConcreteComponent)相同的接口(Component)(使得装饰器与被扩展类类型一致),并在构造函数在中传入该接口(Component)对象,然后就可以在接口需要实现的方法中,在被包装类对象的现有功能上添加新功能了。由于装饰器与被包装类属于同一类型(均为Component),且构造函数的参数为其实现接口类(Component),因此装饰器模式具备嵌套扩展功能,这样我们就能使用装饰器模式一层一层的对最底层被包装类进行功能扩展了。

我们来看看通用的代码写法:

抽象组件(Component):可以使一个接口或者抽象类,其充当被装饰类的原始对象,规定了被装饰对象的行为;

public abstract class Component {
    /**
     * 示例方法
     */
    public abstract void operation();
}

具体组件(ConcreteComponent):实现/集成Component的一个具体对象,也即被装饰对象;

public class ConcreteComponent extends Component {
  
    public void operation() {
        //相应的功能处理
        System.out.println("处理业务逻辑ConcreteComponent");
    }
}

抽象装饰器(Decorator):通用的装饰ConcreteComponent的装饰器,其内部必然有一个属性指向Component抽象组件;其实现一般是一个抽象类,主要是为了让其子类按照其构造形式传入一个Component抽象组件,这是强制的通用行为(当然,如果系统中装饰器逻辑单一,并不需要实现许多装饰器,那么我们可以直接省略该类,而直接实现一个具体装饰器(ConcreteDecorator)即可);

public abstract class Decorator extends Component {
    /**
     * 持有组件对象
     */
    protected Component component;
  
    /**
     * 构造方法,传入组件对象
     * @param component 组件对象
     */
    public Decorator(Component component) {
        this.component = component;
    }
  
    public void operation() {
        //转发请求给组件对象,可以在转发前后执行一些附加动作
        component.operation();
    }
}

具体装饰器(ConcreteDecorator): Decorator的具体实现类,理论上,每个ConcreteDecorator都扩展了Component对象的一种功能。

public class ConcreteDecoratorA extends Decorator {
       public ConcreteDecoratorA(Component component) {
            super(component);
   }
       private void operationFirst(){
           System.out.println("处理业务逻辑ConcreteComponent..ConcreteDecoratorA...operationFirst");
       } //在调用父类的operation方法之前需要执行的操作
       private void operationLast(){
           System.out.println("处理业务逻辑ConcreteComponent..ConcreteDecoratorA...operationLast");
       } //在调用父类的operation方法之后需要执行的操作
       public void operation() {
           //调用父类的方法,可以在调用前后执行一些附加动作
           operationFirst(); //添加的功能
           super.operation();  //这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能
           operationLast(); //添加的功能
   }
}


public class ConcreteDecoratorB extends Decorator {
       public ConcreteDecoratorB(Component component) {
            super(component);
   }
       private void operationFirst(){
           System.out.println("处理业务逻辑ConcreteComponent..ConcreteDecoratorB...operationFirst");
       } //在调用父类的operation方法之前需要执行的操作
       private void operationLast(){
           System.out.println("处理业务逻辑ConcreteComponent..ConcreteDecoratorB...operationLast");
       } //在调用父类的operation方法之后需要执行的操作
       public void operation() {
           //调用父类的方法,可以在调用前后执行一些附加动作
           operationFirst(); //添加的功能
           super.operation();  //这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能
           operationLast(); //添加的功能
   }
}

最后写一段测试代码执行一下:

public class Client{
    public static void main(String[] args){
        Component c1 = new ConcreteComponent (); //首先创建需要被装饰的原始对象(即要被装饰的对象)
        Decorator decoratorA = new ConcreteDecoratorA(c1); //给对象透明的增加功能A并调用
        decoratorA .operation();
        System.out.println("===============================");
        Decorator decoratorB = new ConcreteDecoratorB(c1); //给对象透明的增加功能B并调用
        decoratorB .operation();
        System.out.println("===============================");
        Decorator decoratorBandA = new ConcreteDecoratorB(decoratorA);//装饰器也可以装饰具体的装饰对象,此时相当于给对象在增加A的功能基础上在添加功能B
        decoratorBandA.operation();
    }
}


从上图中,我们可以看到当装饰器叠加的时候,每个装饰器的增强都执行了。最后,我们来看一下装饰器模式的UML类图关系:

装饰器的应用场景:

  1. 用于扩展一个类的功能,或给一个类添加附加职责。
  2. 动态的给一个对象添加功能,这些功能可以再动态的撤销。
  3. 需要为一批的兄弟类进行改装或加装功能。

装饰器在源码中的应用:装饰器模式在JDK中应用还是蛮多的,最明显的类就是IO相关的类,如BufferedReader、InputStream、OutputStream等。我们来看下InputStream的UML类图,我们可以看到XXInputStream是一层套一层的:

在Spring中的TransactionAwareCacheDecorator类我们也可以来尝试理解一下,这个类主要是用来处理事物缓存的,来看一下代码:

TransactionAwareCacheDecorator就是对Cache的一个包装,再来看一个MVC中的装饰器模式HttpHeadResponseDecorator类:

再来看看mybatis里面Cache使用的装饰器的使用模式:

装饰器模式与代理模式对比:

从代码来看,装饰器模式和代理模式是一样的,但这两种模式所面向的功能扩展面是不一样的:

装饰器模式强调自身功能的扩展。Decorator所做的就是增强ConcreteComponent的功能(也有减弱功能),主体对象为ConcreteComponent,着重类自身功能的变化;

代理模式强调对代理过程的控制。Proxy完全掌握对RealSubject的访问控制。因此,Proxy可以决定对RealSubject进行功能扩展,功能缩减甚至功能丧失(不调用RealSubject方法),主体对象为Proxy。

装饰器模式的优缺点:

优点:

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

缺点:

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

最近发表
标签列表