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

 更新时间:2022年07月26日 11:38:56   作者:技术译民  
在面向对象编程中,SOLID?是五个设计原则的首字母缩写,旨在使软件设计更易于理解、灵活和可维护。本文将通过实例详细讲讲C#面向对象编程中开闭原则,需要的可以参考一下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

在面向对象编程中,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#强制类型转换小结,需要的朋友参考下吧
    2017-07-07
  • C#开发Android百度地图手机应用程序(多地图展示)

    C#开发Android百度地图手机应用程序(多地图展示)

    这篇文章主要介绍了C#开发Android百度地图手机应用程序(多地图展示)的相关资料,需要的朋友可以参考下
    2016-02-02
  • C#多线程之线程中止Abort()方法

    C#多线程之线程中止Abort()方法

    这篇文章介绍了C#多线程中的线程中止Abort()方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • 深入浅析Restful接口的两种使用方式

    深入浅析Restful接口的两种使用方式

    restful接口常用的两种方式是get和post.接下来通过本文给大家介绍Restful接口的两种使用方式,本文给大家介绍的非常详细,需要的朋友参考下吧
    2018-09-09
  • C#通过标签软件Bartender的ZPL命令打印条码

    C#通过标签软件Bartender的ZPL命令打印条码

    这篇文章介绍了C#通过标签软件Bartender的ZPL命令打印条码,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-01-01
  • C#实现简单的计算器小功能

    C#实现简单的计算器小功能

    这篇文章主要为大家详细介绍了C#实现简单的计算器小功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 浅析泛型类接口定义

    浅析泛型类接口定义

    在使用泛型定义类的过程中遇到了不少问题,特记录如下,需要的朋友可以参考下
    2013-07-07
  • C#自定义事件监听实现方法

    C#自定义事件监听实现方法

    这篇文章主要介绍了C#自定义事件监听实现方法,涉及C#事件监听的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-08-08
  • C# cefSharep控件的使用详情

    C# cefSharep控件的使用详情

    本文主要介绍了C# cefSharep控件的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • C#实现简单的聊天窗体

    C#实现简单的聊天窗体

    这篇文章主要为大家详细介绍了C#实现简单的聊天窗体,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07

最新评论

?


http://www.vxiaotou.com