意外发现,原来你不知道自己每天都在用门面模式

简介: 一般的电商平台都是整合众多的子系统聚合到一起形成一个大型的购物平台,一般情况下,有很多现成的功能都不是重新开发的,而是要去对接已有的各个子系统,这些子系统可能涉及积分系统、支付系统、物流系统的接口调用。如果所有的接口调用全部由前端发送网络请求去调用现有接口,一则会增加前端开发人员的难度,二则会增加一些网络请求,影响页面性能。此时就可以发挥门面模式的优势了。将所有现成的接口全部整合到一个类中,由后端提供统一的接口供前端调用,这样前端开发人员就不需要关心各接口的业务关系,只需要把精力集中在页面交互上。我们用代码来模拟一个积分兑换礼品的业务场景。

本文节选自《设计模式就该这样学》

1 使用门面模式整合已知API的功能

一般的电商平台都是整合众多的子系统聚合到一起形成一个大型的购物平台,一般情况下,有很多现成的功能都不是重新开发的,而是要去对接已有的各个子系统,这些子系统可能涉及积分系统、支付系统、物流系统的接口调用。如果所有的接口调用全部由前端发送网络请求去调用现有接口,一则会增加前端开发人员的难度,二则会增加一些网络请求,影响页面性能。此时就可以发挥门面模式的优势了。将所有现成的接口全部整合到一个类中,由后端提供统一的接口供前端调用,这样前端开发人员就不需要关心各接口的业务关系,只需要把精力集中在页面交互上。我们用代码来模拟一个积分兑换礼品的业务场景。

首先创建礼品的实体类GiftInfo。

public class GiftInfo {
    private String name;
    public GiftInfo(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

然后编写各个子系统的业务逻辑代码,创建积分系统QualifyService类。

public class QualifyService {
    public boolean isAvailable(GiftInfo giftInfo){
        System.out.println("校验" + giftInfo.getName() + " 积分资格通过,库存通过");
        return true;
    }
}

创建支付系统PaymentService类。

public class PaymentService {
    public boolean pay(GiftInfo pointsGift){
        //扣减积分
        System.out.println("支付" + pointsGift.getName() + " 积分成功");
        return true;
    }
}

创建物流系统ShippingService类。

public class ShippingService {
    //发货
    public String delivery(GiftInfo giftInfo){
        //物流系统的对接逻辑
        System.out.println(giftInfo.getName() + "进入物流系统");
        String shippingOrderNo = "666";
        return shippingOrderNo;
    }
}

接着创建外观角色GiftFacadeService类,对外只开放一个兑换礼物的exchange()方法,在exchange()方法内部整合3个子系统的所有功能。

public class GiftFacadeService {
    private QualifyService qualifyService = new QualifyService();
    private PaymentService pointsPaymentService = new PaymentService();
    private ShippingService shippingService = new ShippingService();
    //兑换
    public void exchange(GiftInfo giftInfo){
        if(qualifyService.isAvailable(giftInfo)){
            //资格校验通过
            if(pointsPaymentService.pay(giftInfo)){
                //如果支付积分成功
                String shippingOrderNo = shippingService.delivery(giftInfo);
                System.out.println("物流系统下单成功,订单号是:"+shippingOrderNo);
            }
        }
    }
}

最后来看客户端代码。

public static void main(String[] args) {
        GiftInfo giftInfo = new GiftInfo("《Spring 5核心原理》");
        GiftFacadeService giftFacadeService = new GiftFacadeService();
        giftFacadeService.exchange(giftInfo);
}

运行结果如下图所示。

20211113132707526.png

通过这样一个案例对比,相信大家对门面模式的印象就非常深刻了。

2 门面模式在Spring源码中的应用

先来看Spring JDBC模块下的JdbcUtils类,它封装了与JDBC相关的所有操作,代码片段如下。

public abstract class JdbcUtils {
    public static final int TYPE_UNKNOWN = -2147483648;
    private static final Log logger = LogFactory.getLog(JdbcUtils.class);
    public JdbcUtils() {
    }
    public static void closeConnection(Connection con) {
        if(con != null) {
            try {
                con.close();
            } catch (SQLException var2) {
                logger.debug("Could not close JDBC Connection", var2);
            } catch (Throwable var3) {
                logger.debug("Unexpected exception on closing JDBC Connection", var3);
            }
        }
    }
    public static void closeStatement(Statement stmt) {
        if(stmt != null) {
            try {
                stmt.close();
            } catch (SQLException var2) {
                logger.trace("Could not close JDBC Statement", var2);
            } catch (Throwable var3) {
                logger.trace("Unexpected exception on closing JDBC Statement", var3);
            }
        }
    }
    public static void closeResultSet(ResultSet rs) {
        if(rs != null) {
            try {
                rs.close();
            } catch (SQLException var2) {
                logger.trace("Could not close JDBC ResultSet", var2);
            } catch (Throwable var3) {
                logger.trace("Unexpected exception on closing JDBC ResultSet", var3);
            }
        }
    }
    ...
}

更多其他操作,看它的结构就非常清楚了,如下图所示。

20211113132707725.png

3 门面模式在MyBatis源码中的应用

再来看一个MyBatis中的Configuration类,其中有很多new开头的方法,源码如下。

public MetaObject newMetaObject(Object object) {
        return MetaObject.forObject(object, this.objectFactory, this.objectWrapperFactory, this.reflectorFactory);
    }
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, 
BoundSql boundSql) {
        ParameterHandler parameterHandler = 
                  mappedStatement.getLang().createParameterHandler(mappedStatement,             parameterObject, boundSql);
        parameterHandler = (ParameterHandler)this.interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
    }
    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, 
        RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
        ResultSetHandler resultSetHandler = 
        new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        ResultSetHandler resultSetHandler = (ResultSetHandler)this.interceptorChain.pluginAll (resultSetHandler);
        return resultSetHandler;
    }
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, 
                Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,             BoundSql boundSql) {
        StatementHandler statementHandler = 
          new RoutingStatementHandler(executor, mappedStatement, parameterObject,           rowBounds, resultHandler, boundSql);
        StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll             (statementHandler);
        return statementHandler;
    }
    public Executor newExecutor(Transaction transaction) {
        return this.newExecutor(transaction, this.defaultExecutorType);
}

