表面模式:简化复杂体系的优雅办理方案

[复制链接]
发表于 2025-6-9 07:05:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
1. 表面模式概述

表面模式(Facade Pattern)是一种布局型筹划模式,它为子体系中的一组接口提供一个统一的高层接口,使子体系更加容易利用。表面模式定义了一个高层接口,这个接口使得子体系更加容易利用,从而简化了客户端与复杂子体系之间的交互。
表面模式的核心头脑是:通过为复杂的子体系提供一个简朴的接口,来低落客户端和子体系之间的耦合度,使得子体系对客户端透明。
2. 模式布局

表面模式主要包含以下几个核心角色:
     

  • 表面(Facade):为客户端提供一个统一的高层接口,封装了对子体系的访问,并且知道怎样将客户端的请求转发给适当的子体系对象
  • 子体系(Subsystem):实现了子体系的功能,并处置惩罚由表面临象分配的任务,但不需要知道表面的存在
  • 客户端(Client):通过表面接口访问子体系,而不是直接访问子体系中的对象
3. 表面模式与适配器模式的区别

固然表面模式和适配器模式都是包装模式(封装一些既有的类),但它们的意图和应用场景有明显的区别:
特性表面模式适配器模式主要意图为复杂子体系提供简朴接口使不兼容的接口可以或许一起工作接口改变创建新的便利接口转换已有接口以匹配客户端盼望包装对象包装多个干系对象通常包装单个对象复杂度简化复杂体系办理接口不匹配问题应用机遇子体系筹划之初或重构时利用已有组件但接口不匹配时 表面模式侧重于简化,而适配器模式侧重于兼容。表面模式通常在筹划之初就思量,而适配器模式则常用于办理已有体系的兼容问题。
4. C#代码示例

4.1 家庭影院体系

