热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C#面向对象编程中开闭原则的示例详解【.Net框架】

在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解、灵活和可维护。本文将通过实例详细讲讲C#面向对象编程中开闭原则

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

在面向对象编程中,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 _shapes;

    public AreaCalculator(List shapes)
    {
        _shapes = shapes;
    }

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

        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
{
    /// 
    /// 计算面积
    /// 
    /// 
    double CalcArea();
}

修改后的 Square 和 Circle 类:

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

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

/// 
/// 圆形
/// 
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 _shapes;

    public AreaCalculator(List shapes)
    {
        _shapes = shapes;
    }

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

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

        return areas.Sum();
    }
}

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

/// 
/// 长方形
/// 
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 {
            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#开闭原则内容请搜索编程笔记以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程笔记!


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
author-avatar
mobiledu2502871243
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有