C#面向对象编程中开闭原则的示例详解

作者:技术译民 时间:2022-12-07 11:15:28 

在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解、灵活和可维护。这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原则的子集,在他2000年的论文《设计原则与设计模式》中首次提出。

SOLID 原则包含:

  • S:单一功能原则(single-responsibility principle)

  • O:开闭原则(open-closed principle)

  • L:里氏替换原则(Liskov substitution principle)

  • I:接口隔离原则(Interface segregation principle)

  • D:依赖反转原则(Dependency inversion principle)

本文我们来介绍开闭原则。

开闭原则

在面向对象编程领域中,开闭原则 (open-closed principle, OCP) 规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,而对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。该特性在产品化的环境中是特别有价值的,在这种环境中,改变源代码需要代码审查,单元测试以及诸如此类的用以确保产品使用品质的过程。遵循开闭原则的代码在扩展时并不发生改变,因此无需这些过程。

具体到类,也就是说,在不修改类本身代码的情况下,应该是可以扩展它的行为的。

C# 示例

让我们回顾一下上一篇文章单一功能原则中提到的 AreaCalculator 类,

class AreaCalculator
{
   private List<object> _shapes;

public AreaCalculator(List<object> shapes)
   {
       _shapes = shapes;
   }

/// <summary>
   /// 计算所有形状的面积总和
   /// </summary>
   /// <returns></returns>
   public double Sum()
   {
       List<double> areas = new List<double>();

foreach (var item in _shapes)
       {
           if (item is Square s)
           {
               areas.Add(Math.Pow(s.SideLength, 2));
           }
           else if (item is Circle c)
           {
               areas.Add(Math.PI * Math.Pow(c.Radius, 2));
           }
       }

return areas.Sum();
   }
}

对于上面的计算方法,考虑这样一种场景,用户想要计算一些其它形状的面积总和,比如三角形、矩形、五边形等等&hellip;&hellip; 您将不得不反复编辑此类以添加更多的 if/else 块,这就违反了开闭原则。

改进

一个更好的做法是,将计算每个形状的面积的逻辑从 AreaCalculator 类中移除,并将其添加到对应每个形状的类中。我们可以定义一个带有 CalcArea 方法的接口 IShape,然后让每个形状都实现这个接口。

接口 IShape:

interface IShape
{
   /// <summary>
   /// 计算面积
   /// </summary>
   /// <returns></returns>
   double CalcArea();
}

修改后的 Square 和 Circle 类:

/// <summary>
/// 正方形
/// </summary>
class Square : IShape
{
   public Square(double length)
   {
       SideLength = length;
   }
   public double SideLength { get; init; }

public double CalcArea()
   {
       return Math.Pow(SideLength, 2);
   }
}

/// <summary>
/// 圆形
/// </summary>
class Circle : IShape
{
   public Circle(double radius)
   {
       Radius = radius;
   }

public double Radius { get; init; }

public double CalcArea()
   {
       return Math.PI * Math.Pow(Radius, 2);
   }
}

AreaCalculator 类也要对应做一些修改:

class AreaCalculator
{
   private List<IShape> _shapes;

public AreaCalculator(List<IShape> shapes)
   {
       _shapes = shapes;
   }

/// <summary>
   /// 计算面积总和
   /// </summary>
   /// <returns></returns>
   public double Sum()
   {
       List<double> areas = new List<double>();

foreach (var item in _shapes)
       {
           areas.Add(item.CalcArea());
       }

return areas.Sum();
   }
}

此时,如果我们有一个新的形状需要进行计算,我们可以直接添加一个实现了接口 IShape 的新类,而无需修改 AreaCalculator 类的代码,比如添加一个长方形类:

/// <summary>
/// 长方形
/// </summary>
class Rectangle : IShape
{
   public Rectangle(double width, double height)
   {
       Width = width;
       Height = height;
   }

public double Width { get; init; }
   public double Height { get; init; }

public double CalcArea()
   {
       return Width * Height;
   }
}

处理输出格式的 SumCalculatorOutputter 类同样无需修改:

class SumCalculatorOutputter
{
   protected AreaCalculator _calculator;

public SumCalculatorOutputter(AreaCalculator calculator)
   {
       _calculator = calculator;
   }

public string String()
   {
       return $"Sum of the areas of provided shapes: {_calculator.Sum()}";
   }

public string JSON()
   {
       var data = new { Sum = _calculator.Sum() };
       return System.Text.Json.JsonSerializer.Serialize(data);
   }
}

然后,我们修改 Main 方法中的代码来测试一下:

static void Main(string[] args)
{
   var shapes = new List<IShape> {
           new Circle(2),
           new Square(5),
           new Rectangle(2,3)
   };

var areaCalculator = new AreaCalculator(shapes);
   var outputer = new SumCalculatorOutputter(areaCalculator);
   Console.WriteLine(outputer.JSON());
   Console.WriteLine(outputer.String());
}

运行一下,输出结果为:

{"Sum":43.56637061435917}
Sum of the areas of provided shapes: 43.56637061435917

现在,这些类的设计,既遵循了单一功能原则,又遵循了开闭原则。

来源:https://www.cnblogs.com/ittranslator/p/SOLID-open-closed-principle.html

标签:C#,面向对象,开闭原则
0
投稿

猜你喜欢

  • C#生成单页静态页简单实例

    2022-01-22 20:00:57
  • Spring Boot 集成 Sharding-JDBC + Mybatis-Plus 实现分库分表功能

    2023-08-28 16:52:09
  • 详解Spring Boot 事务的使用

    2022-08-09 01:59:07
  • C++基础概念讲述

    2022-10-31 16:56:05
  • Java获取文件夹下所有文件名称的方法示例

    2023-08-08 08:01:26
  • Java实现五子棋(附详细源码)

    2023-10-19 15:20:54
  • 解决IDEA中快捷键Alt+Enter不能使用的问题

    2022-09-14 07:54:07
  • Mybatis-Plus自动填充更新操作相关字段的实现

    2022-01-14 20:43:01
  • Android Insets相关知识总结

    2023-04-24 09:08:23
  • 使用SpringBoot实现微服务超时重试模式的示例

    2021-12-28 13:58:45
  • 带你了解Java数据结构和算法之无权无向图

    2023-12-24 10:54:05
  • java原生序列化和Kryo序列化性能实例对比分析

    2023-11-26 16:04:20
  • SpringBoot中使用redis做分布式锁的方法

    2023-02-17 04:08:52
  • Android实现界面左右滑动切换功能

    2022-08-30 02:00:58
  • Java ForkJoin框架的原理及用法

    2022-03-13 05:26:51
  • Java基础知识之ByteArrayInputStream流的使用

    2023-10-27 14:37:53
  • 在Ubuntu中安装VSCode并配置C/C++开发环境的方法步骤

    2021-07-04 18:19:19
  • Java设计模式之java桥接模式详解

    2023-10-15 15:19:10
  • Java中的static关键字你了解多少

    2022-05-21 11:25:51
  • JavaSE的三大接口:Comparator,Comparable和Cloneable详解

    2021-09-01 03:14:20
  • asp之家 软件编程 m.aspxhome.com