下面是一个利用表面模式简化家庭影院体系操作的例子:
  1. using System;
  2. // 子系统类 - 投影仪
  3. public class Projector
  4. {
  5.     public void TurnOn()
  6.     {
  7.         Console.WriteLine("投影仪打开");
  8.     }
  9.    
  10.     public void TurnOff()
  11.     {
  12.         Console.WriteLine("投影仪关闭");
  13.     }
  14.    
  15.     public void SetInput(string input)
  16.     {
  17.         Console.WriteLine($"投影仪输入源设置为: {input}");
  18.     }
  19. }
  20. // 子系统类 - 音响系统
  21. public class SoundSystem
  22. {
  23.     public void TurnOn()
  24.     {
  25.         Console.WriteLine("音响系统打开");
  26.     }
  27.    
  28.     public void TurnOff()
  29.     {
  30.         Console.WriteLine("音响系统关闭");
  31.     }
  32.    
  33.     public void SetVolume(int level)
  34.     {
  35.         Console.WriteLine($"音响音量设置为: {level}");
  36.     }
  37.    
  38.     public void SetSurroundSound()
  39.     {
  40.         Console.WriteLine("音响设置为环绕声模式");
  41.     }
  42. }
  43. // 子系统类 - 播放器
  44. public class StreamingPlayer
  45. {
  46.     public void TurnOn()
  47.     {
  48.         Console.WriteLine("播放器打开");
  49.     }
  50.    
  51.     public void TurnOff()
  52.     {
  53.         Console.WriteLine("播放器关闭");
  54.     }
  55.    
  56.     public void Play(string movie)
  57.     {
  58.         Console.WriteLine($"播放器开始播放: {movie}");
  59.     }
  60.    
  61.     public void Stop()
  62.     {
  63.         Console.WriteLine("播放器停止播放");
  64.     }
  65. }
  66. // 子系统类 - 灯光
  67. public class Lights
  68. {
  69.     public void TurnOn()
  70.     {
  71.         Console.WriteLine("灯光打开");
  72.     }
  73.    
  74.     public void TurnOff()
  75.     {
  76.         Console.WriteLine("灯光关闭");
  77.     }
  78.    
  79.     public void Dim(int level)
  80.     {
  81.         Console.WriteLine($"灯光调暗至: {level}%");
  82.     }
  83. }
  84. // 子系统类 - 自动窗帘
  85. public class Curtains
  86. {
  87.     public void Open()
  88.     {
  89.         Console.WriteLine("窗帘打开");
  90.     }
  91.    
  92.     public void Close()
  93.     {
  94.         Console.WriteLine("窗帘关闭");
  95.     }
  96. }
  97. // 外观类 - 家庭影院外观
  98. public class HomeTheaterFacade
  99. {
  100.     // 引用子系统组件
  101.     private Projector projector;
  102.     private SoundSystem soundSystem;
  103.     private StreamingPlayer player;
  104.     private Lights lights;
  105.     private Curtains curtains;
  106.    
  107.     // 构造函数,注入所有子系统组件
  108.     public HomeTheaterFacade(Projector projector, SoundSystem soundSystem,
  109.                             StreamingPlayer player, Lights lights, Curtains curtains)
  110.     {
  111.         this.projector = projector;
  112.         this.soundSystem = soundSystem;
  113.         this.player = player;
  114.         this.lights = lights;
  115.         this.curtains = curtains;
  116.     }
  117.    
  118.     // 外观方法 - 观看电影
  119.     // 这个方法封装了观看电影所需的一系列复杂操作
  120.     public void WatchMovie(string movie)
  121.     {
  122.         Console.WriteLine("\n=== 准备观看电影 ===");
  123.         
  124.         // 按照特定顺序调用各个子系统
  125.         curtains.Close();         // 关闭窗帘
  126.         lights.Dim(10);           // 将灯光调暗至10%
  127.         projector.TurnOn();       // 打开投影仪
  128.         projector.SetInput("HDMI");// 设置投影仪输入源
  129.         soundSystem.TurnOn();     // 打开音响系统
  130.         soundSystem.SetSurroundSound(); // 设置环绕声
  131.         soundSystem.SetVolume(5); // 设置适当音量
  132.         player.TurnOn();          // 打开播放器
  133.         player.Play(movie);       // 播放指定电影
  134.         
  135.         Console.WriteLine($"=== 现在播放: {movie} ===");
  136.     }
  137.    
  138.     // 外观方法 - 结束电影
  139.     // 这个方法封装了结束电影时的一系列操作
  140.     public void EndMovie()
  141.     {
  142.         Console.WriteLine("\n=== 结束电影 ===");
  143.         
  144.         // 按照特定顺序调用各个子系统
  145.         player.Stop();            // 停止播放
  146.         player.TurnOff();         // 关闭播放器
  147.         soundSystem.TurnOff();    // 关闭音响系统
  148.         projector.TurnOff();      // 关闭投影仪
  149.         lights.TurnOn();          // 打开灯光
  150.         curtains.Open();          // 打开窗帘
  151.         
  152.         Console.WriteLine("=== 影院系统已关闭 ===");
  153.     }
  154. }
  155. // 客户端代码
  156. public class Client
  157. {
  158.     public static void Main(string[] args)
  159.     {
  160.         // 创建子系统对象
  161.         Projector projector = new Projector();
  162.         SoundSystem soundSystem = new SoundSystem();
  163.         StreamingPlayer player = new StreamingPlayer();
  164.         Lights lights = new Lights();
  165.         Curtains curtains = new Curtains();
  166.         
  167.         // 创建外观对象
  168.         HomeTheaterFacade homeTheater =
  169.             new HomeTheaterFacade(projector, soundSystem, player, lights, curtains);
  170.         
  171.         // 使用外观简化的接口观看电影
  172.         homeTheater.WatchMovie("星际穿越");
  173.         
  174.         Console.WriteLine("\n...[电影播放中]...\n");
  175.         
  176.         // 使用外观简化的接口结束电影
  177.         homeTheater.EndMovie();
  178.         
  179.         // 对比:如果没有外观模式,客户端代码会变成这样
  180.         Console.WriteLine("\n=== 不使用外观模式时的客户端代码 ===");
  181.         Console.WriteLine("curtains.Close();");
  182.         Console.WriteLine("lights.Dim(10);");
  183.         Console.WriteLine("projector.TurnOn();");
  184.         Console.WriteLine("projector.SetInput("HDMI");");
  185.         Console.WriteLine("soundSystem.TurnOn();");
  186.         Console.WriteLine("soundSystem.SetSurroundSound();");
  187.         Console.WriteLine("soundSystem.SetVolume(5);");
  188.         Console.WriteLine("player.TurnOn();");
  189.         Console.WriteLine("player.Play("星际穿越");");
  190.         // ...以及更多代码...
  191.     }
  192. }
