设计模式之门面模式

519 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

门面模式,也叫外观模式,在 GoF 的《设计模式》一书中,门面模式是这样定义的:门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。

如果你的日常工作中涉及接口开发,不知道你有没有关注过接口的粒度问题。为了使接口更加通用(可复用性),我们会将接口设计的更细粒度一点,这也符合单一职责原则。但是如果接口的粒度太小,就导致使用者在调用的时候需要调用很多接口才能完成一次事务。相反,如果接口设计的粒度太大,一个接口做很多事情,又会导致接口不够通用,为此针对不同的业务需求,我们就需要开发很多不同的接口来满足,这会导致系统的接口不断膨胀,可复用性也很差。门面模式就是为解决接口的可复用性(通用性)和易用性之间的矛盾而来。

应用场景

  • 封装系统的底层实现,隐藏系统的复杂性。

  • 解决性能问题。例如多个http请求的接口耗时,合并成一个请求后节省了网络通信的耗时。

  • 解决分布式事务问题。封装多个不同的系统接口,在一个接口内处理事务,避免了使用复杂的分布式事务框架或者事后补偿的机制的解决方案。

类图

图片

类图

Facade类通过构造函数注入了两个模块ModuleA和ModuleB,并将ModuleA中和getInteger()方法和ModuleB中的getString()方法组合后返回。

代码实现

ModuleA

public interface ModuleA {

    Integer getInteger();

}

ModuleAImpl

public class ModuleAImpl implements ModuleA {

    @Override
    public Integer getInteger() {
        return 1;
    }

}

ModuleB

public interface ModuleB {

    String getString();

}

ModuleBImpl

public class ModuleBImpl implements ModuleB {

    @Override
    public String getString() {
        return "hello";
    }

}

VO

public class VO {

    private Integer integer;
    private String string;

    public Integer getInteger() {
        return integer;
    }

    public void setInteger(Integer integer) {
        this.integer = integer;
    }

    public String getString() {
        return string;
    }

    public void setString(String string) {
        this.string = string;
    }

    @Override
    public String toString() {
        return "VO{" +
                "integer=" + integer +
                ", string='" + string + '\'' +
                '}';
    }

}

Facade

public class Facade {

    private ModuleA moduleA;
    private ModuleB moduleB;

    public Facade(ModuleA moduleA, ModuleB moduleB) {
        this.moduleA = moduleA;
        this.moduleB = moduleB;
    }

    public VO comb() {
        VO vo = new VO();
        vo.setInteger(moduleA.getInteger());
        vo.setString(moduleB.getString());
        return vo;
    }

}

Main

public class Main {

    public static void main(String[] args) {
        ModuleA moduleA = new ModuleAImpl();
        ModuleB moduleB = new ModuleBImpl();
        Facade facade = new Facade(moduleA, moduleB);
        
        VO vo = facade.comb();
        System.out.println(vo.toString());
    }

}

接口粒度设计得太大,太小都不好。太大会导致接口复用性差,太小会导致接口使用复杂。在实际的开发中,接口的可复用性和易用性需要“微妙”的权衡。对于接口设计,建议 「尽量保持接口的可复用性,但针对特殊情况,允许提供冗余的门面接口,来提供更易用的接口。」