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)

本文我们来介绍单一功能原则

单一功能原则

在面向对象编程领域中,单一功能原则(Single responsibility principle)规定每个类都应该有且仅有一个单一的功能,并且该功能应该由这个类完全封装起来。所有它的(这个类的)服务都应该严密的和该功能平行(功能平行,意味着没有依赖)。

这个术语由罗伯特·C·马丁(Robert Cecil Martin)在他的《敏捷软件开发,原则,模式和实践》一书中的一篇名为『面向对象设计原则』的文章中提出。马丁表述该原则是基于《结构化分析和系统规格》一书中的内聚原则(Cohesion)之上的。

马丁把功能(职责)定义为:“改变的原因”,并总结出一个类或者模块应该有且只有一个改变的原因。一个具体的例子就是,想象有一个用于编辑和打印报表的模块。这样的一个模块存在两个改变的原因。第一,报表的内容可以改变(编辑)。第二,报表的格式可以改变(打印)。这两方面的改变会因为完全不同的起因而发生:一个是本质的修改,一个是表面的修改。单一功能原则认为这两方面的问题事实上是两个分离的功能,因此他们应该分离在不同的类或者模块里。把具有不同的改变原因的事物耦合在一起的设计是糟糕的。

保持一个类专注于单一功能点的一个重要的原因是,它可以使类更加的健壮。回顾上面的例子,如果有一个对于报表“编辑”流程的修改,那么将存在极大的危险性,因为假设这两个功能存在于同一个类中,修改报表的“编辑”流程会导致公共状态或者依赖关系的改变,从而可能使“打印”功能的代码无法正常运行。

C# 示例

例如,考虑这样一个应用程序,它接受一组形状(圆形和正方形),并计算该列表中所有形状的面积之和。

首先,创建形状类,并通过构造函数设置所需的参数。

对于正方形,需要知道它的边长:

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

对于圆形,需要它的半径:

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

接下来,创建 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();}public string Output(){return $"Sum of the areas of provided shapes: {Sum()}";}
}

要使用 AreaCalculator 类,您需要实例化这个类,并传入一个形状列表,并显示其输出。

在此,我们传入一个三个形状的列表:一个半径为 2 的圆,一个边长为 5 的正方形,一个边长为 6 的正方形。

static void Main(string[] args)
{var shapes = new List<object> {new Circle(2),new Square(5),new Square(6)};var areas = new AreaCalculator(shapes);Console.WriteLine(areas.Output());
}

运行程序,您会看到如下的输出:

Sum of the areas of provided shapes: 73.56637061435917

输出正常,但这并不符合单一功能原则。因为 AreaCalculator 类既计算了所有形状的面积之和,又处理了输出数据的格式。

考虑这样一个场景,假如想要输出转换为另一种格式呢,如 JSON。我们就需要去修改 AreaCalculator 类,这样本来是为了修改输出数据的格式,却可能会影响到计算的逻辑,这明显违反了单一功能原则

§正确的示范

AreaCalculator 类应该只关心计算提供的形状的面积之和,不应该关心输出什么格式。

下面我们来做一些修改,删除 AreaCalculator 类中的 Output 方法:

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();}
}

并新增一个 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<object> {new Circle(2),new Square(5),new Square(6)};var areaCalculator = new AreaCalculator(shapes);var outputer = new SumCalculatorOutputter(areaCalculator);Console.WriteLine(outputer.JSON());Console.WriteLine(outputer.String());
}

运行程序,输出结果如下:

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

现在,AreaCalculator 类处理计算逻辑,SumCalculatorOutputter 类处理输出格式,它们各司其职,遵循了单一功能原则

总结

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

作者 :技术译民
出品 :技术译站

参考文档:

  • https://en.wikipedia.org/wiki/SOLID

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/291552.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++之virtual 方法

1、例子1 #include <iostream> class A { public:const char* fetchClassName() { return "A"; } };class B: public A { public:const char* fetchClassName() { return "B"; } };int main(void) {B obj_b;A &obj_a = obj_b;std::cout <<…

U盘安装Ubuntu三步走

需要工具&#xff1a; U盘、Ubuntu的ISO镜像、universal usb installer 1、下载Ubuntu &#xff08;1&#xff09; &#xff08;2&#xff09;我这里下载14.04版本 &#xff08;3&#xff09;我这里下载64位系统 下载后得到的是个torrent种子&#xff0c;双击后用迅雷下载即可 …

sdram 时钟相位_零基础学FPGA (二十五)必会! 从静态时序分析到SDRAM时序收敛(下篇)...

七、SDRAM工作时钟相位偏移计算本文引用地址&#xff1a;http://www.eepw.com.cn/article/279083.htm从上篇文章中我们知道&#xff0c;我们的数据是要经过一定的延时才会到达目标器件的&#xff0c;这个延时也就是相对于源寄存器的时钟发射沿的时间延时&#xff0c;数据在源寄…

星跃计划 | 新项目持续招募中!MSR Asia-MSR Redmond 联合科研计划邀你申请!

微软亚洲研究院与微软总部联合推出的“星跃计划”科研合作项目邀请你来报名&#xff01;本次“星跃计划”报名再次新增了来自微软 ED (Experiences Devices) Applied Research 全球总部的新项目&#xff0c;欢迎大家关注与申请&#xff01;还在等什么&#xff1f;加入“星跃计…

C++之map的前后遍历