复制代码
4.2 复杂计算机体系启动示例

下面是另一个利用表面模式简化计算机体系启动过程的例子:
  1. using System;
  2. // 子系统类 - CPU
  3. public class CPU
  4. {
  5.     public void Freeze()
  6.     {
  7.         Console.WriteLine("CPU: 冻结所有CPU运行");
  8.     }
  9.    
  10.     public void Jump(long position)
  11.     {
  12.         Console.WriteLine($"CPU: 跳转到内存地址 {position}");
  13.     }
  14.    
  15.     public void Execute()
  16.     {
  17.         Console.WriteLine("CPU: 开始执行指令");
  18.     }
  19. }
  20. // 子系统类 - 内存
  21. public class Memory
  22. {
  23.     public void Load(long position, string data)
  24.     {
  25.         Console.WriteLine($"内存: 载入数据到地址 {position} -> 数据: {data}");
  26.     }
  27. }
  28. // 子系统类 - 硬盘
  29. public class HardDrive
  30. {
  31.     public string Read(long lba, int size)
  32.     {
  33.         Console.WriteLine($"硬盘: 从扇区 {lba} 读取 {size} 字节");
  34.         return "操作系统数据...";
  35.     }
  36. }
  37. // 外观类 - 计算机
  38. public class ComputerFacade
  39. {
  40.     // 引用子系统组件
  41.     private CPU cpu;
  42.     private Memory memory;
  43.     private HardDrive hardDrive;
  44.    
  45.     // 构造函数
  46.     public ComputerFacade()
  47.     {
  48.         // 创建子系统组件实例
  49.         this.cpu = new CPU();
  50.         this.memory = new Memory();
  51.         this.hardDrive = new HardDrive();
  52.     }
  53.    
  54.     // 外观方法 - 启动计算机
  55.     public void Start()
  56.     {
  57.         Console.WriteLine("=== 计算机启动过程开始 ===");
  58.         
  59.         // 按照启动计算机的特定步骤调用子系统
  60.         cpu.Freeze(); // 冻结CPU
  61.         
  62.         // 从硬盘读取引导程序
  63.         string bootData = hardDrive.Read(0, 512);
  64.         
  65.         // 将引导程序载入内存
  66.         memory.Load(0, bootData);
  67.         
  68.         // CPU跳转到引导程序位置
  69.         cpu.Jump(0);
  70.         
  71.         // 开始执行引导程序
  72.         cpu.Execute();
  73.         
  74.         Console.WriteLine("=== 计算机启动完成 ===");
  75.     }
  76. }
  77. // 客户端代码
  78. public class ComputerClient
  79. {
  80.     public static void Main(string[] args)
  81.     {
  82.         // 创建并使用计算机外观
  83.         ComputerFacade computer = new ComputerFacade();
  84.         computer.Start();
  85.         
  86.         // 如果没有外观模式,客户端代码会更复杂
  87.         Console.WriteLine("\n=== 不使用外观模式的客户端代码 ===");
  88.         Console.WriteLine("CPU cpu = new CPU();");
  89.         Console.WriteLine("Memory memory = new Memory();");
  90.         Console.WriteLine("HardDrive hardDrive = new HardDrive();");
  91.         Console.WriteLine("cpu.Freeze();");
  92.         Console.WriteLine("String bootData = hardDrive.Read(0, 512);");
  93.         Console.WriteLine("memory.Load(0, bootData);");
  94.         Console.WriteLine("cpu.Jump(0);");
  95.         Console.WriteLine("cpu.Execute();");
  96.     }
  97. }
复制代码
5. 表面模式的实现方式

