The S.O.L.I.D. Object Oriented Programming(OOP) Principles SOLID

出處:http://www.codeproject.com/Articles/60845/The-S-O-L-I-D-Object-Oriented-Programming-OOP-Prin

Introduction

What does it take to be an Object Oriented Programmer? There was a time where I believed all that meant was that you worked with a language such as C#, C++, or Java. However, the more I get acquainted with newer technologies, the more I realize that there is a set of fundamentals core to the title. And really, these fundamentals are about architecting the best, most update-able, scalable systems. Just yesterday while diving into DataObjects.NET, I was greeted by Domain Driven Design(DDD)– a popular architectural abstraction. It motivated me to think about the basics, which is the purpose of this article.

The S.O.L.I.D. Principles of Class Design

The S.O.L.I.D. principles seem to be the least common denominator of creating great classes; even before Design Patterns. I recommend taking some time to really think about each of them and how you can apply them. Let’s dive in, one by one.

The Single Responsibility Principle

There should never be more than one reason for a class to change. Basically, this means that your classes should exist for one purpose only. For example, let’s say you are creating a class to represent a SalesOrder. You would not want that class to save to the database, as well as export an XML-based receipt. Why? Well if later on down the road, you want to change database type (or if you want to change your XML schema), you’re allowing one responsibility’s changes to possibly alter another. Responsibility is the heart of this principle, so to rephrase there should never be more than one responsibility per class.

The Open Closed Principle

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. At first, this seems to be contradictory: how can you make an object behave differently without modifying it? The answer: by using abstractions, or by placing behavior(responsibility) in derivative classes. In other words, by creating base classes with override-able functions, we are able to create new classes that do the same thing differently without changing the base functionality. Further, if properties of the abstracted class need to be compared or organized together, another abstraction should handle this. This is the basis of the “keep all object variables private” argument.

The Liskov Substitution Principle

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. In other words, if you are calling a method defined at a base class upon an abstracted class, the function must be implemented properly on the subtype class. Or, “when using an object through its base class interface, [the] derived object must not expect such users to obey preconditions that are stronger than those required by the base class.” The ever-popular illustration of this is the square-rectangle example. Turns out a square is not a rectangle, at least behavior-wise.

The Interface Segregation Principle

Clients should not be forced to depend upon interfaces that they do not use. My favorite version of this is written as “when a client depends upon a class that contains interfaces that the client does not use, but that other clients do use, then that client will be affected by the changes that those other clients force upon the class.” Kinda sounds like the inheritance specific single responsibility principle.

The Dependency Inversion Principle

Depend on abstractions, not on concretions or High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions. (I like the first explanation the best.) This is very closely related to the open closed principle we discussed earlier. By passing dependencies (such as connectors to devices, storage) to classes as abstractions, you remove the need to program dependency specific. Here’s an example: an Employee class that needs to be able to be persisted to XML and a database. If we placed ToXML() and ToDB() functions in the class, we’d be violating the single responsibility principle. If we created a function that took a value that represented whether to print to XML or to DB, we’d be hard-coding a set of devices and thus be violating the open closed principle. The best way to do this would be to:
  1. Create an abstract class (named DataWriter, perhaps) that can be inherited from for XML (XMLDataWriter) or DB (DbDataWriter) Saving, and then
  2. Create a class (named EmployeeWriter) that would expose an Output(DataWriter saveMethod) that accepts a dependency as an argument. See how the Output method is dependent upon the abstractions just as the output types are? The dependencies have been inverted. Now we can create new types of ways for Employee data to be written, perhaps via HTTP/HTTPS by creating abstractions, and without modifying any of our previous code! No rigidity–the desired outcome.

Sources

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

=======================================================================

OO的五大原則

出處:http://blog.csdn.net/httphttpcn/article/details/5783903

