马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
1. 表面模式概述
表面模式(Facade Pattern)是一种布局型筹划模式,它为子体系中的一组接口提供一个统一的高层接口,使子体系更加容易利用。表面模式定义了一个高层接口,这个接口使得子体系更加容易利用,从而简化了客户端与复杂子体系之间的交互。
表面模式的核心头脑是:通过为复杂的子体系提供一个简朴的接口,来低落客户端和子体系之间的耦合度,使得子体系对客户端透明。
2. 模式布局
表面模式主要包含以下几个核心角色:
- 表面(Facade):为客户端提供一个统一的高层接口,封装了对子体系的访问,并且知道怎样将客户端的请求转发给适当的子体系对象
- 子体系(Subsystem):实现了子体系的功能,并处置惩罚由表面临象分配的任务,但不需要知道表面的存在
- 客户端(Client):通过表面接口访问子体系,而不是直接访问子体系中的对象
3. 表面模式与适配器模式的区别
固然表面模式和适配器模式都是包装模式(封装一些既有的类),但它们的意图和应用场景有明显的区别:
特性表面模式适配器模式主要意图为复杂子体系提供简朴接口使不兼容的接口可以或许一起工作接口改变创建新的便利接口转换已有接口以匹配客户端盼望包装对象包装多个干系对象通常包装单个对象复杂度简化复杂体系办理接口不匹配问题应用机遇子体系筹划之初或重构时利用已有组件但接口不匹配时 表面模式侧重于简化,而适配器模式侧重于兼容。表面模式通常在筹划之初就思量,而适配器模式则常用于办理已有体系的兼容问题。
4. C#代码示例
4.1 家庭影院体系
下面是一个利用表面模式简化家庭影院体系操作的例子:
4.2 复杂计算机体系启动示例
下面是另一个利用表面模式简化计算机体系启动过程的例子:
5. 表面模式的实现方式
实现表面模式通常依照以下步骤:
- 辨认子体系组件:确定需要简化的复杂子体系,以及子体系中需要暴露给客户端的功能
- 筹划表面接口:筹划一个高层接口,提供客户端需要的简化功能
- 实现表面类:创建表面类,组合所需的子体系组件,并在表面方法中和谐它们的工作
- 客户端利用表面:客户端通过表面类访问子体系功能,而不是直接与各个子体系组件交互
实现表面模式的几种变体:
5.1 简朴表面
最基本的情势,表面类直接创建和管理子体系组件。
- public class SimpleFacade
- {
- private SubsystemA subsystemA;
- private SubsystemB subsystemB;
-
- public SimpleFacade()
- {
- this.subsystemA = new SubsystemA();
- this.subsystemB = new SubsystemB();
- }
-
- public void Operation()
- {
- subsystemA.OperationA();
- subsystemB.OperationB();
- }
- }
复制代码 5.2 依赖注入表面
通过构造函数注入子体系组件,进步可测试性和灵活性。
- public class DIFacade
- {
- private ISubsystemA subsystemA;
- private ISubsystemB subsystemB;
-
- public DIFacade(ISubsystemA subsystemA, ISubsystemB subsystemB)
- {
- this.subsystemA = subsystemA;
- this.subsystemB = subsystemB;
- }
-
- public void Operation()
- {
- subsystemA.OperationA();
- subsystemB.OperationB();
- }
- }
复制代码 5.3 单例表面
确保整个体系只有一个表面实例。
- public class SingletonFacade
- {
- private static SingletonFacade instance;
- private SubsystemA subsystemA;
- private SubsystemB subsystemB;
-
- private SingletonFacade()
- {
- this.subsystemA = new SubsystemA();
- this.subsystemB = new SubsystemB();
- }
-
- public static SingletonFacade Instance
- {
- get
- {
- if (instance == null)
- {
- instance = new SingletonFacade();
- }
- return instance;
- }
- }
-
- public void Operation()
- {
- subsystemA.OperationA();
- subsystemB.OperationB();
- }
- }
复制代码 6. 表面模式的应用场景
表面模式适用于以下场景:
6.1 复杂子体系的简化接口
当你需要为复杂的子体系提供一个简朴的接口时,表面模式非常有效。比方,上面的家庭影院和计算机启动示例。
6.2 分层筹划
在分层架构中,每一层都可以利用表面模式来提供对下一层的简化访问。比方,在典型的三层架构(表现层、业务逻辑层、数据访问层)中,每一层可以为其上层提供一个表面。
- // 数据访问层外观
- public class DataAccessFacade
- {
- private UserRepository userRepo;
- private OrderRepository orderRepo;
- private ProductRepository productRepo;
-
- public DataAccessFacade()
- {
- userRepo = new UserRepository();
- orderRepo = new OrderRepository();
- productRepo = new ProductRepository();
- }
-
- // 提供给业务逻辑层的简化方法
- public User GetUser(int userId) { return userRepo.GetById(userId); }
- public Order GetOrder(int orderId) { return orderRepo.GetById(orderId); }
- public List<Product> GetUserPurchasedProducts(int userId)
- {
- List<Product> products = new List<Product>();
- List<Order> orders = orderRepo.GetByUserId(userId);
-
- foreach (Order order in orders)
- {
- foreach (int productId in order.ProductIds)
- {
- products.Add(productRepo.GetById(productId));
- }
- }
-
- return products;
- }
- }
- // 业务逻辑层外观
- public class BusinessLogicFacade
- {
- private DataAccessFacade dataAccess;
- private DiscountCalculator discountCalculator;
- private TaxCalculator taxCalculator;
-
- public BusinessLogicFacade()
- {
- dataAccess = new DataAccessFacade();
- discountCalculator = new DiscountCalculator();
- taxCalculator = new TaxCalculator();
- }
-
- // 提供给表示层的简化方法
- public decimal CalculateOrderTotal(int orderId)
- {
- Order order = dataAccess.GetOrder(orderId);
- decimal subtotal = order.Subtotal;
- decimal discount = discountCalculator.CalculateDiscount(order);
- decimal tax = taxCalculator.CalculateTax(subtotal - discount);
-
- return subtotal - discount + tax;
- }
- }
复制代码 6.3 第三方库的封装
利用表面模式封装复杂的第三方库或API,可以简化调用并隔离体系与外部依赖的变化。
- // 第三方支付库的外观
- public class PaymentFacade
- {
- private ThirdPartyPaymentProcessor processor;
- private PaymentValidator validator;
- private TransactionLogger logger;
-
- public PaymentFacade()
- {
- processor = new ThirdPartyPaymentProcessor();
- validator = new PaymentValidator();
- logger = new TransactionLogger();
- }
-
- // 简化的支付方法
- public bool ProcessPayment(string cardNumber, decimal amount)
- {
- // 验证支付信息
- if (!validator.ValidateCard(cardNumber))
- {
- return false;
- }
-
- // 记录交易开始
- string transactionId = logger.LogTransactionStart(cardNumber, amount);
-
- try
- {
- // 调用第三方处理器
- bool result = processor.Authorize(cardNumber, amount);
- if (result)
- {
- result = processor.Capture(cardNumber, amount);
- }
-
- // 记录交易结果
- logger.LogTransactionResult(transactionId, result);
-
- return result;
- }
- catch (Exception ex)
- {
- // 记录错误
- logger.LogError(transactionId, ex.Message);
- return false;
- }
- }
- }
复制代码 6.4 遗留体系的现代接口
表面模式可以为老旧、复杂的体系提供一个更现代、更简便的接口。
- // 遗留系统的外观
- public class LegacySystemFacade
- {
- private LegacyDatabase database;
- private LegacyAuthenticator authenticator;
- private LegacyFileSystem fileSystem;
-
- public LegacySystemFacade()
- {
- database = new LegacyDatabase();
- authenticator = new LegacyAuthenticator();
- fileSystem = new LegacyFileSystem();
- }
-
- // 现代化的用户认证方法
- public bool Authenticate(string username, string password)
- {
- // 旧系统需要先连接数据库
- database.Connect("localhost", "admin", "password");
-
- // 查询用户记录
- string userRecord = database.ExecuteQuery("SELECT * FROM USERS WHERE USERNAME = '" + username + "'");
-
- // 认证用户
- return authenticator.CheckCredentials(userRecord, password);
- }
-
- // 现代化的文件处理方法
- public string GetDocument(string documentId)
- {
- // 建立文件系统连接
- fileSystem.ConnectToServer();
-
- // 旧系统复杂的文件读取逻辑
- string filePath = fileSystem.TranslateDocumentIdToPath(documentId);
- byte[] fileContents = fileSystem.ReadFileBytes(filePath);
- string documentText = System.Text.Encoding.UTF8.GetString(fileContents);
-
- // 断开连接
- fileSystem.DisconnectFromServer();
-
- return documentText;
- }
- }
复制代码 7. 表面模式的优缺点
7.1 优点
- 隔离客户端与子体系:客户端不需要了解复杂子体系的内部工作原理。
- 疏松耦合:淘汰客户端代码和子体系之间的依赖关系。
- 简化客户端代码:客户端利用简朴的表面接口,而不是直接处置惩罚多个子体系组件。
- 分层筹划支持:可以为每一层提供统一的入口点,使架构更加清晰。
- 封装变化:当子体系变化时,只需修改表面类,而不影响客户端代码。
7.2 缺点
- 可能引入巨大的表面类:如果表面类承担过多责任,可能会变得过于巨大和复杂。
- 有时会低落灵活性:可能会限定对子体系的精致控制,因为表面只提供了有限的接口。
- 可能违反开闭原则:当需要添加新的子体系功能时,可能需要修改表面类。
- 可能产生多余的间接调用:如果客户端只需利用子体系的一小部分功能,通过表面可能会引入不必要的性能开销。
8. 表面模式与其他筹划模式的关系
筹划模式关系形貌适配器模式适配器将一个类的接口转换为客户端盼望的另一个接口,而表面提供了一个简化的接口。适配器办理接口不匹配问题,表面办理复杂性问题。中介者模式两者都旨在处置惩罚多个类之间的关系,但表面简化了客户端与子体系之间的交互,而中介者会合管理同事对象之间的交互。署理模式署理控制对对象的访问,而表面简化对子体系的访问。署理通常只包装一个对象,而表面可能包装多个子体系组件。单例模式表面类通常利用单例模式实现,确保体系中只有一个表面实例。 9. 现实项目中的表面模式应用
9.1 Web API的服务层
在Web API开发中,服务层可以作为控制器和仓储层之间的表面:
- // 仓储层
- public interface IUserRepository
- {
- User GetById(int id);
- void Save(User user);
- }
- public interface IOrderRepository
- {
- Order GetById(int id);
- List<Order> GetByUserId(int userId);
- void Save(Order order);
- }
- // 服务层(外观)
- public class UserService
- {
- private readonly IUserRepository userRepository;
- private readonly IOrderRepository orderRepository;
- private readonly IEmailService emailService;
- private readonly IAuthenticationService authService;
-
- public UserService(
- IUserRepository userRepository,
- IOrderRepository orderRepository,
- IEmailService emailService,
- IAuthenticationService authService)
- {
- this.userRepository = userRepository;
- this.orderRepository = orderRepository;
- this.emailService = emailService;
- this.authService = authService;
- }
-
- // 简化的用户注册流程
- public bool RegisterUser(string username, string email, string password)
- {
- // 检查用户名是否已存在
- if (authService.IsUsernameTaken(username))
- return false;
-
- // 创建用户账户
- var user = new User { Username = username, Email = email };
- authService.SetPassword(user, password);
-
- // 保存用户
- userRepository.Save(user);
-
- // 发送欢迎邮件
- emailService.SendWelcomeEmail(email, username);
-
- return true;
- }
-
- // 简化的订单历史查询
- public List<OrderSummary> GetUserOrderHistory(int userId)
- {
- User user = userRepository.GetById(userId);
- if (user == null)
- return new List<OrderSummary>();
-
- List<Order> orders = orderRepository.GetByUserId(userId);
-
- // 转换为展示模型
- return orders.Select(o => new OrderSummary
- {
- Id = o.Id,
- Date = o.Date,
- Amount = o.TotalAmount,
- Status = o.Status
- }).ToList();
- }
- }
- // API控制器
- public class UserController : ApiController
- {
- private readonly UserService userService;
-
- public UserController(UserService userService)
- {
- this.userService = userService;
- }
-
- [HttpPost]
- public IActionResult Register([FromBody] RegisterModel model)
- {
- // 控制器只需调用外观的简单方法
- bool success = userService.RegisterUser(
- model.Username, model.Email, model.Password);
-
- if (success)
- return Ok();
- else
- return BadRequest("用户名已存在");
- }
-
- [HttpGet("{userId}/orders")]
- public IActionResult GetOrders(int userId)
- {
- // 控制器通过外观获取订单历史
- var orders = userService.GetUserOrderHistory(userId);
- return Ok(orders);
- }
- }
复制代码 9.2 跨平台应用步伐
表面模式可用于隔离平台特定的API,提供一个统一的跨平台接口:
- // 平台特定的文件系统实现
- public interface IFileSystem
- {
- bool FileExists(string path);
- string ReadAllText(string path);
- void WriteAllText(string path, string contents);
- }
- // Windows实现
- public class WindowsFileSystem : IFileSystem
- {
- public bool FileExists(string path)
- {
- return System.IO.File.Exists(path);
- }
-
- public string ReadAllText(string path)
- {
- return System.IO.File.ReadAllText(path);
- }
-
- public void WriteAllText(string path, string contents)
- {
- System.IO.File.WriteAllText(path, contents);
- }
- }
- // Android实现
- public class AndroidFileSystem : IFileSystem
- {
- private Context context;
-
- public AndroidFileSystem(Context context)
- {
- this.context = context;
- }
-
- public bool FileExists(string filename)
- {
- try {
- context.OpenFileInput(filename);
- return true;
- } catch (Exception) {
- return false;
- }
- }
-
- public string ReadAllText(string filename)
- {
- using (var inputStream = context.OpenFileInput(filename))
- using (var reader = new BufferedReader(new InputStreamReader(inputStream)))
- {
- StringBuilder content = new StringBuilder();
- string line;
- while ((line = reader.ReadLine()) != null)
- {
- content.AppendLine(line);
- }
- return content.ToString();
- }
- }
-
- public void WriteAllText(string filename, string contents)
- {
- using (var outputStream = context.OpenFileOutput(filename, FileMode.Private))
- using (var writer = new PrintWriter(outputStream))
- {
- writer.Write(contents);
- }
- }
- }
- // 外观类
- public class StorageService
- {
- private readonly IFileSystem fileSystem;
-
- public StorageService(IFileSystem fileSystem)
- {
- this.fileSystem = fileSystem;
- }
-
- // 高层方法:保存用户设置
- public bool SaveSettings(Dictionary<string, string> settings)
- {
- try
- {
- // 序列化设置
- string json = JsonSerializer.Serialize(settings);
-
- // 保存到文件
- fileSystem.WriteAllText("settings.json", json);
- return true;
- }
- catch (Exception)
- {
- return false;
- }
- }
-
- // 高层方法:加载用户设置
- public Dictionary<string, string> LoadSettings()
- {
- try
- {
- // 检查文件是否存在
- if (!fileSystem.FileExists("settings.json"))
- return new Dictionary<string, string>();
-
- // 读取并反序列化
- string json = fileSystem.ReadAllText("settings.json");
- return JsonSerializer.Deserialize<Dictionary<string, string>>(json);
- }
- catch (Exception)
- {
- return new Dictionary<string, string>();
- }
- }
- }
- // 客户端代码
- public class SettingsScreen
- {
- private StorageService storageService;
-
- public SettingsScreen(StorageService storageService)
- {
- this.storageService = storageService;
- }
-
- public void SaveUserPreferences(string theme, string language)
- {
- var settings = new Dictionary<string, string>
- {
- ["theme"] = theme,
- ["language"] = language
- };
-
- // 使用外观简化存储操作
- if (storageService.SaveSettings(settings))
- Console.WriteLine("设置已保存");
- else
- Console.WriteLine("保存设置失败");
- }
-
- public void LoadUserPreferences()
- {
- // 使用外观简化加载操作
- var settings = storageService.LoadSettings();
-
- string theme = settings.ContainsKey("theme") ? settings["theme"] : "default";
- string language = settings.ContainsKey("language") ? settings["language"] : "en";
-
- Console.WriteLine($"加载设置: 主题={theme}, 语言={language}");
- }
- }
复制代码 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
|