实现表面模式通常依照以下步骤:

  • 辨认子体系组件:确定需要简化的复杂子体系,以及子体系中需要暴露给客户端的功能
  • 筹划表面接口:筹划一个高层接口,提供客户端需要的简化功能
  • 实现表面类:创建表面类,组合所需的子体系组件,并在表面方法中和谐它们的工作
  • 客户端利用表面:客户端通过表面类访问子体系功能,而不是直接与各个子体系组件交互
实现表面模式的几种变体:

5.1 简朴表面

最基本的情势,表面类直接创建和管理子体系组件。
  1. public class SimpleFacade
  2. {
  3.     private SubsystemA subsystemA;
  4.     private SubsystemB subsystemB;
  5.    
  6.     public SimpleFacade()
  7.     {
  8.         this.subsystemA = new SubsystemA();
  9.         this.subsystemB = new SubsystemB();
  10.     }
  11.    
  12.     public void Operation()
  13.     {
  14.         subsystemA.OperationA();
  15.         subsystemB.OperationB();
  16.     }
  17. }
复制代码
5.2 依赖注入表面

通过构造函数注入子体系组件,进步可测试性和灵活性。
  1. public class DIFacade
  2. {
  3.     private ISubsystemA subsystemA;
  4.     private ISubsystemB subsystemB;
  5.    
  6.     public DIFacade(ISubsystemA subsystemA, ISubsystemB subsystemB)
  7.     {
  8.         this.subsystemA = subsystemA;
  9.         this.subsystemB = subsystemB;
  10.     }
  11.    
  12.     public void Operation()
  13.     {
  14.         subsystemA.OperationA();
  15.         subsystemB.OperationB();
  16.     }
  17. }
复制代码
5.3 单例表面

确保整个体系只有一个表面实例。
  1. public class SingletonFacade
  2. {
  3.     private static SingletonFacade instance;
  4.     private SubsystemA subsystemA;
  5.     private SubsystemB subsystemB;
  6.    
  7.     private SingletonFacade()
  8.     {
  9.         this.subsystemA = new SubsystemA();
  10.         this.subsystemB = new SubsystemB();
  11.     }
  12.    
  13.     public static SingletonFacade Instance
  14.     {
  15.         get
  16.         {
  17.             if (instance == null)
  18.             {
  19.                 instance = new SingletonFacade();
  20.             }
  21.             return instance;
  22.         }
  23.     }
  24.    
  25.     public void Operation()
  26.     {
  27.         subsystemA.OperationA();
  28.         subsystemB.OperationB();
  29.     }
  30. }
复制代码
6. 表面模式的应用场景

表面模式适用于以下场景:
6.1 复杂子体系的简化接口

当你需要为复杂的子体系提供一个简朴的接口时,表面模式非常有效。比方,上面的家庭影院和计算机启动示例。
6.2 分层筹划

在分层架构中,每一层都可以利用表面模式来提供对下一层的简化访问。比方,在典型的三层架构(表现层、业务逻辑层、数据访问层)中,每一层可以为其上层提供一个表面。
  1. // 数据访问层外观
  2. public class DataAccessFacade
  3. {
  4.     private UserRepository userRepo;
  5.     private OrderRepository orderRepo;
  6.     private ProductRepository productRepo;
  7.    
  8.     public DataAccessFacade()
  9.     {
  10.         userRepo = new UserRepository();
  11.         orderRepo = new OrderRepository();
  12.         productRepo = new ProductRepository();
  13.     }
  14.    
  15.     // 提供给业务逻辑层的简化方法
  16.     public User GetUser(int userId) { return userRepo.GetById(userId); }
  17.     public Order GetOrder(int orderId) { return orderRepo.GetById(orderId); }
  18.     public List<Product> GetUserPurchasedProducts(int userId)
  19.     {
  20.         List<Product> products = new List<Product>();
  21.         List<Order> orders = orderRepo.GetByUserId(userId);
  22.         
  23.         foreach (Order order in orders)
  24.         {
  25.             foreach (int productId in order.ProductIds)
  26.             {
  27.                 products.Add(productRepo.GetById(productId));
  28.             }
  29.         }
  30.         
  31.         return products;
  32.     }
  33. }
  34. // 业务逻辑层外观
  35. public class BusinessLogicFacade
  36. {
  37.     private DataAccessFacade dataAccess;
  38.     private DiscountCalculator discountCalculator;
  39.     private TaxCalculator taxCalculator;
  40.    
  41.     public BusinessLogicFacade()
  42.     {
  43.         dataAccess = new DataAccessFacade();
  44.         discountCalculator = new DiscountCalculator();
  45.         taxCalculator = new TaxCalculator();
  46.     }
  47.    
  48.     // 提供给表示层的简化方法
  49.     public decimal CalculateOrderTotal(int orderId)
  50.     {
  51.         Order order = dataAccess.GetOrder(orderId);
  52.         decimal subtotal = order.Subtotal;
  53.         decimal discount = discountCalculator.CalculateDiscount(order);
  54.         decimal tax = taxCalculator.CalculateTax(subtotal - discount);
  55.         
  56.         return subtotal - discount + tax;
  57.     }
  58. }
