浅谈设计模式-外观/门面模式

书接上回,本篇讲一下结构型模式-外观设计模式

外观设计模式

外观模式又叫门面模式

定义:为子系统中的一组接口提供一个一致的界面,它定义了一个高层接口,这个接口使得这一子系统更加容易使用。(乍一看,还真不知道扯的是啥,文绉绉的。)

要解释一下名词:

子系统:可以理解为实现功能的各种组件(类、接口等)

界面:facade翻译过来就是界面的意思,有些人也翻译成门面。从代码上讲,可以理解为一个统一的api(可以是类可以是接口)

大白话:facade外观模式就是一种能遮蔽子系统(api操作/实现)复杂性,使用一套更简便api统一操作模式。

看下图:

其中的Facade就是界面/统一的API。

案例分析

需求:3个子系统,要求执行顺序A--->B--->C 

普通版

子系统 

public class SubSystemA {
    void doA(){
        System.out.println("执行了A操作....");
    }
}
public class SubSystemB {
    void doB(){
        System.out.println("执行了B操作....");
    }
}
public class SubSystemC {
    void doC(){
        System.out.println("执行了c操作....");
    }
}

测试 

public class App {
    public static void main(String[] args) {
        SubSystemA a = new SubSystemA();
        SubSystemB b = new SubSystemB();
        SubSystemC c = new SubSystemC();
        
        //A-->B-->
        a.doA();
        b.doB();
        c.doC();
    }
}
执行了A操作....
执行了B操作....
执行了c操作....

解析

上面代码实现需求约定:A-->B--->C, 这种写法从设计层面上分析是否存在问题呢?
答案是有,

1>客户端App执行A-->B--->C前提必须完全了解SubSystemA/B/C 3个子系统,同时知道怎么调用对应的方法,最终才能实现需求。
2>SystemA/B/C 3个子系统其实已经对App暴露所有代码细节啦,从架构设计角度看,应用层不应该知道低层实现细节。因为:迪米特原则,最少知道原因

怎么改进?外观模式

简单外观

需求:3个子系统,要求执行顺序A--->B--->C 

 SubSystemA/B/C 个类不变, 添加一个Facade类。

Facade类

public class Facade {
    public void doWork(){
        SubSystemA a = new SubSystemA();
        SubSystemB b = new SubSystemB();
        SubSystemC c = new SubSystemC();

        //A-->B-->
        a.doA();
        b.doB();
        c.doC();
    }
}

测试

public class App {
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.doWork();
    }
}

解析

从代码上仅仅是将执行逻辑拷贝到到Facade类执行而已,这个跟之前直接在App上操作好像没有啥区别?还是有区别,这种做法带来个好处

1>Facade类遮蔽客户端与系统内部模块交互(SubSystemA/B/C),把SubSystemA/B/C 模块组成一个整体,开放Facade类统一对外。这操作不但方便客户端调用,也封装了系统内部细节。也就是说,Facade类与各个模块的交互已经是内部行为啦,即使后续ABC调用顺序发生该变,只需要内部调整即可,客户端完全无须变动。

2>提高代码复用性,简化开发。当多个客户端需要使用SubSystemA/B/C功能时,只需要调用Facade类doWork方法即可,无须各种new, 各种子系统调用。

3>减少学习成,从学习3个子系统操作,变为学习一个Facade类。

抽象版

普通版的外观模式还是一定问题,比如:

子系统执行顺序:A-->B--->C  要调整为  C-->B-->A, 怎么办?

此时需要直接修改Facade类doWork代码,一旦这么操作,就违背了开闭原则(扩展开发,修改关闭) 怎办?--->抽象外观模式

SubSystemA/B/C 类不变

IFacade接口

public interface IFacade {
    void doWork();
}

FacadeImplABC

public class FacadeImplABC implements IFacade {
    public void doWork(){
        SubSystemA a = new SubSystemA();
        SubSystemB b = new SubSystemB();
        SubSystemC c = new SubSystemC();

        //A-->B-->C
        a.doA();
        b.doB();
        c.doC();
    }
}

FacadeImplCBA

public class FacadeImplCBA implements IFacade {
    public void doWork(){
        SubSystemA a = new SubSystemA();
        SubSystemB b = new SubSystemB();
        SubSystemC c = new SubSystemC();
        //C-->B-->A
        c.doC();
        b.doB();
        a.doA();
    }
}

 测试

public class App {
    public static void main(String[] args) {

        FacadeImplABC abc = new FacadeImplABC();
        FacadeImplCBA cba = new FacadeImplCBA();
        abc.doWork();  //ABC
        
        cba.doWork();  //CBA
    }
}

解析

后续需要调整ABC执行顺序, 只需要new 出新的Facade即可。UML图中画了FacadeFactory类,起来将FacadeImpl类跟工厂方法设计模式混合使用。

适用场景

1>给复杂子系统提供统一的简单接口时,可以使用

2>想让客户端与实现类存在松散耦合时,可以使用。

3>想构建多层结构系统时,可以使用。

优缺点

优点
简化调用过程,无需深入了解子系统
减少系统依赖,设计出松散耦合
更好的划分访问层次,隐藏内部实现细节
符合迪米特法则,即最少知道原则

缺点
增加子系统,拓展子系统行为容易引入风险
不符合开闭原则

开发案例

外观模式运用非常广泛,从JDK里面找找-HttpClientFacade

jdk.internal.net.http 包下一个类进行http访问的

final class HttpClientFacade extends HttpClient implements Trackable {

    final HttpClientImpl impl;

    /**
     * Creates an HttpClientFacade.
     */
    HttpClientFacade(HttpClientImpl impl) {
        this.impl = impl;
    }

    @Override // for tests
    public Tracker getOperationsTracker() {
        return impl.getOperationsTracker();
    }
    @Override
    public Optional<CookieHandler> cookieHandler() {
        return impl.cookieHandler();
    }
    @Override
    public SSLContext sslContext() {
        return impl.sslContext();
    }
    .....

}

其中的子系统对象是:HttpClientImpl 

除了上面之外,在Tomcat中也能找到非常经典使用-RequestFacade

Tomcat传给Servlet 或者springmvc的controller中request并不是原生的request对象而是包装之后的,也就是RequestFacade

public class RequestFacade implements HttpServletRequest {
    protected Request request = null;
    protected static final StringManager sm = StringManager.getManager(RequestFacade.class);

    public RequestFacade(Request request) {
        this.request = request;
    }


    public Object getAttribute(String name) {
        if (this.request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        } else {
            return this.request.getAttribute(name);
        }
    }
    public String getContentType() {
        if (this.request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        } else {
            return this.request.getContentType();
        }
    }

    public ServletInputStream getInputStream() throws IOException {
        if (this.request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        } else {
            return this.request.getInputStream();
        }
    }
    .....
}

其中的子系统对象是:Request 

总结

外观模式本质:封装交互,简化调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪飞yes

我对钱没兴趣~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值