翼度科技»论坛 编程开发 .net 查看内容

面向对象设计的六大原则(SOLID原则)-——开闭原则

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
开闭原则(Open-Closed Principle, OCP)是面向对象设计的五大SOLID原则之一。这个原则主张“软件实体(类、模块、函数等)应该对扩展开放,对修改关闭”。也就是说,软件的设计应该允许在不修改原有代码的情况下增加新的功能。这样的设计有助于降低代码的复杂性和维护成本,同时提高系统的可复用性和可维护性。
详细解释

开闭原则强调两点:

  • 对扩展开放:当需要增加新功能时,应该通过添加新的代码来实现,而不是修改已有的代码。这可以通过使用抽象、接口、继承和多态等面向对象的技术来实现。
  • 对修改关闭:已有的代码,特别是那些已经经过测试和验证的代码,应该尽量避免修改。这样可以减少引入新bug的风险,同时保持系统的稳定性。
遵循开闭原则可以带来以下好处:

  • 提高可维护性:由于系统对修改关闭,因此可以减少因修改已有代码而引入的错误。
  • 提高可扩展性:系统对扩展开放,使得添加新功能变得更加容易。
  • 降低代码的耦合度:通过抽象和接口来定义系统的行为,可以减少类与类之间的直接依赖,从而降低代码的耦合度。