复制代码
6.3 第三方库的封装

利用表面模式封装复杂的第三方库或API,可以简化调用并隔离体系与外部依赖的变化。
  1. // 第三方支付库的外观
  2. public class PaymentFacade
  3. {
  4.     private ThirdPartyPaymentProcessor processor;
  5.     private PaymentValidator validator;
  6.     private TransactionLogger logger;
  7.    
  8.     public PaymentFacade()
  9.     {
  10.         processor = new ThirdPartyPaymentProcessor();
  11.         validator = new PaymentValidator();
  12.         logger = new TransactionLogger();
  13.     }
  14.    
  15.     // 简化的支付方法
  16.     public bool ProcessPayment(string cardNumber, decimal amount)
  17.     {
  18.         // 验证支付信息
  19.         if (!validator.ValidateCard(cardNumber))
  20.         {
  21.             return false;
  22.         }
  23.         
  24.         // 记录交易开始
  25.         string transactionId = logger.LogTransactionStart(cardNumber, amount);
  26.         
  27.         try
  28.         {
  29.             // 调用第三方处理器
  30.             bool result = processor.Authorize(cardNumber, amount);
  31.             if (result)
  32.             {
  33.                 result = processor.Capture(cardNumber, amount);
  34.             }
  35.             
  36.             // 记录交易结果
  37.             logger.LogTransactionResult(transactionId, result);
  38.             
  39.             return result;
  40.         }
  41.         catch (Exception ex)
  42.         {
  43.             // 记录错误
  44.             logger.LogError(transactionId, ex.Message);
  45.             return false;
  46.         }
  47.     }
  48. }
复制代码
6.4 遗留体系的现代接口

表面模式可以为老旧、复杂的体系提供一个更现代、更简便的接口。
  1. // 遗留系统的外观
  2. public class LegacySystemFacade
  3. {
  4.     private LegacyDatabase database;
  5.     private LegacyAuthenticator authenticator;
  6.     private LegacyFileSystem fileSystem;
  7.    
  8.     public LegacySystemFacade()
  9.     {
  10.         database = new LegacyDatabase();
  11.         authenticator = new LegacyAuthenticator();
  12.         fileSystem = new LegacyFileSystem();
  13.     }
  14.    
  15.     // 现代化的用户认证方法
  16.     public bool Authenticate(string username, string password)
  17.     {
  18.         // 旧系统需要先连接数据库
  19.         database.Connect("localhost", "admin", "password");
  20.         
  21.         // 查询用户记录
  22.         string userRecord = database.ExecuteQuery("SELECT * FROM USERS WHERE USERNAME = '" + username + "'");
  23.         
  24.         // 认证用户
  25.         return authenticator.CheckCredentials(userRecord, password);
  26.     }
  27.    
  28.     // 现代化的文件处理方法
  29.     public string GetDocument(string documentId)
  30.     {
  31.         // 建立文件系统连接
  32.         fileSystem.ConnectToServer();
  33.         
  34.         // 旧系统复杂的文件读取逻辑
  35.         string filePath = fileSystem.TranslateDocumentIdToPath(documentId);
  36.         byte[] fileContents = fileSystem.ReadFileBytes(filePath);
  37.         string documentText = System.Text.Encoding.UTF8.GetString(fileContents);
  38.         
  39.         // 断开连接
  40.         fileSystem.DisconnectFromServer();
  41.         
  42.         return documentText;
  43.     }
  44. }
