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

 更新时间:2022年07月26日 11:38:56   作者:技术译民  
在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解、灵活和可维护。本文将通过实例详细讲讲C#面向对象编程中开闭原则,需要的可以参考一下

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

SOLID 原则包含:

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

开闭原则

在面向对象编程领域中,开闭原则 (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();
    }
}

对于上面的计算方法,考虑这样一种场景,用户想要计算一些其它形状的面积总和,比如三角形、矩形、五边形等等…… 您将不得不反复编辑此类以添加更多的 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

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

总结

本文我介绍了 SOLID 原则中的开闭原则 (open-closed principle),并通过 C# 代码示例简明地诠释了它的含意和实现,希望对您有所帮助。

参考文档:

https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design

到此这篇关于C#面向对象编程中开闭原则的示例详解的文章就介绍到这了,更多相关C#开闭原则内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C#数字图像处理之图像缩放的方法

    C#数字图像处理之图像缩放的方法

    这篇文章主要介绍了C#数字图像处理之图像缩放的方法,涉及C#针对图像的缩放技巧,需要的朋友可以参考下
    2015-04-04
  • C# WinForm实现自动更新程序之客户端的示例代码

    C# WinForm实现自动更新程序之客户端的示例代码

    这篇文章主要为大家详细介绍了利用C# WinForm实现自动更新程序之客户端的实现方法,文中的示例代码讲解详细,感兴趣的小伙伴可以尝试一下
    2022-10-10
  • c#读取XML多级子节点

    c#读取XML多级子节点

    本文主要介绍了c#读取XML多级子节点的方法。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-03-03
  • Unity 实现删除missing脚本组件

    Unity 实现删除missing脚本组件

    这篇文章主要介绍了Unity 删除missing脚本组件的实现方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Unity实现移动端手势解锁功能

    Unity实现移动端手势解锁功能

    这篇文章主要为大家详细介绍了Unity实现移动端手势解锁功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07
  • C#在Windows上调用7-zip实现压缩文件

    C#在Windows上调用7-zip实现压缩文件

    这篇文章主要为大家详细介绍了C#如何在Windows上调用7-zip实现压缩文件,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以学习一下
    2023-10-10
  • c#实现51单片机频率计的代码分享(数字频率计设计)

    c#实现51单片机频率计的代码分享(数字频率计设计)

    c#实现51单片机频率计的代码分享,大家参考使用吧
    2013-12-12
  • c#中的泛型委托详解

    c#中的泛型委托详解

    本文主要介绍了c#中的泛型委托。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-01-01
  • 详解c# 并行计算

    详解c# 并行计算

    本文主要介绍了并行计算的简单使用,并行循环的中断和跳出、并行循环中为数组/集合添加项、返回集合运算结果/含有局部变量的并行循环、、PLinq(Linq的并行计算)等相关内容。
    2020-12-12
  • C#类和结构详解

    C#类和结构详解

    本文详细讲解了C#中的类和结构,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04

最新评论