外观(Fascade)模式定义一个高级的接口,将子系统里的一组接口整合起来,提供了一个统一的外观。
    在以下情况下可以考虑使用外观模式:

    • 设计初期阶段,应该有意识的将不同层分离,层与层之间建立外观模式。
    • 开发阶段,子系统越来越复杂,增加外观模式提供一个简单的调用接口。
    • 维护一个大型遗留系统的时候,可能这个系统已经非常难以维护和扩展,但又包含非常重要的功能,为其开发一个外观类,以便新系统与其交互

    “那外观模式在什么时候使用最好呢?”
    “这要分三个阶段来说,首先,在设计初期阶段,应该要有意识将不同的两个层分离,比如经典的三层架构,就需要考虑在数据访问层和业务逻辑层、业务逻辑层和表示层的层与层之间建立外观Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合度大大降低。
    其次,在开发阶段子系统往往因为不断的重构演化而变得越来越复杂,大多数的模式使用时也都会产生很多很小的类,这本是好事,但也给外部调用它们的用户程序带来了使用上的困难,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。
    第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含非常重要的功能,新的需求开发必须要依赖于它。此时,用外观模式Facade也是非常合适的。你可以为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰的简单接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。”

    举个例子,我们要实现游戏里挂机的功能,在几张地图之间冒险,将掉落的物品卖掉,然后当玩家HP小于一定值的时候,去医院治疗。
    首先,游戏里已经定义了玩家类(这里用一个简化的单例类表示,完整单例的写法参考小话设计模式之单例模式):

    1. public class Player
    2. {
    3. static Player _player = null;
    4. private Player()
    5. {
    6. HP = 100;
    7. }
    8. public static Player GetInstance()
    9. {
    10. if (_player == null) {
    11. _player = new Player ();
    12. }
    13. return _player;
    14. }
    15. public int HP { get; set;}
    16. public int Coins{ get; set;}
    17. }

    然后是冒险、当铺和医院,为了方便示例,我们把它们都简化成静态类:

    public static class Adventure
    {
        public static string Explore(int mapId)
        {
            Console.WriteLine ("Explore the map " + mapId);
            Player.GetInstance ().HP -= 10;
            return "stone";
        }
    }
    
    public static class PawnShop
    {
        public static void Sell(string stuffName)
        {
            Console.WriteLine ("Sell the stuff "+ stuffName);
            Player.GetInstance ().Coins += 10;
        }
    }
    
    public static class Hospital
    {
        public static void Heal()
        {
            Player player = Player.GetInstance ();
            if (player.Coins >= 20) {
                Console.WriteLine ("Player is recovered");
                Player.GetInstance ().Coins -= 20;
                Player.GetInstance ().HP += 50;
            }
        }
    }
    

    那么如何完成一个挂机的接口?这里我们使用外观模式:

    public static class HookFascade
    {
        public static void StartToHook(int mapIdStart, int mapIdEnd)
        {
            if (mapIdStart > mapIdEnd) {
                return;
            }
            for (int i = mapIdStart; i <= mapIdEnd; i++) {
                string drop = Adventure.Explore (i);
                PawnShop.Sell (drop);
                if (Player.GetInstance ().HP <= 30) {
                    Hospital.Heal ();
                }
            }
        }
    }
    

    这样我们就完成了一个挂机的功能,使用:

    HookFascade.StartToHook (1, 10);
    

    其实我们只做了一件事情,就是把本来写在客户端里的挂机逻辑整合成了一个类的方法。但是使得具体的挂机逻辑对用户透明,实现了用户与Player、Adventure等类之间的松耦合关系。当我们调整挂机逻辑的时候(例如增加了一个召唤兽,或者有些掉落要卖掉而有些不卖),就不需要修改用户的代码。当然缺点也很明显,就是限制了用户的发挥空间。

    —————————————————————————————————————————————————————————
    作者:凯奥斯
    来源:CSDN
    原文:https://blog.csdn.net/ecidevilin/article/details/52710658
    版权声明:本文为博主原创文章,转载请附上博文链接!