复制代码
7. 表面模式的优缺点

7.1 优点


  • 隔离客户端与子体系:客户端不需要了解复杂子体系的内部工作原理。
  • 疏松耦合:淘汰客户端代码和子体系之间的依赖关系。
  • 简化客户端代码:客户端利用简朴的表面接口,而不是直接处置惩罚多个子体系组件。
  • 分层筹划支持:可以为每一层提供统一的入口点,使架构更加清晰。
  • 封装变化:当子体系变化时,只需修改表面类,而不影响客户端代码。
7.2 缺点


  • 可能引入巨大的表面类:如果表面类承担过多责任,可能会变得过于巨大和复杂。
  • 有时会低落灵活性:可能会限定对子体系的精致控制,因为表面只提供了有限的接口。
  • 可能违反开闭原则:当需要添加新的子体系功能时,可能需要修改表面类。
  • 可能产生多余的间接调用:如果客户端只需利用子体系的一小部分功能,通过表面可能会引入不必要的性能开销。
8. 表面模式与其他筹划模式的关系

筹划模式关系形貌适配器模式适配器将一个类的接口转换为客户端盼望的另一个接口,而表面提供了一个简化的接口。适配器办理接口不匹配问题,表面办理复杂性问题。中介者模式两者都旨在处置惩罚多个类之间的关系,但表面简化了客户端与子体系之间的交互,而中介者会合管理同事对象之间的交互。署理模式署理控制对对象的访问,而表面简化对子体系的访问。署理通常只包装一个对象,而表面可能包装多个子体系组件。单例模式表面类通常利用单例模式实现,确保体系中只有一个表面实例。 9. 现实项目中的表面模式应用

9.1 Web API的服务层

在Web API开发中,服务层可以作为控制器和仓储层之间的表面:
  1. // 仓储层
  2. public interface IUserRepository
  3. {
  4.     User GetById(int id);
  5.     void Save(User user);
  6. }
  7. public interface IOrderRepository
  8. {
  9.     Order GetById(int id);
  10.     List<Order> GetByUserId(int userId);
  11.     void Save(Order order);
  12. }
  13. // 服务层(外观)
  14. public class UserService
  15. {
  16.     private readonly IUserRepository userRepository;
  17.     private readonly IOrderRepository orderRepository;
  18.     private readonly IEmailService emailService;
  19.     private readonly IAuthenticationService authService;
  20.    
  21.     public UserService(
  22.         IUserRepository userRepository,
  23.         IOrderRepository orderRepository,
  24.         IEmailService emailService,
  25.         IAuthenticationService authService)
  26.     {
  27.         this.userRepository = userRepository;
  28.         this.orderRepository = orderRepository;
  29.         this.emailService = emailService;
  30.         this.authService = authService;
  31.     }
  32.    
  33.     // 简化的用户注册流程
  34.     public bool RegisterUser(string username, string email, string password)
  35.     {
  36.         // 检查用户名是否已存在
  37.         if (authService.IsUsernameTaken(username))
  38.             return false;
  39.         
  40.         // 创建用户账户
  41.         var user = new User { Username = username, Email = email };
  42.         authService.SetPassword(user, password);
  43.         
  44.         // 保存用户
  45.         userRepository.Save(user);
  46.         
  47.         // 发送欢迎邮件
  48.         emailService.SendWelcomeEmail(email, username);
  49.         
  50.         return true;
  51.     }
  52.    
  53.     // 简化的订单历史查询
  54.     public List<OrderSummary> GetUserOrderHistory(int userId)
  55.     {
  56.         User user = userRepository.GetById(userId);
  57.         if (user == null)
  58.             return new List<OrderSummary>();
  59.             
  60.         List<Order> orders = orderRepository.GetByUserId(userId);
  61.         
  62.         // 转换为展示模型
  63.         return orders.Select(o => new OrderSummary
  64.         {
  65.             Id = o.Id,
  66.             Date = o.Date,
  67.             Amount = o.TotalAmount,
  68.             Status = o.Status
  69.         }).ToList();
  70.     }
  71. }
  72. // API控制器
  73. public class UserController : ApiController
  74. {
  75.     private readonly UserService userService;
  76.    
  77.     public UserController(UserService userService)
  78.     {
  79.         this.userService = userService;
  80.     }
  81.    
  82.     [HttpPost]
  83.     public IActionResult Register([FromBody] RegisterModel model)
  84.     {
  85.         // 控制器只需调用外观的简单方法
  86.         bool success = userService.RegisterUser(
  87.             model.Username, model.Email, model.Password);
  88.             
  89.         if (success)
  90.             return Ok();
  91.         else
  92.             return BadRequest("用户名已存在");
  93.     }
  94.    
  95.     [HttpGet("{userId}/orders")]
  96.     public IActionResult GetOrders(int userId)
  97.     {
  98.         // 控制器通过外观获取订单历史
  99.         var orders = userService.GetUserOrderHistory(userId);
  100.         return Ok(orders);
  101.     }
  102. }