在学习和使用OO设计的时候,我们应该明白:OO的出现使得软件工程师们能够用更接近真实世界的方法描述软件系统。然而,软件毕竟是建立在抽象层次上的东西,再怎么接近真实,也不能替代真实或被真实替代。 
OO设计的五大原则之间并不是相互孤立的。彼此间存在着一定关联,一个可以是另一个原则的加强或是基础。违反其中的某一个,可能同时违反了其余的原则。因此应该把这些原则融会贯通,牢记在心! 
OO的五大原则是指SRP、OCP、LSP、DIP、ISP。 
1. SRP(Single Responsibility Principle 单一职责原则) 
单一职责很容易理解,也很容易实现。所谓单一职责,就是一个设计元素只做一件事。什么是“只做一件事”?简单说就是少管闲事。现实中就是如此,如果要你专心做一件事情,任何人都有信心可以做得很出色。 
OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。 
2. OCP :开闭原则,很简单,一句话:“Closed for Modification; Open for Extension”——“对变更关闭;对扩展开放”。开闭原则其实没什么好讲的,我将其归结为一个高层次的设计总则。OCP的动机很简单:软件是变化的。不论是优质的设计还是低劣的设计都无法回避这一问题。OCP说明了软件设计应该尽可能地使架构稳定而又容易满足不同的需求。 为什么要OCP?答案也很简单——重用。 
3.LSP——里氏替换原则 
OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性“抽象”是语言提供的功能。“多态”由继承语义实现。 如此,问题产生了:“我们如何去度量继承关系的质量?” 
Liskov于1987年提出了一个关于继承的原则“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“继承必须确保超类所拥有的性质在子类中仍然成立。”也就是说,当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系。 
该原则称为Liskov Substitution Principle——里氏替换原则。 
我们来研究一下LSP的实质。学习OO的时候,我们知道,一个对象是一组状态和一系列行为的组合体。状态是对象的内在特性,行为是对象的外在特性。LSP所表述的就是在同一个继承体系中的对象应该有共同的行为特征。 
这一点上,表明了OO的继承与日常生活中的继承的本质区别。举一个例子:生物学的分类体系中把企鹅归属为鸟类。我们模仿这个体系,设计出这样的类和关系。 


类“鸟”中有个方法fly,企鹅自然也继承了这个方法,可是企鹅不能飞阿,于是,我们在企鹅的类中覆盖了fly方法,告诉方法的调用者:企鹅是不会飞的。这完全符合常理。但是,这违反了LSP,企鹅是鸟的子类,可是企鹅却不能飞!需要注意的是,此处的“鸟”已经不再是生物学中的鸟了,它是软件中的一个类、一个抽象。 
有人会说,企鹅不能飞很正常啊,而且这样编写代码也能正常编译,只要在使用这个类的客户代码中加一句判断就行了。但是,这就是问题所在!首先,客户代码和“企鹅”的代码很有可能不是同时设计的,在当今软件外包一层又一层的开发模式下,你甚至根本不知道两个模块的原产地是哪里,也就谈不上去修改客户代码了。客户程序很可能是遗留系统的一部分,很可能已经不再维护,如果因为设计出这么一个“企鹅”而导致必须修改客户代码,谁应该承担这部分责任呢?(大概是上帝吧,谁叫他让“企鹅”不能飞的。^_^)“修改客户代码”直接违反了OCP,这就是OCP的重要性。违反LSP将使既有的设计不能封闭! 

修正后的设计如下: 

LSP并没有提供解决这个问题的方案,而只是提出了这么一个问题。 于是,工程师们开始关注如何确保对象的行为。1988年,B. Meyer提出了Design by Contract(契约式设计)理论。DbC从形式化方法中借鉴了一套确保对象行为和自身状态的方法,其基本概念很简单: 

每个方法调用之前,该方法应该校验传入参数的正确性,只有正确才能执行该方法,否则认为调用方违反契约,不予执行。这称为前置条件(Pre-condition)。 
一旦通过前置条件的校验,方法必须执行,并且必须确保执行结果符合契约,这称之为后置条件(Post-condition)。 
对象本身有一套对自身状态进行校验的检查条件,以确保该对象的本质不发生改变,这称之为不变式(Invariant)。 
以上是单个对象的约束条件。为了满足LSP,当存在继承关系时,子类中方法的前置条件必须与超类中被覆盖的方法的前置条件相同或者更宽松;而子类中方法的后置条件必须与超类中被覆盖的方法的后置条件相同或者更为严格。 

4.DIP 依赖倒置原则 
依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。 
简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述: 
抽象不应当依赖于细节;细节应当依赖于抽象; 
要针对接口编程,不针对实现编程。 

