SOLID 原则基本概念:
程序设计领域, SOLID (单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期 引入的记忆术首字母缩略字,指代了面向对象编程和面向对象设计的五个基本原则。当这些原则被一起应用时,它们使得一个程序员开发一个容易进行软件维护和扩展的系统变得更加可能SOLID被典型的应用在测试驱动开发上,并且是敏捷开发以及自适应软件开发的基本原则的重要组成部分。
[S] Single Responsibility Principle (单一功能原则)
单一功能原则 :单一功能原则 认为对象应该仅具有一种单一功能的概念。
换句话说就是让一个类只做一种类型责任,当这个类需要承担其他类型的责任的时候,就需要分解这个类。在所有的SOLID原则中,这是大多数开发人员感到最能完全理解的一条。严格来说,这也可能是违反最频繁的一条原则了。单一责任原则可以看作是低耦合、高内聚在面向对象原则上的引申,将责任定义为引起变化的原因,以提高内聚性来减少引起变化的原因。责任过多,可能引起它变化的原因就越多,这将导致责任依赖,相互之间就产生影响,从而极大的损伤其内聚性和耦合度。单一责任,通常意味着单一的功能,因此不要为一个模块实 现过多的功能点,以保证实体只有一个引起它变化的原因。
namespace SOLID { public class Users { /// <summary> /// 支付 /// </summary> public void Pay(){} /// <summary> /// 数据库操作 /// </summary> public void DataAccess(){} /// <summary> /// 日志操作 /// </summary> public void Logger(){} } }
在这个用户类中有这三个功能:1.支付逻辑,2数据库逻辑,3.日志操作。如果将这三个功能结合在一个类中,可能会出现修改部分代码时会破坏其他的部分。多个功能也使这个用户类难以理解,降低了内聚性。所以最好就是将这个类分离为三个分离的类,每个类仅仅有一个功能。
namespace SOLID { /// <summary> /// 数据库操作 /// </summary> class DataAccess { }
/// <summary> /// 日志 /// </summary> class Logger { }
/// <summary> /// 支付 /// </summary> class Pay { } }
[o] Open Close Principle (开闭原则)
开闭原则(ocp) 认为“软件体应该是对于扩展开放的,但是对于修改封闭的”的概念。
软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。这个原则是诸多面向对象编程原则中最抽象、最难理解的一个。
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。
可以使用变化和不变来说明:封装不变部分,开放变化部分,一般使用接口继承实现方式来实现“开放”应对变化,说大白话就是:你不是要变化吗?,那么我就让你继承实现一个对象,用一个接口来抽象你的职责,你变化越多,继承实现的子类就越多。
abstract class DataAccess { public abstract void OpenConnection(); public abstract void CloseConnection(); public abstract void ExecuteCommand(); } /// <summary> /// SQL /// </summary> class SqlDataAccess : DataAccess { /// <summary> /// 打开SQL数据库 /// </summary> public override void OpenConnection(){} /// <summary> /// 关闭Sql数据连接 /// </summary> public override void CloseConnection(){} /// <summary> /// 执行Sql数据命令 /// </summary> public override void ExecuteCommand(){} } /// <summary> /// ORACLE /// </summary> class OracleDataAccess : DataAccess { /// <summary> /// 打开Oracle数据连接 /// </summary> public override void OpenConnection(){} /// <summary> /// 关闭Oracle数据连接 /// </summary> public override void CloseConnection(){} /// <summary> /// 执行Oracle数据命令 /// </summary> public override void ExecuteCommand(){} }
[L] Liskov Substitution Principle(里氏替换原则)
里氏替换原则 :里氏替换原则 认为“程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的”的概念。
软件工程大师Robert C. Martin把里氏替换原则最终简化为一句话:“Subtypes must be substitutable for their base types”。也就是,子类必须能够替换成它们的基类。即:子类应该可以替换任何基类能够出现的地方,并且经过替换以后,代码还能正常工作。另外,不应该 在代码中出现if/else之类对子类类型进行判断的条件。里氏替换原则LSP是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类 型的模块在无需修改的情况下就可以扩展。在很多情况下,在设计初期我们类之间的关系不是很明确,LSP则给了我们一个判断和设计类之间关系的基准:需不需 要继承,以及怎样设计继承关系。
当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系。继承对于OCP,就相当于多态性对于里氏替换原则。子类可以代替基类,客户使用基类,他们不需要知道派生类所做的事情。这是一个针对行为职责可替代的原则,如果S是T的子类型,那么S对象就应该在不改变任何抽象属性情况下替换所有T对象。
class Rectangle { protected int width = 0; protected int height = 0; public virtual void SetWidth(int width) { this.width = width; } public virtual void SetHeight(int height) { this.height = height; } public virtual int GetArea() { return this.width * this.height; } } class Square : Rectangle { public override void SetHeight(int height) { this.height = height; this.width = height; } public override void SetWidth(int width) { this.height = width; this.width = width; } }
[I] Interface Segregation Principle(接口隔离原则)
接口隔离原则 :接口隔离原则 认为“多个特定客户端接口要好于一个宽泛用途的接口”的概念。
不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。注意:在代码中应用ISP并不一定意味着服务就是绝对安全的。仍然需要采用良好的编码实践,以确保正确的验证与授权。
这个原则起源于施乐公司,他们需要建立了一个新的打印机系统,可以执行诸如装订的印刷品一套,传真多种任务。此系统软件创建从底层开始编制,并实现了这些 任务功能,但是不断增长的软件功能却使软件本身越来越难适应变化和维护。每一次改变,即使是最小的变化,有人可能需要近一个小时的重新编译和重新部署。这 是几乎不可能再继续发展,所以他们聘请罗伯特Robert帮助他们。他们首先设计了一个主要类Job,几乎能够用于实现所有任务功能。只要调用Job类的 一个方法就可以实现一个功能,Job类就变动非常大,是一个胖模型啊,对于客户端如果只需要一个打印功能,但是其他无关打印的方法功能也和其耦合,ISP 原则建议在客户端和Job类之间增加一个接口层,对于不同功能有不同接口,比如打印功能就是Print接口,然后将大的Job类切分为继承不同接口的子 类,这样有一个Print Job类,等等。
interface IDataAccess { void OpenConnection(); void CloseConnection(); } interface ISqlDataAccess : IDataAccess { void ExecuteSqlCommand(); } interface IOracleDataAccess : IDataAccess { void ExecuteOracleCommand(); } class SqlDataAccess : ISqlDataAccess { /// <summary> /// 执行Sql数据命令 /// </summary> public void ExecuteSqlCommand(){} /// <summary> /// 打开Sql数据连接 /// </summary> public void OpenConnection(){} /// <summary> /// 关闭Sql数据连接 /// </summary> public void CloseConnection(){} } class OracleDataAccess : IOracleDataAccess { /// <summary> /// 执行Oracle数据命令 /// </summary> public void ExecuteOracleCommand(){} /// <summary> /// 打开Oracle数据连接 /// </summary> public void OpenConnection(){} /// <summary> /// 关闭Oracle数据连接 /// </summary> public void CloseConnection(){} }
[D] Dependency Inversion Principle(依赖反转原则)
依赖反转原则: 依赖反转原则 认为一个方法应该遵从“依赖于抽象而不是一个实例” 的概念。依赖注入是该原则的一种实现方式。
依赖倒置原则(Dependency Inversion Principle,DIP)规定:代码应当取决于抽象概念,而不是具体实现。
高层模块不应该依赖于低层模块,二者都应该依赖于抽象
抽象不应该依赖于细节,细节应该依赖于抽象
类可能依赖于其他类来执行其工作。但是,它们不应当依赖于该类的特定具体实现,而应当是它的抽象。这个原则实在是太重要了,社会的分工化,标准化都 是这个设计原则的体现。显然,这一概念会大大提高系统的灵活性。如果类只关心它们用于支持特定契约而不是特定类型的组件,就可以快速而轻松地修改这些低级 服务的功能,同时最大限度地降低对系统其余部分的影响。
interface IBankAccount { long BankNumber { get; set; } // 卡号 decimal Balance { get; set; } // 余额 }
// 转账人 interface ITransferSource : IBankAccount { void CutPayment(decimal value); }
// 收款人 interface ITransferDestination : IBankAccount { void AddMoney(decimal value); }
class BankAccout : IBankAccount, ITransferSource, ITransferDestination { public long BankNumber { get; set; } public decimal Balance { get; set; } public void CutPayment(decimal value) { Balance -= value; } public void AddMoney(decimal value) { Balance += value; } }
class TransferAmount { public decimal Amount { get; set; } public void Transfer(ITransferSource source, ITransferDestination dest) { source.CutPayment(Amount); dest.AddMoney(Amount); } }
参考于:
http://www.cnblogs.com/Ax0ne/p/3619481.html
http://www.cnblogs.com/shanyou/archive/2009/09/21/1570716.html