复制代码
9.2 跨平台应用步伐

表面模式可用于隔离平台特定的API,提供一个统一的跨平台接口:
  1. // 平台特定的文件系统实现
  2. public interface IFileSystem
  3. {
  4.     bool FileExists(string path);
  5.     string ReadAllText(string path);
  6.     void WriteAllText(string path, string contents);
  7. }
  8. // Windows实现
  9. public class WindowsFileSystem : IFileSystem
  10. {
  11.     public bool FileExists(string path)
  12.     {
  13.         return System.IO.File.Exists(path);
  14.     }
  15.    
  16.     public string ReadAllText(string path)
  17.     {
  18.         return System.IO.File.ReadAllText(path);
  19.     }
  20.    
  21.     public void WriteAllText(string path, string contents)
  22.     {
  23.         System.IO.File.WriteAllText(path, contents);
  24.     }
  25. }
  26. // Android实现
  27. public class AndroidFileSystem : IFileSystem
  28. {
  29.     private Context context;
  30.    
  31.     public AndroidFileSystem(Context context)
  32.     {
  33.         this.context = context;
  34.     }
  35.    
  36.     public bool FileExists(string filename)
  37.     {
  38.         try {
  39.             context.OpenFileInput(filename);
  40.             return true;
  41.         } catch (Exception) {
  42.             return false;
  43.         }
  44.     }
  45.    
  46.     public string ReadAllText(string filename)
  47.     {
  48.         using (var inputStream = context.OpenFileInput(filename))
  49.         using (var reader = new BufferedReader(new InputStreamReader(inputStream)))
  50.         {
  51.             StringBuilder content = new StringBuilder();
  52.             string line;
  53.             while ((line = reader.ReadLine()) != null)
  54.             {
  55.                 content.AppendLine(line);
  56.             }
  57.             return content.ToString();
  58.         }
  59.     }
  60.    
  61.     public void WriteAllText(string filename, string contents)
  62.     {
  63.         using (var outputStream = context.OpenFileOutput(filename, FileMode.Private))
  64.         using (var writer = new PrintWriter(outputStream))
  65.         {
  66.             writer.Write(contents);
  67.         }
  68.     }
  69. }
  70. // 外观类
  71. public class StorageService
  72. {
  73.     private readonly IFileSystem fileSystem;
  74.    
  75.     public StorageService(IFileSystem fileSystem)
  76.     {
  77.         this.fileSystem = fileSystem;
  78.     }
  79.    
  80.     // 高层方法:保存用户设置
  81.     public bool SaveSettings(Dictionary<string, string> settings)
  82.     {
  83.         try
  84.         {
  85.             // 序列化设置
  86.             string json = JsonSerializer.Serialize(settings);
  87.             
  88.             // 保存到文件
  89.             fileSystem.WriteAllText("settings.json", json);
  90.             return true;
  91.         }
  92.         catch (Exception)
  93.         {
  94.             return false;
  95.         }
  96.     }
  97.    
  98.     // 高层方法:加载用户设置
  99.     public Dictionary<string, string> LoadSettings()
  100.     {
  101.         try
  102.         {
  103.             // 检查文件是否存在
  104.             if (!fileSystem.FileExists("settings.json"))
  105.                 return new Dictionary<string, string>();
  106.                
  107.             // 读取并反序列化
  108.             string json = fileSystem.ReadAllText("settings.json");
  109.             return JsonSerializer.Deserialize<Dictionary<string, string>>(json);
  110.         }
  111.         catch (Exception)
  112.         {
  113.             return new Dictionary<string, string>();
  114.         }
  115.     }
  116. }
  117. // 客户端代码
  118. public class SettingsScreen
  119. {
  120.     private StorageService storageService;
  121.    
  122.     public SettingsScreen(StorageService storageService)
  123.     {
  124.         this.storageService = storageService;
  125.     }
  126.    
  127.     public void SaveUserPreferences(string theme, string language)
  128.     {
  129.         var settings = new Dictionary<string, string>
  130.         {
  131.             ["theme"] = theme,
  132.             ["language"] = language
  133.         };
  134.         
  135.         // 使用外观简化存储操作
  136.         if (storageService.SaveSettings(settings))
  137.             Console.WriteLine("设置已保存");
  138.         else
  139.             Console.WriteLine("保存设置失败");
  140.     }
  141.    
  142.     public void LoadUserPreferences()
  143.     {
  144.         // 使用外观简化加载操作
  145.         var settings = storageService.LoadSettings();
  146.         
  147.         string theme = settings.ContainsKey("theme") ? settings["theme"] : "default";
  148.         string language = settings.ContainsKey("language") ? settings["language"] : "en";
  149.         
  150.         Console.WriteLine($"加载设置: 主题={theme}, 语言={language}");
  151.     }
  152. }