上面这些方法都是对JDBC中关键组件操作的封装。

4 门面模式在Tomcat源码中的应用

另外,门面模式在Tomcat的源码中也有体现,也非常有意思。以RequestFacade类为例,来看其源码。

public class RequestFacade implements HttpServletRequest {
...
@Override
    public String getContentType() {
        if (request == null) {
            throw new IllegalStateException(
                            sm.getString("requestFacade.nullRequest"));
        }
        return request.getContentType();
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (request == null) {
            throw new IllegalStateException(
                            sm.getString("requestFacade.nullRequest"));
        }
        return request.getInputStream();
    }
    @Override
    public String getParameter(String name) {
        if (request == null) {
            throw new IllegalStateException(
                            sm.getString("requestFacade.nullRequest"));
        }
        if (Globals.IS_SECURITY_ENABLED){
            return AccessController.doPrivileged(
                new GetParameterPrivilegedAction(name));
        } else {
            return request.getParameter(name);
        }
    }
...
}

从名字就知道它用了门面模式。它封装了非常多的request操作,也整合了很多servlet-api以外的内容,给用户使用提供了很大便捷。同样,Tomcat针对Response和Session也封装了对应的ResponseFacade类和StandardSessionFacade类,感兴趣的小伙伴可以深入了解一下。

小伙伴们是不是意外地发现,你每天都在用门面模式?


【推荐】Tom弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦


本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。

相关文章
|
5月前
|
设计模式
学会了这个设计模式,再也不是只会写if/else了
本文详细介绍了责任链设计模式(Chain of Responsibility Pattern),这是一种行为型设计模式,用于创建一个接收者对象的链,通过解耦请求的发送者和接收者,允许沿着链传递请求,直到某个接收者能够处理它。
学会了这个设计模式,再也不是只会写if/else了
|
7月前
|
开发框架 Java
JAVA反射:揭秘!运行时如何窥探类的秘密?
【6月更文挑战第30天】Java反射是运行时检查类信息并动态操作对象的机制。通过`Class`对象,我们可以访问私有成员,如在Person类示例中设置私有变量name和调用方法。反射增加了代码灵活性,常用于动态类型、插件和框架设计。
38 0
|
设计模式 安全 Java
设计模式之单例模式(创建、单例破坏、防止破坏)
设计模式之单例模式(创建、单例破坏、防止破坏)
102 0
|
8月前
|
算法
犯错总结--工厂模式和策略模式傻傻没分清
犯错总结--工厂模式和策略模式傻傻没分清
68 0
犯错总结--工厂模式和策略模式傻傻没分清
|
8月前
|
设计模式 存储
二十三种设计模式全面解析-深入探究备忘录模式:保留过去,预见未来
二十三种设计模式全面解析-深入探究备忘录模式:保留过去,预见未来
|
8月前
|
设计模式
二十三种设计模式全面解析-当你的对象需要知道其他对象的状态变化时,观察者模式是你的救星!
二十三种设计模式全面解析-当你的对象需要知道其他对象的状态变化时,观察者模式是你的救星!
|
安全 算法 关系型数据库
如何避免在C#中出现混乱代码
如何避免在C#中出现混乱代码
|
Java C语言
看似无害的代码如何搞垮系统
编程就像魔法。最近遇到一个诡异的问题:添加一段看似无害的简单代码后,系统原有功能不可用了。 ## 复现演示 jdk 8 可使用如下演示代码复现这个问题。 `TaskCenter` 是一个任务框架,可添加多个任务,随后框架将执行这些任务。 `First` 任务是新增代码,看起来简单无害,且看不出对原有任务 `Count` 有何影响,但添加 `First` 任务后,其自身执行正常,原本正常的 `C
135 0
|
设计模式 Java
浅析Java设计模式【3.7】——状态
Java常用设计模式,状态模式
91 0
浅析Java设计模式【3.7】——状态
|
设计模式 消息中间件 JavaScript
代码越写越乱?那是因为你没用责任链
代码越写越乱?那是因为你没用责任链
代码越写越乱?那是因为你没用责任链