应用场景及代码示例(C#)

场景1:插件系统

假设我们有一个图像处理系统,我们希望通过插件的方式来添加新的图像处理功能。
代码示例
  1. public interface IImageFilter  
  2. {  
  3.     void ApplyFilter(Image image);  
  4. }  
  5.   
  6. public class BrightnessFilter : IImageFilter  
  7. {  
  8.     public void ApplyFilter(Image image)  
  9.     {  
  10.         // 增加亮度的逻辑  
  11.     }  
  12. }  
  13.   
  14. public class ContrastFilter : IImageFilter  
  15. {  
  16.     public void ApplyFilter(Image image)  
  17.     {  
  18.         // 调整对比度的逻辑  
  19.     }  
  20. }  
  21.   
  22. public class ImageProcessor  
  23. {  
  24.     private List<IImageFilter> filters = new List<IImageFilter>();  
  25.   
  26.     public void AddFilter(IImageFilter filter)  
  27.     {  
  28.         filters.Add(filter);  
  29.     }  
  30.   
  31.     public void ProcessImage(Image image)  
  32.     {  
  33.         foreach (var filter in filters)  
  34.         {  
  35.             filter.ApplyFilter(image);  
  36.         }  
  37.     }  
  38. }  
  39.   
  40. // 使用示例  
  41. var processor = new ImageProcessor();  
  42. processor.AddFilter(new BrightnessFilter());  
  43. processor.AddFilter(new ContrastFilter());  
  44. var image = new Image(); // 假设有一个Image类  
  45. processor.ProcessImage(image);
复制代码
在这个例子中,ImageProcessor 类对扩展开放,因为我们可以很容易地添加新的滤镜(通过实现 IImageFilter 接口)。同时,它对修改关闭,因为我们不需要修改 ImageProcessor 类来支持新的滤镜。
场景2:策略模式

策略模式是一种常见的设计模式,用于根据不同的情况选择不同的算法或策略。这也符合开闭原则。
代码示例
  1. public interface ISortingStrategy  
  2. {  
  3.     void Sort(List<int> list);  
  4. }  
  5.   
  6. public class BubbleSortStrategy : ISortingStrategy  
  7. {  
  8.     public void Sort(List<int> list)  
  9.     {  
  10.         // 冒泡排序的逻辑  
  11.     }  
  12. }  
  13.   
  14. public class QuickSortStrategy : ISortingStrategy  
  15. {  
  16.     public void Sort(List<int> list)  
  17.     {  
  18.         // 快速排序的逻辑  
  19.     }  
  20. }  
  21.   
  22. public class SortedList  
  23. {  
  24.     private ISortingStrategy sortingStrategy;  
  25.   
  26.     public SortedList(ISortingStrategy sortingStrategy)  
  27.     {  
  28.         this.sortingStrategy = sortingStrategy;  
  29.     }  
  30.   
  31.     public void SetSortingStrategy(ISortingStrategy sortingStrategy)  
  32.     {  
  33.         this.sortingStrategy = sortingStrategy;  
  34.     }  
  35.   
  36.     public void Sort(List<int> list)  
  37.     {  
  38.         sortingStrategy.Sort(list);  
  39.     }  
  40. }  
  41.   
  42. // 使用示例  
  43. var sortedList = new SortedList(new BubbleSortStrategy());  
  44. var numbers = new List<int> { 3, 1, 4, 1, 5, 9 };  
  45. sortedList.Sort(numbers);  
  46.   
  47. // 更换排序策略  
  48. sortedList.SetSortingStrategy(new QuickSortStrategy());  
  49. sortedList.Sort(numbers);
复制代码
 在这个例子中,SortedList 类对排序策略的扩展开放,因为我们可以通过实现 ISortingStrategy 接口来添加新的排序算法。同时,它对修改关闭,因为更换排序策略时不需要修改 SortedList 类的内部代码。
当然,开闭原则可以应用于许多不同的场景。以下是一些额外的应用场景示例,以及相应的C#代码:
场景3:日志记录系统

在一个大型系统中,日志记录是非常重要的。你可能想要根据不同的需求添加不同的日志记录器,比如文件日志记录器、控制台日志记录器或数据库日志记录器。通过使用开闭原则,你可以轻松地添加新的日志记录器,而不需要修改现有的日志记录系统。
  1. public interface ILogger  
  2. {  
  3.     void Log(string message);  
  4. }  
  5.   
  6. public class FileLogger : ILogger  
  7. {  
  8.     public void Log(string message)  
  9.     {  
  10.         // 将日志写入文件的逻辑  
  11.     }  
  12. }  
  13.   
  14. public class ConsoleLogger : ILogger  
  15. {  
  16.     public void Log(string message)  
  17.     {  
  18.         Console.WriteLine(message);  
  19.     }  
  20. }  
  21.   
  22. public class LoggingSystem  
  23. {  
  24.     private readonly List<ILogger> loggers = new List<ILogger>();  
  25.   
  26.     public void RegisterLogger(ILogger logger)  
  27.     {  
  28.         loggers.Add(logger);  
  29.     }  
  30.   
  31.     public void Log(string message)  
  32.     {  
  33.         foreach (var logger in loggers)  
  34.         {  
  35.             logger.Log(message);  
  36.         }  
  37.     }  
  38. }  
  39.   
  40. // 使用示例  
  41. var loggingSystem = new LoggingSystem();  
  42. loggingSystem.RegisterLogger(new FileLogger());  
  43. loggingSystem.RegisterLogger(new ConsoleLogger());  
  44. loggingSystem.Log("This is a log message.");
复制代码
在这个例子中,LoggingSystem 类对日志记录器的扩展开放,因为我们可以实现 ILogger 接口来创建新的日志记录器,并将其注册到系统中。同时,它对修改关闭,因为添加新的日志记录器不需要修改 LoggingSystem 类的代码。
场景4:数据库访问层

在构建应用程序时,你可能需要访问不同的数据库,比如SQL Server、MySQL或Oracle。通过使用开闭原则,你可以设计一个数据库访问层,该层对不同类型的数据库扩展开放,而对现有代码的修改关闭。
代码示例
  1. public interface IDatabase  
  2. {  
  3.     void Connect();  
  4.     void ExecuteQuery(string query);  
  5.     void Close();  
  6. }  
  7.   
  8. public class SqlServerDatabase : IDatabase  
  9. {  
  10.     public void Connect()  
  11.     {  
  12.         // 连接到SQL Server的逻辑  
  13.     }  
  14.   
  15.     public void ExecuteQuery(string query)  
  16.     {  
  17.         // 在SQL Server上执行查询的逻辑  
  18.     }  
  19.   
  20.     public void Close()  
  21.     {  
  22.         // 关闭SQL Server连接的逻辑  
  23.     }  
  24. }  
  25.   
  26. public class MySqlDatabase : IDatabase  
  27. {  
  28.     public void Connect()  
  29.     {  
  30.         // 连接到MySQL的逻辑  
  31.     }  
  32.   
  33.     public void ExecuteQuery(string query)  
  34.     {  
  35.         // 在MySQL上执行查询的逻辑  
  36.     }  
  37.   
  38.     public void Close()  
  39.     {  
  40.         // 关闭MySQL连接的逻辑  
  41.     }  
  42. }  
  43.   
  44. public class DatabaseManager  
  45. {  
  46.     private IDatabase database;  
  47.   
  48.     public DatabaseManager(IDatabase database)  
  49.     {  
  50.         this.database = database;  
  51.     }  
  52.   
  53.     public void PerformQuery(string query)  
  54.     {  
  55.         database.Connect();  
  56.         database.ExecuteQuery(query);  
  57.         database.Close();  
  58.     }  
  59. }  
  60.   
  61. // 使用示例  
  62. var sqlServerDb = new SqlServerDatabase();  
  63. var databaseManager = new DatabaseManager(sqlServerDb);  
  64. databaseManager.PerformQuery("SELECT * FROM Users");  
  65.   
  66. // 切换到MySQL数据库  
  67. var mySqlDb = new MySqlDatabase();  
  68. databaseManager = new DatabaseManager(mySqlDb);  
  69. databaseManager.PerformQuery("SELECT * FROM Users");
复制代码
在这个例子中,DatabaseManager 类对不同类型的数据库扩展开放,因为我们可以通过实现 IDatabase 接口来创建新的数据库访问类。同时,它对修改关闭,因为切换数据库类型不需要修改 DatabaseManager 类的代码。实际上,在实际应用中,你可能会使用依赖注入框架来动态地注入不同的数据库实现,而不是像示例中那样手动创建和切换它们。
public interface ISortingStrategy { void Sort(List list); } public class BubbleSortStrategy : ISortingStrategy { public void Sort(List list) { // 冒泡排序的逻辑 } } public class QuickSortStrategy : ISortingStrategy { public void Sort(List list) { // 快速排序的逻辑 } } public class SortedList { private ISortingStrategy sortingStrategy; public SortedList(ISortingStrategy sortingStrategy) { this.sortingStrategy = sortingStrategy; } public void SetSortingStrategy(ISortingStrategy sortingStrategy) { this.sortingStrategy = sortingStrategy; } public void Sort(List list) { sortingStrategy.Sort(list); } } // 使用示例 var sortedList = new SortedList(new BubbleSortStrategy()); var numbers = new List { 3, 1, 4, 1, 5, 9 }; sortedList.Sort(numbers); // 更换排序策略 sortedList.SetSortingStrategy(new QuickSortStrategy()); sortedList.Sort(numbers);

来源:https://www.cnblogs.com/forges/p/18067528
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具