复制代码
10. 表面模式的最佳实践


  • 保持表面简朴:表面类应该提供一个简朴、统一的接口,避免引入太多复杂性。
  • 不要强制利用表面:表面应该是可选的,客户端应该可以根据需要直接访问子体系组件。
  • 适当分解大型表面:如果一个表面变得过于巨大,思量将其分解为多个干系的表面类。
  • 利用依赖注入:通过构造函数注入子体系组件,而不是在表面内部直接创建它们,这进步了代码的可测试性和灵活性。
  • 思量接口而不是详细类:表面应该依赖于抽象接口而不是详细实现,这样可以更容易地更换子体系组件。
  • 不要过度利用:并非所有子体系都需要表面。当子体系充足简朴时,直接利用可能更清晰。
11. 总结

表面模式是一种强大的布局型筹划模式,它通过为复杂子体系提供一个简化的高层接口,使客户端代码更加简便和易于利用。表面模式不仅可以低落体系的复杂度,还可以淘汰客户端和子体系之间的耦合,进步代码的可维护性和灵活性。
表面模式的核心上风在于:

  • 简化了客户端对复杂子体系的利用
  • 促进了松耦合的体系筹划
  • 有助于分层架构的实现
  • 提供了隔离变化的机制
在现实开发中,表面模式常用于体系分层、API筹划、遗留体系集成和跨平台开发等场景。它与其他筹划模式(如适配器、中介者、署理等)有明显的区别,并且通常与这些模式配合利用,以构建更加灵活和可维护的软件体系。
当我们面临复杂子体系,或者希望为客户端提供一个统一、简便的接口时,应该思量利用表面模式。但同时也要注意避免过度利用,以免引入不必要的抽象层。
学习资源


  • Design Patterns: Elements of Reusable Object-Oriented Software - GoF经典著作
  • Head First Design Patterns - 生动易懂的筹划模式入门册本
  • Refactoring.Guru - Facade Pattern
  • C# Design Pattern Essentials
  • Microsoft Learn - Design Patterns

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表