C#软件设计——小话设计模式原则之:单一职责原则SRP
前言:上篇C#软件设计——小话设计模式原则之:依赖倒置原则DIP简单介绍了下依赖倒置的由来以及使用,中间插了两篇WebApi的文章,这篇还是回归正题,继续来写写设计模式另一个重要的原则:单一职责原则。
软件设计原则系列文章索引
- C#软件设计——小话设计模式原则之:依赖倒置原则DIP
- C#软件设计——小话设计模式原则之:单一职责原则SRP
- C#软件设计——小话设计模式原则之:接口隔离原则ISP
- C#软件设计——小话设计模式原则之:开闭原则OCP
一、原理介绍
1、官方定义
单一职责原则,英文缩写SRP,全称Single Responsibility Principle。
原始定义:There should never be more than one reason for a class to change。
官方翻译:应该有且仅有一个原因引起类的变更。简单点说,一个类,最好只负责一件事,只有一个引起它变化的原因。
2、自己理解
2.1、原理解释
上面的定义不难理解,引起类变化的原因不能多于一个。也就是说每一个类只负责自己的事情,此所谓单一职责。
我们知道,在OOP里面,高内聚、低耦合是软件设计追求的目标,而单一职责原则可以看做是高内聚、低耦合的引申,将职责定义为引起变化的原因,以提高内聚性,以此来减少引起变化的原因。职责过多,可能引起变化的原因就越多,这将是导致职责依赖,相互之间就产生影响,从而极大的损伤其内聚性和耦合度。单一职责通常意味着单一的功能,因此不要为类实现过多的功能点,以保证实体只有一个引起它变化的原因。
不管是从官方定义,还是对“单一职责”名称的解释,都能很好的理解单一职责原则的意义。其实在软件设计中,要真正用好单一职责原则并不简单,因为遵循这一原则最关键的地方在于职责的划分,博主的理解是职责的划分是根据需求定的,同一个类(接口)的设计,在不同的需求里面,可能职责的划分并不一样,为什么这么说呢?我们来看下面的例子。
二、场景示例
关于单一职责原则的原理,我们就不做过多的解释了。重点是职责的划分!重点是职责的划分!重点是职责的划分!重要的事情说三遍。下面根据一个示例场景来看看如何划分职责。
假定现在有如下场景:国际手机运营商那里定义了生产手机必须要实现的接口,接口里面定义了一些手机的属性和行为,手机生产商如果要生成手机,必须要实现这些接口。
1、初始设计——初稿
我们首先以手机作为单一职责去设计接口,方案如下。
/// <summary>
/// 充电电源
/// </summary>
public class ElectricSource
{ }
public interface IMobilePhone
{
//运行内存
string RAM { get; set; } //手机存储内存
string ROM { get; set; } //CPU主频
string CPU { get; set; } //屏幕大小
int Size { get; set; } //手机充电接口
void Charging(ElectricSource oElectricsource); //打电话
void RingUp(); //接电话
void ReceiveUp(); //上网
void SurfInternet();
}
然后我们的手机生产商去实现这些接口
//具体的手机示例
public class MobilePhone:IMobilePhone
{
public string RAM
{
get {throw new NotImplementedException();}
set{ throw new NotImplementedException();}
} public string ROM
{
get{throw new NotImplementedException();}
set{ throw new NotImplementedException();}
} public string CPU
{
get{ throw new NotImplementedException();}
set{ throw new NotImplementedException();}
} public int Size
{
get{throw new NotImplementedException();}
set{throw new NotImplementedException();}
} public void Charging(ElectricSource oElectricsource)
{
throw new NotImplementedException();
} public void RingUp()
{
throw new NotImplementedException();
} public void ReceiveUp()
{
throw new NotImplementedException();
} public void SurfInternet()
{
throw new NotImplementedException();
}
}
这种设计有没有问题呢?这是一个很有争议的话题。单一职责原则要求一个接口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情,原则上来说,我们以手机作为单一职责去设计,也是有一定的道理的,因为我们接口里面都是定义的手机相关属性和行为,引起接口变化的原因只可能是手机的属性或者行为发生变化,从这方面考虑,这种设计是有它的合理性的,如果你能保证需求不会变化或者变化的可能性比较小,那么这种设计就是合理的。但实际情况我们知道,现代科技日新月异,科技的进步促使着人们不断在手机原有基础上增加新的属性和功能。比如有一天,我们给手机增加了摄像头,那么需要新增一个像素的属性,我们的接口和实现就得改吧,又有一天,我们增加移动办公的功能,那么我们的接口实现是不是也得改。由于上面的设计没有细化到一定的粒度,导致任何一个细小的改动都会引起从上到下的变化,有一种“牵一发而动全身”的感觉。所以需要细化粒度,下面来看看我们如何变更设计。
2、二次设计——变更
我们将接口细化
//手机属性接口
public interface IMobilePhoneProperty
{
//运行内存
string RAM { get; set; } //手机存储内存
string ROM { get; set; } //CPU主频
string CPU { get; set; } //屏幕大小
int Size { get; set; } //摄像头像素
string Pixel { get; set; }
} //手机功能接口
public interface IMobilePhoneFunction
{
//手机充电接口
void Charging(ElectricSource oElectricsource); //打电话
void RingUp(); //接电话
void ReceiveUp(); //上网
void SurfInternet(); //移动办公
void MobileOA();
}
实现类
//手机属性实现类
public class MobileProperty:IMobilePhoneProperty
{ public string RAM
{
get{ throw new NotImplementedException();}
set{ throw new NotImplementedException();}
} public string ROM
{
get{ throw new NotImplementedException();}
set{ throw new NotImplementedException();}
} public string CPU
{
get{ throw new NotImplementedException();}
set{throw new NotImplementedException();}
} public int Size
{
get{throw new NotImplementedException();}
set{throw new NotImplementedException();}
} public string Pixel
{
get{throw new NotImplementedException();}
set{throw new NotImplementedException();}
}
} //手机功能实现类
public class MobileFunction:IMobilePhoneFunction
{ public void Charging(ElectricSource oElectricsource)
{
throw new NotImplementedException();
} public void RingUp()
{
throw new NotImplementedException();
} public void ReceiveUp()
{
throw new NotImplementedException();
} public void SurfInternet()
{
throw new NotImplementedException();
} public void MobileOA()
{
throw new NotImplementedException();
}
} //具体的手机实例
public class HuaweiMobile
{
private IMobilePhoneProperty m_Property;
private IMobilePhoneFunction m_Func;
public HuaweiMobile(IMobilePhoneProperty oProperty, IMobilePhoneFunction oFunc)
{
m_Property = oProperty;
m_Func = oFunc;
}
}
对于上面题的问题,这种设计能够比较方便的解决,如果是增加属性,只需要修改IMobilePhoneProperty和MobileProperty即可;如果是增加功能,只需要修改IMobilePhoneFunction和MobileFunction即可。貌似完胜第一种解决方案。那么是否这种解决方案就完美了呢?答案还是看情况。原则上,我们将手机的属性和功能分开了,使得职责更加明确,所有的属性都由IMobilePhoneProperty接口负责,所有的功能都由IMobilePhoneFunction接口负责,如果是需求的粒度仅仅到了属性和功能这一级,这种设计确实是比较好的。反之,如果粒度再细小一些呢,那我们这种职责划分是否完美呢?比如我们普通的老人机只需要一些最基础的功能,比如它只需要充电、打电话、接电话的功能,但是按照上面的设计,它也要实现IMobilePhoneFunction接口,某一天,我们增加了一个新的功能玩游戏,那么我们就需要在接口上面增加一个方法PlayGame()。可是我们老人机根本用不着实现这个功能,可是由于它实现了该接口,它的内部实现也得重新去写。从这点来说,以上的设计还是存在它的问题。那么,我们如何继续细化接口粒度呢?
3、最终设计——成型
接口细化粒度设计如下
//手机基础属性接口
public interface IMobilePhoneBaseProperty
{
//运行内存
string RAM { get; set; } //手机存储内存
string ROM { get; set; } //CPU主频
string CPU { get; set; } //屏幕大小
int Size { get; set; }
} //手机扩展属性接口
public interface IMobilePhoneExtentionProperty
{
//摄像头像素
string Pixel { get; set; }
} //手机基础功能接口
public interface IMobilePhoneBaseFunc
{
//手机充电接口
void Charging(ElectricSource oElectricsource); //打电话
void RingUp(); //接电话
void ReceiveUp();
} //手机扩展功能接口
public interface IMobilePhoneExtentionFunc
{
//上网
void SurfInternet(); //移动办公
void MobileOA(); //玩游戏
void PlayGame();
}
实现类和上面类似
//手机基础属性实现
public class MobilePhoneBaseProperty : IMobilePhoneBaseProperty
{ public string RAM
{
get{throw new NotImplementedException();}
set{throw new NotImplementedException();}
} public string ROM
{
get{throw new NotImplementedException();}
set {throw new NotImplementedException();}
} public string CPU
{
get{throw new NotImplementedException();}
set{ throw new NotImplementedException();}
} public int Size
{
get{ throw new NotImplementedException();}
set{ throw new NotImplementedException();}
}
} //手机扩展属性实现
public class MobilePhoneExtentionProperty : IMobilePhoneExtentionProperty
{ public string Pixel
{
get{ throw new NotImplementedException();}
set{ throw new NotImplementedException();}
}
} //手机基础功能实现
public class MobilePhoneBaseFunc : IMobilePhoneBaseFunc
{
public void Charging(ElectricSource oElectricsource)
{
throw new NotImplementedException();
} public void RingUp()
{
throw new NotImplementedException();
} public void ReceiveUp()
{
throw new NotImplementedException();
}
} //手机扩展功能实现
public class MobilePhoneExtentionFunc : IMobilePhoneExtentionFunc
{ public void SurfInternet()
{
throw new NotImplementedException();
} public void MobileOA()
{
throw new NotImplementedException();
} public void PlayGame()
{
throw new NotImplementedException();
}
}
此种设计能解决上述问题,细分到此粒度,这种方案基本算比较完善了。能不能算完美?这个得另说。接口的粒度要设计到哪一步,取决于需求的变更程度,或者说取决于需求的复杂度。
三、总结
以上通过一个应用场景简单介绍了下单一职责原则的使用,上面三种设计,没有最合理,只有最合适。理解单一职责原则,最重要的就是理解职责的划分,职责划分的粒度取决于需求的粒度,最后又回到了那句话:没有最好的设计,只有最适合的设计。欢迎园友拍砖斧正。如果园友们觉得本文对你有帮助,请帮忙推荐,博主将继续努力~~
C#软件设计——小话设计模式原则之:单一职责原则SRP的更多相关文章
- C#软件设计——小话设计模式原则之:依赖倒置原则DIP
前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做“全战”, ...
- C#软件设计——小话设计模式原则之:接口隔离原则ISP
前言:有朋友问我,设计模式原则这些东西在园子里都讨论烂了,一搜一大把的资料,还花这么大力气去整这个干嘛.博主不得不承认,园子里确实很多这方面的文章,并且不乏出色的博文.博主的想法是,既然要完善知识体系 ...
- C#软件设计——小话设计模式原则之:开闭原则OCP
前言:这篇继续来看看开闭原则.废话少说,直接入正题. 软件设计原则系列文章索引 C#软件设计——小话设计模式原则之:依赖倒置原则DIP C#软件设计——小话设计模式原则之:单一职责原则SRP C#软件 ...
- 开放-封闭原则(OCP)开-闭原则 和 依赖倒转原则,单一职责原则
单一职责原则 1.单一职责原则(SRP),就一个类而言,应该仅有一个引起它变化的原因 2.如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会消弱或抑制这个类完成其他职责的能力. ...
- IOS设计模式的六大设计原则之单一职责原则(SRP,Single Responsibility Principle)
定义 就一个类而言,应该仅有一个引起它变化的原因. 定义解读 这是六大原则中最简单的一种,通俗点说,就是不存在多个原因使得一个类发生变化,也就是一个类只负责一种职责的工作. 优点 类的复杂度降低,一个 ...
- 最简单直接地理解Java软件设计原则之单一职责原则
理论性知识 定义 单一职责原则, Single responsibility principle (SRP): 一个类,接口,方法只负责一项职责: 不要存在多余一个导致类变更的原因: 优点 降低类的复 ...
- 设计模式笔记:单一职责原则(SRP, Single Responsibility Principle)
1. 单一职责原则核心思想 一个类应该有且只有一个变化的原因. 2. 为什么引入单一职责原则 单一职责原则将不同的职责分离到单独的类,每一个职责都是一个变化的中心. 在SRP中,把职责定义为变化的原因 ...
- C#设计模式系列:单一职责原则(Single Responsibility Principle)
1.单一职责原则的核心思想 一个类应该有且只有一个变化的原因. 2.为什么要引入单一职责原则 单一职责原则将不同的职责分离到单独的类,每一个职责都是一个变化的中心.当需求变化时,这个变化将通过更改职责 ...
- java设计模式学习笔记--单一职责原则
单一职责原则注意事项和细节 1.降低类的复杂度,一个类只负责一项职责 2.提高可读性,可维护性 3.降低变更引起的风险 4.通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单 ...
随机推荐
- 【blade的UI设计】理解前端MVC与分层思想
前言 最近校招要来了,很多大三的同学一定按捺不住心中的焦躁,其中有期待也有彷徨,或许更多的是些许担忧,最近在开始疯狂的复习了吧 这里小钗有几点建议给各位: ① 不要看得太重,关心则乱,太紧张反而表现不 ...
- $(function) ready onload 等区别
新手接触javascript.jquery的时候不可避免的要接触题目所标识的相关内容,反复看过几次一到用的时候总是不踏实,写文以记之. 符号“$”是jquery对象(个人这样理解,拥有函数的用法).接 ...
- javascript 函数初探 (一)--- 神马是函数
神马是函数? 所谓函数,本质上是一种代码的分组形式.我们可以通过这种形式赋予某组代码一个名字,以便与之后的调用.下面,我们来示范以下函数的声明: function sum(a, b){ var c = ...
- SharePoint 2013 图文开发系列之定义站点模板
SharePoint站点模板是一个非常好的功能,方便我们开发一类网站,然后在此基础上做二次开发,对于SharePoint的使用,有着举足轻重的作用. 因为篇幅比较长,所以加上目录,方便大家查看: 一. ...
- php 7.0 安装mcrypt
1. 添加ppa源 sudo add-apt-repository ppa:ondrej/php 2. 升级源 sudo apt-get update && sudo apt-get ...
- iOS开发之Bug--UITextField使用时文字向下偏移问题
这个问题,在之前项目中偶然遇到过,但是bug不明显,也不知道具体是哪种重现方式重现的,所以一直暂时略过了,但是随着项目的系统的规模变大,代码量越多, 这个问题出现的越来越明显了. 于是就只能靠百度和g ...
- 如何在 ASP.NET 4.6 与 IIS10 中运用 HTTP/2 ?
在过去十年中,Web 技术已经取得了种种进展.从基本的 HTML 开始,网页发展出更丰富的外观和感觉,变得更加直观,对用户更加友好并且越来越大放异彩.这些变化的关键贡献来自于一些新的和翻新技术,且借力 ...
- IRIS数据集的分析-数据挖掘和python入门-零门槛
所有内容都在python源码和注释里,可运行! ########################### #说明: # 撰写本文的原因是,笔者在研究博文“http://python.jobbole.co ...
- spring mvc生成注册验证码
通过Spring MVC为系统添加验证码 1:布局登陆页面,用户名,密码,填写验证码的文本框,及验证码的图片及点击换图 <%@ taglib prefix="c" uri=& ...
- SQLite学习笔记(九)&&pager模块
概述 通过上一篇文章的分析,我们知道了pager模块在整个sqlite中所处的位置.它是sqlite的核心模块,充当了多种重要角色.作为一个事务管理器,它通过并发控制和故障恢复实现事务的ACID特性, ...