掘金 后端 ( ) • 2024-04-03 13:37

门面模式

门面模式(Facade),也叫 “外观模式”:门面模式为子系统中的一组接口提供一个一致的门面(也就是软件的入口),此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。门面模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用者只需跟这个接口发生调用,而无需关心这个子系统的内部细节。

门面模式包含三种角色:

  • Facade(门面类):为调用者提供统一的调用接口, 门面类知道哪些子系统负责处理请求并完成相关的请求处理,从而将调用者的请求转交给适当子系统对象去完成处理。
  • Client (调用者): 门面接口的调用者,也就是使用者。
  • SubSystemList(子系统的集合)也就是门面接口实现类中的成员变量:指模块或者子系统,去实际处理 Facade对象指派的任务,他是功能的实际提供者。

案例:我们以烹饪为例,从商品购买食材,到切菜烹饪整个过程演示门面模式。其中Cooker就是门面类,Client类就是调用者类,其他类都属于子系统的集合。

UML类图:

门面模式.jpg

客户端Client类:

/**
 * 这里客户端模拟人对象
 */
public class Client {
​
    public static void main(String[] args) {
        Cooker cooker = new Cooker();
        cooker.cooking();
        System.out.println("开始吃碗里做好的食物");
    }
}

厨师类Cooker类:

/**
 * 模拟厨师对象
 */
public class Cooker {
​
    private Shop shop = new Shop();
​
    private Water water = new Water();
​
    private FoodKnife fk = new FoodKnife();
​
    private IronPan pan = new IronPan();
​
    private Stove stove = new Stove();
​
    private Bowl bowl = new Bowl();
​
    /**
     * 烹饪方法
     */
    public void cooking() {
        //从菜市场将食材买到
        Food food = shop.getFood();
        //对食材进行清洗
        water.wash(food);
        //将食材用菜刀进行切加工
        fk.cut(food);
        //将切好的食材放入铁锅中
        pan.makeIn(food);
        //将灶台点火并将装有食材的铁锅放上去进行煎炒
        stove.fireUpAndPlace(pan, food);
        //煎炒完毕放入碗中可以吃了
        bowl.makeIn(food);
    }
}

Shop商店类:

/**
 * 商店类
 */
public class Shop {
​
    /**
     * 购买食材方法
     */
    public Food getFood() {
        System.out.println("从菜市场买到了食材");
        return new Food();
    }
}

食品Food类:

/**
 * 食品类
 */
public class Food {
    
}

水Water类:

/**
 * 水类
 */
public class Water {
​
    /**
     * 洗食材方法
     */
    public void wash(Food food) {
        System.out.println("对食材用水进行清洗。");
    }
}

菜刀FoodKnife类:

/**
 * 菜刀类
 */
public class FoodKnife {
​
    /**
     * 切食材方法
     */
    public void cut(Food food) {
        System.out.println("用菜刀切食材,使之达到可以直接下锅的状态。");
    }
}

铁锅IronPan类:

/**
 * 铁锅类
 */
public class IronPan {
​
    /**
     * 放入食材
     */
    public void makeIn(Food food) {
        System.out.println("将切好的食材放入铁锅中");
    }
​
    /**
     * 炒食材
     */
    public void saute(Food food) {
        System.out.println("对食材进行煎炒……");
    }
}

天然气灶Stove类:

/**
 * 天然气灶类
 */
public class Stove {
​
    /**
     * 点火并放上铁锅方法
     */
    public void fireUpAndPlace(IronPan pan, Food food) {
        pan.saute(food);
    }
}

碗Bowl类:

/**
 * 碗类
 */
public class Bowl {
​
    /**
     * 盛东西
     */
    public void makeIn(Food food) {
        System.out.println("将做好的食材盛入碗中……");
    }
}

总结:

  • 门面模式对外屏蔽了子系统的细节,因此门面模式降低了客户端对子系统使用的复杂性。
  • 门面模式对客户端与子系统的直接耦合关系进行了解耦,让子系统内部的模块更易维护和扩展。
  • 通过合理的使用门面模式,可以帮我们更好的划分访问的层次,形成较为合理的层次结构,特别是在分层模块化架构的时候,就应该使用门面模式。
  • 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade类交互,提高代码的可复用性。
  • 不能过多或者滥用门面模式,使用门面模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。
  • 门面模式将具体实现细节和调用者去调控进行分离,使得调用者对其功能的内部细节实现方式不关心,就遵守了迪米特法则,调用者尽可能少的知道其要实现功能的者的内部实现细节。