c#六大设计原则(以仪器代码为例)
[有格式的原文请到https://www.cnc6.cn/c六大设计原则/文末下载]
软件设计原则常见的有6大原则,分别为:
①单一职责原则;
②开闭原则;
③依赖倒置原则;
④里氏替换原则;
⑤接口隔离原则;
⑥迪米特法则。
使用C#编程方式,并结合仪器(Instrument)编程,对以上设计原则进行讲解。
一、单一职责原则
简述:类职责要单一,不能有多个职责,具有多个职责的类要进行拆分,形成单一职责的类。
C#代码如下:
class DCPowerSupply
{
public void On() { }
public void Off() { }
public double Voltage { set; get; }
public double Current { set; get; }
public void Record(string content)
{
//记录操作
}
}
以上代码中,函数Off、On、Voltage、Current都是属于DCPowerSupply类的职责,但函数Record并不属于DCPowerSupply的职责,因此,需要将Record函数重新形成一个新类,并应用到该类中,新修改的C#代码如下:
class DCPowerSupply
{
public void On() { }
public void Off() { }
public double Voltage { set; get; }
public double Current { set; get; }
}
class Log
{
public void Record(string content)
{
//记录操作
}
}
客户端代码如下:
class Program
{
static void Main(string[] args)
{
DCPowerSupply dcPowerSupply = new DCPowerSupply();
dcPowerSupply.Voltage = 5;
dcPowerSupply.On();
Log log = new Log();
log.Record($"DCPowerSupply:{dcPowerSupply.Voltage},On");
}
}
从以上代码可以看出,原本耦合性很强的DCPowerSupply中的函数Record,已经从该类中进行解耦,形成松耦合的代码,复用性更强。
二、开闭原则
简述:对类的操作,拓展的要开放,修改的要关闭,类一旦写好并运行OK,就不要轻易去变动它,利用面向对象编程的继承特性,可以很好地对所需要增加的功能进行拓展。
C#代码如下:
class ACPowerSupply
{
public void On() { }
public void Off() { }
public double Voltage { set; get; }
public double Current { set; get; }
}
现在需要在ACPowerSupply类加入测量功率因素(PowerFactor)的功能,如果像这样修改ACPowerSupply类,C#代码如下:
class ACPowerSupply
{
public void On() { }
public void Off() { }
public double Voltage { set; get; }
public double Current { set; get; }
public double PowerFactor { set; get; }
}
那就会违背了开闭原则,因ACPowerSupply类已经写好,如果要增加功能,可以新编写一个名为ACPowerSupplyEx的类,继承自ACPowerSupply,并在其中加入PowerFactor的功能即可。
C#代码如下:
class ACPowerSupplyEx:ACPowerSupply
{
public double PowerFactor { set; get; }
}
三、依赖倒置原则
简述:抽象不依赖于具体细节,具体细节依赖抽象,要面向抽象编程,不要面向实现编程。说的简单点,无论是变量声明、返回值、还是参数,其类型都是抽象的,而不是具体的。
鉴于以上特点:
①每个类尽量继承自接口或抽象类、或两者的结合体;
②每个类不应该从具体类派生;
③由于有使用继承,尽量参照里氏替换原则(以下第四点所述)。
C#代码如下:
class PowerSupply
{
public void On() { }
public void Off() { }
public double Voltage { set; get; }
public double Current { set; get; }
}
class DCPowerSupply: PowerSupply
{
public double Power { set; get; }
}
class ACPowerSupply: PowerSupply
{
public double PowerFactor { set; get; }
}
class PowerSupplySeller
{
public void Sell(ACPowerSupply powerSupply)
{
//卖AC电源
}
public void Sell(DCPowerSupply powerSupply)
{
//卖DC电源
}
}
客户端代码如下:
class Program
{
static void Main(string[] args)
{
PowerSupplySeller seller = new PowerSupplySeller();
seller.Sell(new ACPowerSupply());
//下次想卖DC电源产品,就不得不在PowerSupplySeller类写入一个重载版的Sell方法,违背了开闭原则
}
}
正确的C#代码如下(只需对public void Sell(ACPowerSupply powerSupply)进行修改即可):
class PowerSupplySeller
{
public void Sell(PowerSupply powerSupply)
{
//卖AC或DC电源
}
}
客户端代码如下:
class Program
{
static void Main(string[] args)
{
PowerSupplySeller seller = new PowerSupplySeller();
seller.Sell(new ACPowerSupply());
seller.Sell(new DCPowerSupply());
}
}
以上不仅仅体现在形参上,还体现在方法或属性的返回值、字段类型等。
四、里氏替换原则
简述:可以使用父类的类型来代替子类的类型,但不改变子类的行为,继承必须确保父类所拥有的性质在子类中仍然成立。
C#代码如下:
class Meter
{
public double Voltage
{
get
{
//模仿返回一个值,这里以5.01V为准
double value = 5.01;
Console.WriteLine($"DigitalMeter,Voltage:{value}");
return value;
}
}
public double Current
{
get
{
//模仿返回一个值,这里以1.001A为准
double value = 1.001;
Console.WriteLine($"DigitalMeter,Current:{value}");
return value;
}
}
}
class DigitalMeter: Meter
{
}
class DCPowerSupply : Meter
{
public double Voltage
{
get
{
//模仿返回一个值,这里以12.02V为准
double value = 12.02;
Console.WriteLine($"DCPowerSupply,Voltage:{value}");
return value;
}
}
public double Current
{
get
{
//模仿返回一个值,这里以2.002A为准
double value = 2.002;
Console.WriteLine($"DCPowerSupply,Current:{value}");
return value;
}
}
}
客户端代码如下:
class Program
{
static void Main(string[] args)
{
Meter meter1 = new DigitalMeter();
Meter meter2 = new DCPowerSupply();
var voltage1 = meter1.Voltage;
var voltage2 = meter2.Voltage;
var current1 = meter1.Current;
var current2 = meter2.Current;
}
}
运行结果如下:
从以上可以看出,本来为DCPowerSupply的类,既然输出了Meter的信息,这就违背了里氏替换原则。
新修改的代码如下:
interface IMeter
{
double Voltage { get; }
double Current { get; }
}
class Meter: IMeter
{
public double Voltage
{
get
{
//模仿返回一个值,这里以5.01V为准
double value = 5.01;
Console.WriteLine($"DigitalMeter,Voltage:{value}");
return value;
}
}
public double Current
{
get
{
//模仿返回一个值,这里以1.001A为准
double value = 1.001;
Console.WriteLine($"DigitalMeter,Current:{value}");
return value;
}
}
}
class DigitalMeter: Meter
{
}
class DCPowerSupply : IMeter
{
public double Voltage
{
get
{
//模仿返回一个值,这里以12.02V为准
double value = 12.02;
Console.WriteLine($"DCPowerSupply,Voltage:{value}");
return value;
}
}
public double Current
{
get
{
//模仿返回一个值,这里以2.002A为准
double value = 2.002;
Console.WriteLine($"DCPowerSupply,Current:{value}");
return value;
}
}
}
客户端代码如下:
class Program
{
static void Main(string[] args)
{
IMeter meter1 = new DigitalMeter();
IMeter meter2 = new DCPowerSupply();
var voltage1 = meter1.Voltage;
var voltage2 = meter2.Voltage;
var current1 = meter1.Current;
var current2 = meter2.Current;
}
}
运行结果如下:
从以上代码可以看出,用父类代替子类时,不能影响其子类的行为,否则,就违背了里氏替换原则。
五、接口隔离原则
简述:要让接口尽量简单,功能一致的可以写在仪器,与接口无关的必须重新写接口,也就是说,不要让接口变得臃肿。
C#代码如下:
interface IInstrument
{
void Write(string command);
string Read();
double Voltage { get; }
double Current { get; }
}
以上代码有两组,一组为Write、Read,另外一组为Voltage、Current,按照功能分类的话,Write、Read应属于端口的读写,而Voltage、Current则为电表的读电压、读电流,按照接口隔离原则,需要进行隔离。
修改后的代码如下:
interface IPort
{
void Write(string command);
string Read();
}
interface IMeter
{
double Voltage { get; }
double Current { get; }
}
六、迪米特法则
简述:类A、类B之间本身没有直接关联关系,那么就不能相互调用,可以通过第三个类(类C)进行间接调用,降低类A与类B的耦合性,提高模块的相对独立性。
C#代码如下:
class DCPowerSupply
{
public Port Port { set; get; }
public double Voltage
{
set
{
Port.Write($"Voltage:{value}");
}
get
{
return Convert.ToDouble(Port.Read());
}
}
public double Current
{
set
{
Port.Write($"Current:{value}");
}
get
{
return Convert.ToDouble(Port.Read());
}
}
public void On() { }
public void Off() { }
}
class Port
{
public void Write(string command)
{
}
public string Read()
{
return new Random().NextDouble().ToString();
}
}
以上代码,Port类与DCPowerSupply类进行了相互调用,耦合性很强,改变其中一个类的方法,对调用的那个类有很大的影响。
修改之后的代码如下:
class DCPowerSupply
{
public double Voltage { set; get; }
public double Current { set; get; }
public void On() { }
public void Off() { }
}
class Port
{
public void Write(string command)
{
}
public string Read()
{
return new Random().NextDouble().ToString();
}
}
class DCPowerSupplyWithPort
{
DCPowerSupply powerSupply;
Port port;
public DCPowerSupplyWithPort()
{
powerSupply = new DCPowerSupply();
port = new Port();
}
public double Voltage
{
set
{
port.Write($"Voltage:{value}");
}
get
{
return Convert.ToDouble(port.Read());
}
}
public double Current
{
set
{
port.Write($"Current:{value}");
}
get
{
return Convert.ToDouble(port.Read());
}
}
public void On()
{
powerSupply.On();
}
public void Off()
{
powerSupply.Off();
}
}
以上代码,可以将Port类与DCPowerSupply类进行分离,使用了第三个类DCPowerSupplyWithPort,使得Port类与DCPowerSupply类松耦合,独立性强,符合迪米特法则,同样,该法则还适用于三层架构。
c#六大设计原则(以仪器代码为例)的更多相关文章
- IOS设计模式的六大设计原则之开放-关闭原则(OCP,Open-Close Principle)
定义 一个软件实体(如类.模块.函数)应当对扩展开放,对修改关闭. 定义解读 在项目开发的时候,都不能指望需求是确定不变化的,大部分情况下,需求是变化的.那么如何应对需求变化的情况?这就是开放-关闭原 ...
- PHP设计模式的六大设计原则
PHP设计模式的六大设计原则 1 简介 软件设计最大的难题就是应对需求的变化,但是纷繁复杂的需求变化却是不可预料的.此时,我们可以通过六大设计原则良好的应对未来的变化. 2 讲解 2.1 单一职责原则 ...
- 十年阿里java架构师的六大设计原则和项目经验
先看一幅图吧: 这幅图清晰地表达了六大设计原则,但仅限于它们叫什么名字而已,它们具体是什么意思呢?下面我将从原文.译文.理解.应用,这四个方面分别进行阐述. 1.单一职责原则(Single Res ...
- iOS----------设计模式的六大设计原则------>开放-关闭原则(OCP,Open-Close Principle)
定义 一个软件实体(如类.模块.函数)应当对扩展开放,对修改关闭. 定义解读 在项目开发的时候,都不能指望需求是确定不变化的,大部分情况下,需求是变化的.那么如何应对需求变化的情况?这就是开放-关闭原 ...
- JAVA设计模式总结之六大设计原则
从今年的七月份开始学习设计模式到9月底,设计模式全部学完了,在学习期间,总共过了两篇:第一篇看完设计模式后,感觉只是脑子里面有印象但无法言语.于是决定在看一篇,到9月份第二篇设计模式总于看完了,这一篇 ...
- 六大设计原则——单一职责原则【Single Responsibility Principle】
声明:本文内容是从网络书籍整理而来,并非原创. 用户管理的例子 先看一张用户管理的类图: 再看一眼上面的图,思考:这样合理吗? 这个接口是一个很糟糕的设计! 用户的属性和行为竟然混合在一起!!! 正 ...
- SOLID 设计原则 In C# 代码实现
[S] Single Responsibility Principle (单一职责原则) 认为一个对象应该仅只有一个单一的职责 namespace SingleResponsibilityPrinci ...
- Java六大设计原则
类的设计原则 依赖倒置原则-Dependency Inversion Principle (DIP) 里氏替换原则-Liskov Substitution Principle (LSP) 接口 ...
- 六大设计原则(C#)
为什么要有设计原则,我觉得一张图片就可以解释这一切 一.单一职责原则(SRP) 对于一个类而言,应该只有一个发生变化的原因.(单一职责不仅仅是指类) 如果一个模块需要修改,它肯定是有原因的,除此原因之 ...
随机推荐
- UILabel实现自适应宽高需要注意的地方(三)
一.需求图如下所示 UILabel 的高度自适应 UILabel中的段落间距可设置 图片效果如下: 调整段落适应长宽高方式: 需求: 保证"游戏玩法 ...
- 关于防范csrf攻击基于token鉴权
在web开发中,之前都使用cookie + session方式来实现身份认证鉴权.但是现在前后端分离,以及终端有可能不支持cookie的情况下,一般都采用token方式.现在系统设计思路如下: 服务端 ...
- 介绍两种Timer定时器的使用
第一种, 直接实例化Timer类,设置时间间隔,到达时间后执行想要执行的事件.代码示例: using System; using System.Collections.Generic; using S ...
- STL函数static void (* set_malloc_handler(void (*f)()))()与函数指针解析
在C++ STL的SGI实现版本中,一级空间配置器class __malloc_alloc_template中有一个静态函数的实现如下: static void (*set_malloc_handle ...
- SpringBoot实现文件上传
前言参考:快速开发第一个SpringBoot应用 这篇文章会讲解如何使用SpringBoot完成一个文件上传的过程,并且附带一些SpringBoot开发中需要注意的地方 首先我们写一个文件上传的htm ...
- 【python3两小时根本不够】入门笔记04:线程+Lock安全同步
有了简单爬虫,但是效率实在是太慢,于是决定启用线程进行爬取数据 但是对于临界资源的定义不好把握,思路如下: 1.定义队列(Queue的数据结构,List也可,安全性待考究) demo:https:// ...
- Centos7.6 双网卡,修改默认路由
centos修改默认路由 需求:服务器有两个网卡,一个是内网网卡,一个是外网网卡,但是默认的路由是内网: route -n 列出所有路由: 删除默认的路由: route del default gw ...
- Azkaban 3.73.1 集群搭建(Multiple Executor)
一.简介 Azkaban是LinkedIn开源的任务调度框架,采用java编写 Azkaban功能和特点: 任务的依赖处理 任务监控,失败告警 任务流的可视化 任务权限管理 Azkaban具有轻量可插 ...
- 常用的方法论-SWOT
- 2018.8.17 2018暑假集训 关于dp的一些感想(以目前的知识水平)
学了这么长时间的dp似乎还是不怎么样 谨以此篇文字记录一年以来与dp斗智斗勇的各种经历 关于dp(也就是动态规划)似乎对于每个OIer来说都是一个永远的噩梦. 刚刚开始学dp的时候完全搞不明白(只是觉 ...