java用迭代器只能往后迭代 但是C可以往后迭代和往前迭代 1、CMap往后迭代 map<int,int> amap; amap.insert(pair<int,int>(1,1)); amap.insert(pair<int.int>(2,2)); map<int,int>::iterator it; for(it amap.begin();it ! ampa.end();it…

Linux服务器上监控网络带宽与监控性能命令大全

【51CTO精选译文】本文介绍了一些可以用来监控网络使用情况的Linux命令行工具。这些工具可以监控通过网络接口传输的数据&#xff0c;并测量目前哪些数据所传输的速度。入站流量和出站流量分开来显示。一些命令可以显示单个进程所使用的带宽。这样一来&#xff0c;用户很容易发…

Android的AlertDialog详解

AlertDialog的构造方法全部是Protected的&#xff0c;所以不能直接通过new一个AlertDialog来创建出一个AlertDialog。 要创建一个AlertDialog&#xff0c;就要用到AlertDialog.Builder中的create()方法。 使用AlertDialog.Builder创建对话框需要了解以下几个方法&#xff1a; s…

C++中之a[5](栈区)和int a=new int[5](堆区)有什么区别

1、int* anew int[5]需要判断内存是否分配成功&#xff0c;以及在不用时需要使用delete[] a进行内存释放&#xff1b; 2、如果不是a[5]&#xff0c;而是a[5000000000]或者更大的话&#xff0c;那一般情况下&#xff0c;就只能使用int* anew这种方式了。这个涉及到内存存放位置的…

最长严格上升子序列

该题应使用动归 时间限制: 1 s空间限制: 256000 KB题目等级 : 黄金 Gold题解查看运行结果题目描述 Description给一个数组a1, a2 ... an&#xff0c;找到最长的上升降子序列ab1<ab2< .. <abk&#xff0c;其中b1<b2<..bk。 输出长度即可。 输入描述 Input Descri…

workbench mysql mac_mysql workbench mac下载-mysql workbench mac 64位下载8.0.15 官方最新版__西西软件下载...

MySQL Workbench mac版是专为数据库架构师、开发人员和 DBA 打造的一个统一的可视化工具。MySQL Workbench 为数据库管理员、程序开发者和系统规划师提供可视化的Sql开发、数据库建模、以及数据库管理功能。MySQL Workbench 提供了数据建模工具、SQL 开发工具和全面的管理工具(…

C# 使用Awaiter

可以对任何提供 GetAwaiter 方法并返回 awaiter 的对象使用 async 关键字。awaiter 用 OnCompleted 方法实现 INotifyCompletion 接口。此方法在任务完成时调用。下面的代码片段不是在任务中使用 await&#xff0c;而是使用任务的 GetAwaiter 方法。Task 类的 GetAwaiter 返回一…

模板-1-模板类的特化

2019独角兽企业重金招聘Python工程师标准>>> 类模板的特化 语义: 表明该模板类在特殊的类型下具有不同的行为.类的定义,应该与模板类放入一个头文件中,告知编译器该特化类的存在;类成员的定义,应该放入源文件中.该特化类就与普通类一样,是一个实实在在存在的实体.语…

mac环境下分别用vim和Xcode运行C++(hello word)程序

1、用vim写第一个C++程序 1) 打开mac终端 2) 用vim新建一个test.cpp文件 vim test.cpp 3) 写hello word代码 输入插入命令 i 写入代码 #include<iostream>using namespace std;int main() {cout<<"hello word"<<…

C# 内存法图像处理

内存法通过把图像储存在内存中进行处理&#xff0c;效率大大高于GetPixel方法&#xff0c;安全性高于指针法。 笔者当初写图像处理的时候发现网上多是用GetPixel方法实现&#xff0c;提到内存法的时候也没有具体实现&#xff0c;所以笔者在这里具体实现一下- -&#xff0c;望指…

mysql分组查询和子查询语句_6.MySQL分组聚合查询,子查询

自己的MySQL阅读笔记&#xff0c;持续更新&#xff0c;直到看书结束。数据库技术可以有效帮助一个组织或者企业科学、有效的管理数据&#xff0c;也是现在很多企业招聘数据分析师的必备要求之一。大家如果看过MySQL的书&#xff0c;也可以看我的知识导图做一个复习&#xff0c;…

swf 文件在线播放的,怎么能够下载呢?(除视频外其它都可)

点击播放SWF文件的网页上方工具的Internet选项&#xff0c;点击设置&#xff0c;再点击查看文件&#xff0c;里面有播放的SWF文件图标&#xff0c;把它复制就可以了。转载于:https://blog.51cto.com/wangheyu1/1894807

ABP vNext微服务架构详细教程——分布式权限框架(下)

3公共组件添加公共类库Demo.Permissions&#xff0c;编辑Demo.Permissions.csproj文件&#xff0c;将 <Project Sdk"Microsoft.NET.Sdk"> 改为&#xff1a;<Project Sdk"Microsoft.NET.Sdk.Web">为Demo.Permissions项目添加Nuget引用Volo.Abp.…

ios开发第一步--虚拟机安装MAC OS X

暂时还没买Macbook&#xff0c;先用虚拟机练练手。 先说说准备工作&#xff0c;我是在win8下安装的&#xff0c;这个不是关键的&#xff0c;只要Vmware版本和MAC OS X版本确定就行了&#xff0c;win7下同样可以。 1、虚拟机Vmware10.0.0 下载地址 http://pan.baidu.com/s/1jGv…

算法学习笔记(三)-----各种基础排序问题

2019独角兽企业重金招聘Python工程师标准>>> 一、直接插入排序&#xff1a;是最简单的排序方法&#xff0c;算法简单来说就是可以把第一个数a[0]看做有序数组&#xff0c;那么a[1]要插入进来&#xff0c;对比&#xff0c;插入合适位置&#xff1b;然后a[0],a[1]是有…

mac之把打开终端设置快捷键为Ctrl+Alt+T

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程 1、在Automator.app中创建一个AppleScript Finder&#xff0d;>应用程序->Automator打开Automator.app&#xff0c;打开Automator后…