5.ISP 接口隔离原则 
使用多个专门的接口比使用单一的总接口要好。广义的接口:一个接口相当于剧本中的一种角色,而此角色在一个舞台上由哪一个演员来演则相当于接口的实现。因此一个接口应当简单的代表一个角色,而不是一个角色。,如果系统设计多哥角色的话,则应当每一个角色都由一个特定的接口代表。狭义的接口(Interface):接口隔离原则讲的就是同一个角色提供宽、窄不同的接口,以对付不同的客户端。

===============================================================================

白話- OO設計原則 (SOLID原則) – 附生活實例

出處:http://rockssdlog.blogspot.tw/2012/03/oo-solid.html

世界最遙遠的開發目標,不是老闆的天馬行空,不是技術的困難程度,而是你以為就要完成了,需求卻仍然改個不停。   - - -血汗工程師的心聲

以下的5個原則,也稱為SOLID原則,是用來檢視當系統需求改變時,你是否能快速、安心的面對。是否應該引用Design Pattern,也可用目前是否符合這些原則來做檢視。

這些原則間並非完全互斥,即當你發現違反了某個原則時,通常也會違反其他原則。而其中最重要的便是OCP原則。


1. SRP:Single Responsibility Principle(單一職責)

定義:一個class應該只有一個需要改變的原因。
There should never be more than one reason for a class to change.

白話:一個class只作一件事。

問題:
1)到底切多細,很難一開始就知道。
2)切太細造成複雜度過高。



2. OCP: Open Closed Principle(開放封閉)

定義:軟體設計,應該對擴充開放,對修改封閉。
Software entities like classes, modules and functions should be open for extension but closed for modifications.

白話:軟體要很容易擴充功能,且擴充時原有的code都不需修改。

重要性:OCP是OOD諸多主張的核心。

生活實例: 
某情聖:『我永遠在電話中叫女朋友"寶貝",這樣怎麼換人或劈腿都不會穿幫』






3. LSP : Liskov Substitution Principle(Liskov替換)

定義:子類必須能夠替換其父類別。
Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.

白話:設計父類別時,只把所有的子類都有的東西放進來。

生活實例:
女兒問你『把拔,什麼是鳥類?』
你很得意的說『很簡單啊,就是會的動物嘛!』 (把『會飛』加到『鳥類』這個概念中)
接著打開鳥類大百科開始解說:
『就像這個老鷹就是會飛的動物』
『這個烏鴉也是會飛的動物 』
『還有………這個… 企鵝 ……?』@ @|||


PS.『不會飛的鳥』請見Wiki…

與OCP關係:LSP也是為了確保子類別擴充時不會違反OCP 。



4. ISP:Interface Segregation Principle(介面隔離)

定義:客戶(Client)只要依賴它們所需的介面
Clients should not be forced to depend upon interfaces that they don’t use.

白話:設計介面也盡量簡單,別把不相關的東西放進來。

生活實例:
業務員:『太太!你看我們最新的遙控器,可以遙控電視、冷氣、撥放器,連洗衣機也可以遙控,讓你一機在手家事一手包辦…』
太太:『那我衣服洗到一半,被轉電視的小朋友按到停止怎麼辦?可以把洗衣機的部分拿掉嗎?』
業務員:『…』


解決方式:
1)委任
2)多重繼承




5. DIP : Dependency Inversion Principle(依賴倒轉)

定義:
1)高階模組不應依賴低階模組,兩者應依賴抽象概念。
High-level modules should not depend on low-level modules. Both should depend on abstractions. 
2)抽象概念不應依賴細節,細節應依賴抽象概念。
Abstractions should not depend on details. Details should depend on abstractions.

白話:不要具體的指明物件的關係,而要抽象觀念替代之。

相關觀念:IoC(Inversion of Control, 控制反轉)。另DI(Dependency Injection, 依賴注入)則為其實作的方法。


生活實例:
你下班快到家時,接到老婆的電話,才驚覺今天是結婚紀念日。
千萬別在電話中說,『當然,我帶了妳最愛的玫瑰花回家哦!』(具體)
因為很可能稍後發現……附近的花店都關門了…
只要說『當然,親愛的,我帶了禮物回家』(抽象)
這樣一來就能看到路邊賣什麼就買什麼。

未經允許不得轉載:GoMCU » The S.O.L.I.D. Object Oriented Programming(OOP) Principles SOLID