(译)跟媳妇解释面向对象设计

This article is from http://www.codeproject.com/Articles/93369/How-I-explained-OOD-to-my-wife

虽然本文题目里写的是跟媳妇解释面向对象设计,但是我广大屌丝很少有媳妇是做软件开发的。如果是没有听说过面向对象程序设计概念的媳妇,仍旧不能看懂本文。这本质上是对刚对面向对象程序设计有一点概念,写过简单的小程序的人写的文章。

(欢迎转载,请注明出处:http://bitzhuwei.cnblogs.com

注:我知道之前有人翻译过这篇文章了,但是我还是想自己翻译一下,一来加深理解,二来我觉得之前的译文多少有些生硬,还是有不符合中文的表达习惯。无论写的好坏,欢迎批评指正!

How I explained OOD to my wife

By Al-Farooque Shubho, 2 Aug 2010

clip_image001

clip_image002

   4.90 (254 votes)

clip_image004
1

clip_image006
2

clip_image006[1]
3

clip_image007
4

clip_image008
5

4.90/5 - 254 votes

4 removed

μ 4.86, σa 0.96 [?]

 

clip_image009Prize winner in Competition "Best overall article of July 2010"

Introduction

引子

My wife Farhana wants to resume her career as a software developer (she started her career as a software developer, but couldn't proceed much because of our first child's birth), and these days, I am trying to help her learn Object Oriented Designs as I've got some experience in software design and development.

我媳妇方夏娜同学以前是软件工程师,后来生孩子就不干了。现在她想重新开始。这几天我就在辅导她学习OOD(Object Oriented Design,面向对象的设计),毕竟我是专业的嘛~

Since my early days in software development, I have always observed that no matter how hard a technical issue seems, it always becomes easier if explained from a real life perspective and discussed in a conversational manner. As we had some fruitful conversations on Object Oriented Designs, I thought I could share it because someone might find it an interesting way of learning OOD.

资深的我认为,就算某技术问题难以理解,只要从日常生活的角度去解释,用对话的方式去交流,那就难不到哪儿去啦。现在我把我跟媳妇关于OOD的对话分享出来,我猜会有人感兴趣的。

Following is how our OOD conversation took place:

对话如下。

Topic: Introducing OOD

话题:OOD第一印象

Shubho: Darling, let's start learning Object Oriented Designs. You know Object Oriented Principles, right?

本屌:亲爱的,咱们开始学OOD喽,你知道OOP(Object Oriented Principles,面向对象的原则)吧?

Farhana: You mean, Encapsulation, Inheritance, and Polymorphism, right? Yes, I know the principles.

媳妇:你说的是“封装”、“继承”和“多态”吧?我当然知道。

Shubho: OK, I hope you already know how to use classes and objects. Let us learn Object Oriented Designs today.

本屌:OK。那你肯定知道怎么写一个类,然后创建对象什么的了。今天来学OOD就。

Farhana: Hold on a second. Isn't Object Oriented Principles enough to do Object Oriented programming? I mean, I can define classes and encapsulate properties and methods. I can also define a class hierarchy based upon their relationship. So, what's left?

媳妇:等等等等。我会用OOP还不够做面向对象的编程吗?你看,我能定义一个类,封装属性和方法;还知道根据需要去创建继承它的类。你说我还有要学的吗?

Shubho: Good question. Object Oriented Principles and Object Oriented Design are actually two different things. Let me give you a real life example to help you understand.

本屌:问的好。要知道,OOP和OOD完全是两码事。我举个例子你就明白了。你小的时候学过字母表对吧。

When you were child you learned alphabets first, right?

Farhana: Yes.

媳妇:是啊。

Shubho: OK. You also learned words and how to assemble the alphabets to make those meaningful words. Along with that, you learned some basic grammar to assemble the words into sentences. For example, you had to maintain tenses, and you had to use prepositions, conjunctions, and others properly to create grammatically correct sentences. Say, a sentence like the following:

本屌:OK。你也学会了很多单词是由哪些字母排列组成的,你还学会了怎么把单词组成符合语法的句子。比如这个:

"I" (pronoun) "want" (Verb) "to" (Preposition) "learn" (Verb) "OOD" (Noun)

You see, you are assembling the words in some order, and you are also choosing the correct words to end up with a sentence that has some meaning.

你看,你按某种顺序把单词排列起来,

Farhana: OK, so, what does this mean?

媳妇:嗯哪……你想说什么?

Shubho: This is analogous to Object Oriented Principles. OOP says about the basic principles and the core ideas to do Object Oriented programming. Here, OOP can be compared to the basic English grammar, where the basic grammar teaches you how to construct a meaningful and correct sentence using words, and OOP teaches you to construct classes, encapsulate properties and methods inside them, and also develop a hierarchy between them and use them in your code.

本屌:这和OOP是多么类似呀。OOP是面向对象编程的基本原则和核心思想。OOP可以比作基础的英语语法:语法告诉你如何用单词写出含义正确的句子,OOP告诉你如何创建一个类,给它设置属性和方法,甚至创建继承类。

Farhana: Hm..I got the point. So, where does OOD fit here?

媳妇:啊,原来如此。那OOD跟这个有什么关系?

Shubho: You'll get your answer soon. Now, suppose you need to write articles and essays on some topics. You may also wish to write books on different subjects that you have expertise on. Knowing how to construct sentences is not enough to write good essays/articles or books, right? You need to write a lot, and need to learn to explain things in a good way so that readers can easily understand what you are trying to say.

本屌:马上。比方说你要写一篇文章,或者写本书叫月子。那你不光要知道怎么写一句话一段话,你要写多少多少页的东西,还要有组织篇章结构的技巧,写的浅显易懂别人才容易理解,才想看你写的,对吧。

Farhana: Seems interesting... carry on.

媳妇:啊有道理,继续。

Shubho: Now, if you want to write a book on a particular subject (say, Learning Object Oriented Design), you got to know how to divide the subject into smaller topics. You also need to write chapters on those topics, and you need to write prefaces, introductions, explanations, examples, and many other paragraphs in the chapters. You need to design the overall book and learn some best practices of writing techniques so that the reader can easily understand what you are trying to explain. This is about the bigger picture.

本屌:具体来说,如果你想写一本书(比如关于学习OOD的书),你要把问题划分为若干子问题,组织各个章节的内容及其先后顺序,还要写前言、序言、搜罗安排示例、弄个封面什么的。你看一下那些写的好的书是怎么组织的,然后借鉴借鉴,最后才能写出一本受读者欢迎的好书。大体蓝图就是这样。

In software development, OOD addresses the bigger picture. You need to design your software in a way that your classes and code are modularized, re-usable, and flexible, and there are some good guidelines to do that so that you don't have to re-invent the wheel. There are some design principles that you can apply to design your classes and objects. Makes sense?

换到软件开发领域,OOD就是讲怎么设计蓝图的。好的软件蓝图,其类或者说代码,要模块化,具有可重用性,扩展性。因此软件代码的设计也有一些原则(design principles),用来指导你设计优秀的蓝图。而且,现在已经有若干前人设计的优秀蓝图被分享出来供大家借鉴,我们无须从零开始。

Farhana: Hmm.. got the initial idea, but need to learn more.

媳妇:大概理解你的意思啦,再说明白点。

Shubho: Don't worry, you will learn soon. Just let our discussion going.

本屌:OK。想更明白,请看下一话题。

Topic: Why OOD?

话题:OOD算个毛?

Shubho: This is a very important question. Why should we care about Object Oriented Design when we can create some classes quickly and finish development and deliver? Isn't that enough?

本屌:这个问题太重要了,重要到我们忘记了为什么它这么重要。你说咱轻轻爽爽的写几个类,把软件做出来,把钱拿到手,不就行了么。OOD算个毛?

Farhana: Yes, I didn't know about OOD earlier, and still I was able to develop and deliver projects. So, what is the big deal?

媳妇:是啊,我以前也不懂OOD,可还是好好的做出过软件来呀。OOD有什么了不起?

Shubho: OK, let me get a classic quote for you:

"Walking on water and developing software from a specification are easy if both are frozen."

- Edward V.Berard

本屌:OK。哥引用一下名人名言:

"Walking on water and developing software from a specification are easy if both are frozen."

- Edward V.Berard

“如果水结冰,需求说明书不改,水上漂和软件开发同样的易如反掌。”——爱德华V.贝拉尔

(译者注:翻译过来就没意思了,frozen在英语里一语双关,既有水结冰的意思,又有需求固定不变的意思)

Farhana: You mean software development specifications keep changing constantly?

媳妇:你的意思是软件开发需求说明书无休止的改变?

Shubho: Exactly! The universal truth of software is "your software is bound to change". Why?

本屌:没错!改变无止境就是软件的普遍真理。

Because, your software solves real life business problems and real life business processes evolve and change – always

因为,你开发的软件解决了生活生产中的问题,而真实世界中,问题是在变化的,那真是永远,永远,永远,永永远远地变。.

Your software does what it is supposed to do today and does it good enough. But, is your software smart enough to support "changes"? If not, you don't have a smartly designed software.

你开发的软件能满足今天的需求,满足的很好。但是它能满足“改变”吗?如果不行,那就不是一个设计的好的软件。

Farhana: OK, so, please explain "smartly designed software" sir!

媳妇:OK。那么怎么才算设计得好的软件,亲?

Shubho: "A smartly designed software can adjust changes easily; it can be extended, and it is re-usable."

本屌:设计良好的软件能够轻松地适应改变,可扩展,可重用。

And, applying a good "Object Oriented Design" is the key to achieve such a smart design. Now, when can you claim that you have applied good OOD in your code?

在代码设计中应用OOD是实现良好设计的关键。那么,怎么才算是在代码设计中应用了OOD呢?

Farhana: That's my question too.

媳妇:我也想知道。

Shubho: You are doing Object Oriented Design if your code is:

· Of course, object oriented.

· Re-usable.

· Can be changed with minimal effort.

· Can be extended without changing existing code.

施展OOD,就意味着如下内容:

当然,面向对象编程;

代码可重用;

为了适应需求的改变,所需改动的代码最少(改动代码所需时间最短,等等都是这个意思);

无需改变现有代码即可扩展新功能。

Farhana: And..?

媳妇:这……

Shubho: We are not alone. Many people have already given lots of thoughts and put a lot of effort on this issue, and they've tried to achieve good Object Oriented Design and identify some basic principles for doing Object Oriented Design (some basic inspirations which you can use to lay out your object oriented design). They also have identified some common design patterns that are applicable to common scenarios (based on the basic principles).

本屌:你不是一个人。已经有很多人为OOD付出心血,他们尝试着应用OOD,并且总结出来一些OOD的通用原则。基于这些原则,他们给出了若干通用的设计模式(design pattern),每一张设计模式都能够应用到一种类型的场景。

Farhana: Can you name some?

媳妇:能举几个OOD设计原则的例子不,亲?

Shubho: Sure. There are many design principles out there, but at the basic level, there are five principles which are abbreviated as the SOLID principles (thanks to Uncle Bob, the great OOD mentor).

本屌:当然。设计原则有很多啦,其中最基础的5个被缩写为SOLID原则(感谢Uncle Bob,超彪悍的OOD导师)。

S = Single Responsibility Principle(单一职责原则)

O = Opened Closed Principle(开闭原则)

L = Liscov Substitution Principle(里氏替换原则)

I = Interface Segregation Principle(接口分离原则)

D = Dependency Inversion Principle(依赖倒置原则)

In the following discussion, we will try to understand each of these in detail.

下面咱们就讨论一下这个SOLID原则吧。

Topic: Single Responsibility Principle

话题:单一职责原则

Shubho: Let me show you the poster first. We should thank the person who made the posters, these are really interesting.

本屌:来看一下这张海报。做海报的人可真有才。

clip_image010

(图中文字:单一职责原则;你能干,不等于你应该干)

Single Responsibility Principle poster

单一职责原则的海报

It says, "just because you can implement all the features in a single device, you shouldn't". Why? Because it adds a lot of manageability problems for you in the long run.

海报告诉你:“就算你能在一件东西上做出所有的功能,你也不应该这么做”。Why?因为长期来看,这会引起巨头疼的维护问题。

Let me tell you the principle in Object Oriented terms.

用面向对象术语说就是这样的:

"There should never be more than one reason for a class to change."

“只能有一种理由让你修改这个类”。

Or, differently said: "A class should have one and only one responsibility".

另一种说法是:“一个类应该有且只应该有一个原则”。

Farhana: Can you please explain?

媳妇:嘛意思啊?求详情求解释?

Shubho: Sure, this principle says that if you have a class that has more than one reason to change (or has more than one overall responsibility), you need to split the class into multiple classes based upon their responsibility.

本屌:好的。这个原则说的是,如果一个类有超过一个理由让它修改代码(或者有超过一个功能),你就要根据它的功能把它拆分开。

Farhana: Hmm... does that mean that I can't have multiple methods in a single class?

媳妇:额,这是不是说我一个类里面不能有多个方法了?

Shubho: Not at all. Rather, you surely can have multiple methods in a single class. The issue is, they have to meet a single purpose. Now, why is splitting important?

本屌:没那回事。一个类里有多个方法是理所当然的。问题是,这些方法是为实现一个共同的功能才写的。好了,现在想想为什么拆分功能这么重要?

It's important because:

那是因为:

· Each responsibility is an axis of change.

· Code becomes coupled if classes have more than one responsibility.

每个功能都会引起改变

一个类有多个功能就会引起紧耦合(改变这个功能的东西,会影响到另一个功能)

Farhana: Could you please give me an example?

媳妇:能不能给个例子,亲?

Shubho: Sure, take a look at the following class hierarchy. Actually, this example is taken from Uncle Bob, so thanks to him again.

本屌:当然。看一下下面这个类继承图。实际上这也是从Uncle Bob哪里拿来的,再次感谢她。

clip_image011

Class hierarchy showing violation of SRP principle

违反SRP原则的类继承图

Here, the Rectangle class does the following:

这个Rectangle类做这些事:

· It calculates the value of the rectangular area.

· It renders the rectangle in the UI.

计算矩形面积

在UI上画矩形

And, two applications are using this Rectangle class:

然后来了两个应用程序调用这个Rectangle类:

· A computational geometry application uses this class to calculate the area

· A graphical application uses this class to draw a rectangle in the UI

一个几何计算程序用这个类来计算面积

一个画图程序用这个类来在UI上画矩形

This is violating the SRP (Single Responsibility Principle)!

这违反了SRP原则!

Farhana: How?

媳妇:怎么违反了?

Shubho: You see, the Rectangle class is actually performing two different things. It is calculating the area in one method, and it is returning a GUI representation of the rectangle in another method. This leads to some interesting problems:

本屌:你看,Rectangle类实际上做了两件事,它要计算面积,还要在GUI上画图。问题就出在这里。

· We must include the GUI in the computational geometry application. While deploying the geometry application, we must include the GUI library.

· A change to the Rectangle class for the graphical application may lead to a change, build, and test for the computational geometry application, and vice-versa.

在几何计算应用程序里不得不include一下GUI库。

一旦图形应用程序要求Rectangle类做修改,那么几何计算应用程序的部署和测试工作就得再来一次;反之亦然。

Farhana: It's getting interesting. So I guess we should break up the class based on its responsibilities, right?

媳妇:哦,我猜我们应该把Rectangle类拆分一下。

Shubho: Exactly. Can you predict what we should do?

本屌:太对了。你估摸估摸到底该怎么拆分呢?

Farhana: Sure, let me try. Following is what we might need to do:

媳妇:恩,我觉得是这么做:

Separate the responsibilities into two different classes, such as:

把Rectangle的两个功能(职责)分到两个新的类里去:

· Rectangle: This class should define the area() method.

· RectangleUI: This class should inherit the Rectangle class and define the Draw() method.

新Rectangle:定义计算面积的area()方法。

RectangleUI类:继承新Rectangle类,定义Draw()方法。

Shubho: Perfect. In this case, the Rectangle class will be used by the computational geometry application, and the RectangleUI class will be used by the graphical application. We could even separate the classes into two separate DLLs, and that will allow us not to touch the other in case a change is needed to be implemented in one.

本屌:哎呦,不错呦。新Rectangle类用在几何计算应用程序里,RectangleUI类用在绘图程序里。我们还可以把这两个类分到两个DLL文件里。这么一来,改变其中一个类的代码,对另一个类和它所在的DLL文件都毫无影响(不必重新测试、部署另一个)。

Farhana: Thanks, I think I understand SRP. One thing, SRP seems to be an idea of breaking things into molecular parts so that it becomes reusable and can be managed centrally. So, shouldn't we apply SRP in the method level as well? I mean, we might have written methods that have many lines of code for doing multiple things. These methods might be violating SRP, right?

媳妇:嘿嘿。SRP就是这么回事啊。等等,SRP我感觉是这么个思想:把要做的事拆分成若干小的步骤部分,这样就可重用、便于管理维护。那我们是不是也应该把SRP这个核心思想应用到方法层次上?你想,有的方法可能有很多行代码,它做了很多事才完成它的任务。这种方法就违反了SRP啊,对不对?

Shubho: You got it right. You should break down your methods so that each method does a particular work. That will allow you to re-use methods, and in case a change is required, you are able to do the change by modifying minimal amount of code.

本屌:对头。这种大块头的方法就应该拆分一下,达到每个方法都只做一件事。这样一来,方法也就可重用了。而且你要改代码的时候,改动就会局限在较小的方法里,这对维护和测试都是很有益的。

Topic: Open-Closed Principle

话题:开闭原则

Shubho: Here goes the poster for the Open-Closed Principle:

本屌:先上图:开闭原则!额,是不是有点重口味了这个。

clip_image012

(图中文字:开闭原则;穿穿衣服,何必开膛破肚)

Figure: Open Closed Principle poster

海报:开闭原则

If I need to explain it in design oriented terms, it would be as follows:

用设计术语来说就是这样的:

"Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification."

“软件实体(类,模块,函数,等等)要面向扩展,回避修改。”

At the most basic level, that means, you should be able to extend a class's behavior without modifying it. It's just like I should be able to put on a dress without doing any change to my body, ha ha.

简单来说,你写了一个类,要想扩展这个类的功能,你最好在不修改这个类的代码的基础上完成。好比我本来是我,冬天了想加件衣服,这不需要把我开膛破肚吧,哈哈。

Farhana: Interesting. You can change your look by putting any dress you want, and you don't have to change your body for that. So you are open for extension, right?

媳妇:额,你想穿什么就穿什么,不用自残。所以你对扩展衣服是开放的,对吧?

Shubho: Yes. In OOD, open for extensions means that the behavior of the module/class can be extended and we can make the module behave in new and different ways as the requirements change, or to meet the needs of new applications.

本屌:对。OOD说的对扩展开放,指的是类的行为可以被扩展,我们可以根据用户需求的改变而改变一个模块的行为,或者说满足新应用程序的要求。

Farhana: And your body is closed for modification. I like this example. So, the source code for a core class or module should not be modified when it needs an extension. Can you explain with some examples?

媳妇:还有你的身体对修改是关闭的。这个例子很形象。这意思是说扩展一项功能的时候,模块原有的核心类的源代码不应该改变。能不能再举个例子亲?

Shubho: Sure, take a look at the following example. This doesn't support the "Open-Closed" principle:

本屌:好。看下面这个示例。它是不支持开闭原则的。

clip_image013

Class hierarchy showing violation of Open Closed principle

类图:违反了开闭原则的例子

You see, the Client and Server classes are both concrete. So, if for any reason, the server implementation is changed, the client also needs a change.

你看,Client类和Server类都是具体类。所以,如果Server类的定义改变了,势必要相应改变Client类。

Farhana: Makes sense. If a browser is implemented as tightly coupled to a specific server (such as IIS), then if the server is replaced for some reason with another one (say, Apache) the browser also needs to be changed or replaced. That would be really horrible!

媳妇:言之有理嘛。如果浏览器是跟一个具体的服务器(例如IIS)紧耦合的,那要是把IIS换成Apache,浏览器跟服务器交互的部分就肯定得改了。这牵一发而动全身的改动太恶心了。

Shubho: Correct. So following would be the correct design:

本屌:正解。所以下面这种设计才是更好的。

clip_image014

Class hierarchy showing Open Closed Principle

类图:符合开闭原则

In this example, there is an Abstract Server class added, and the client holds a reference to the abstract class, and the Concrete Server class implements the Abstract Server class. So, if for any reason the Server implementation is changed, the Client is not likely to require any change.

这个类图里加了一个抽象服务器类,Client类里有一个对抽象服务器类的引用字段,然后用具体的服务器类继承和实现抽象服务器类。如此一来,当客户告诉你把IIS换成Apache,你只要再写个Apache服务器的类就行了,客户端就不受影响。

The Abstract Server class here is closed for modification, and the concrete class implementations here are open for extension.

你感觉感觉,是不是抽象服务器类对修改关闭(有事没事都别改它的代码),具体类的实现却是对扩展开放的。

Farhana: As I understand, abstraction is the key, right?

媳妇:我感觉,关键的手段就是抽象,是吗?

Shubho: Yes, basically, you abstract the things that are the core concepts of your system, and if you do the abstraction well, most likely, it would require no change when the functionality is to be extended (such as the server is an abstract concept). And, you define the abstract things in the implementations (say, IISServer implements the Server) and code against abstractions (Server) as much as possible. This would allow you to extend the abstract thing and define a new implementation (say, ApacheServer) without doing any change in the client code.

本屌:是的。你把系统的核心概念抽象成类,如果抽的好,以后随便扩展具体什么功能都不会影响这些核心的东西。尽可能的把事情抽象化,然后在具体类(IISServer什么的)里去实现那些抽象定义。那么以后你想扩展抽象类(比如写个ApacheServer什么的)就不会牵涉到客户端的代码了。

Topic: Liskov's Substitution Principle

话题:里氏替换原则

Shubho: The name "Liskov's Substitution Principle" sounds very heavy, but the idea is pretty basic. Take a look at this interesting poster:

本屌:里氏替换原则听起来很吓人,其实一点也不难。继续先上图:

clip_image015

(图中文字:里氏替换原则;浮水为鸭,呱呱为鸭,充电非鸭,笨呀你丫)

Liskov Substitution Principle poster

里氏替换原则的海报

The principle says:

这个原则告诉我们:

"Subtypes must be substitutable for their base types."

“基类型出现的地方必须能够用子类型代替。”

Or, if said differently:

换个方式说是:

"Functions that use references to base classes must be able to use objects of derived classes without knowing it."

“基类的函数在子类里面也必须正常使用。”

Farhana: Sorry, sounds confusing to me. I thought this is the basic rule of OOP. This is polymorphism, right? Why was an Object Oriented Principle required on this issue?

媳妇:我迷茫了。我以为你在讲OOP,可这是多态吧?为什么OOP会讲这个?

(注:译者认为是继承,不是多态)

Shubho: Good question. Here is your answer:

本屌:问的好,是这样的。

In basic Object Oriented Principles, "Inheritance" is usually described as an "is a" relationship. If a "Developer" is a "SoftwareProfessional", then the "Developer" class should inherit the "SoftwareProfessional" class. Such "Is a" relationships are very important in class designs, but it's easy to get carried away and end up in a wrong design with a bad inheritance.

在基础的OOP里面,继承被描述成是“是一个”的关系。如果“开发者”是一个“软件专家”,那么“开发者”类就应该继承“软件专家”类。类设计中的这种“是一个”的关系是非常重要的,但是你很容易被迷惑而做出错误的类继承设计。

The "Liskov's Substitution Principle" is just a way of ensuring that inheritance is used correctly.

里氏替换原则就是一种保证正确使用继承的方法。

Farhana: I see. Interesting.

媳妇:明白了,这个好像很有用。

Shubho: Yes darling, indeed. Let's take a look at an example:

本屌:当然了亲,咱们再看个例子来。

clip_image016

Class hierarchy showing an example of Liskov Substitution Principle

符合里氏替换原则的类继承图

Here, the KingFisher class extends the Bird base class and hence inherits the Fly() method, which is pretty good.

此示例中KingFisher(翠鸟)类扩展了Bird类,因此它继承了Fly()方法。这没问题。

Now take a look at the following example:

现在我加上个东西你再看。

clip_image017

Corrected class hierarchy of Liskov Substitution Principle

修改之后的类继承图违反了里氏替换原则

Ostrich is a Bird (definitely it is!) and hence it inherits the Bird class. Now, can it fly? No! Here, the design violates the LSP.

Ostrich(鸵鸟)是一个鸟类(这肯定的),然后我们就让Ostrich类继承了Bird类。问题来了,鸵鸟能飞吗?不能啊!这就违反了里氏替换原则。

So, even if in real world this seems natural, in the class design, Ostrich should not inherit the Bird class, and there should be a separate class for birds that can't really fly and Ostrich should inherit that.

所以说,就算在真实世界里你可以说鸵鸟是一个鸟类,但是在类设计里,Ostrich就不应该继承Bird类。应该是有一个单独的类代表不能飞的鸟,让Ostrich继承它。

Farhana: OK, understood. So, let me try to point out why the LSP is so important:

媳妇:原来如此。让我试试总结一下LSP的重要性在哪儿。

· If LSP is not maintained, class hierarchies would be a mess, and if a subclass instance was passed as parameter to methods, strange behavior might occur.

· If LSP is not maintained, unit tests for the base classes would never succeed for the subclass.

· 若不满足LSP,类继承将是一团乱麻。若子类的实例作为参数传递给一个方法,会发生各种奇葩的事。

· 若不满足LSP,对基类的单元测试对子类就通不过了。

Am I right?

我说的对不?

Shubho: You are absolutely right. You can design objects and apply LSP as a verification tool to test the hierarchy whether inheritance is properly done.

本屌:你说的太对了。类图设计完的时候你就可以用LSP原则作为验证工具来检验你写的继承关系是不是合适。

Topic: The Interface Segregation Principle

话题:接口分离原则

Shubho: Today, we will learn the "Interface Segregation Principle". Here is the poster:

本屌:今天我们学习“接口分离原则”。再来看个海报来:

clip_image018

(图中文字:接口分离原则;这个USB口,打死我也不知道该插在哪儿啊?!)

Interface Segregation Principle poster

海报:接口分离原则

Farhana: What does it mean?

媳妇:这是什么意思?

Shubho: It means the following:

"Clients should not be forced to depend upon interfaces that they do not use."

本屌:意思是:“客户端代码不应该费劲去弄懂他用不到的接口”。

Farhana: Explain please.

媳妇:具体解释一下亲?

Shubho: Sure, here is your explanation:

本屌:恩。这么说吧:

Suppose you want to purchase a television and you have two to choose from. One has many switches and buttons, and most of those seem confusing and doesn't seem necessary to you. Another has a few switches and buttons, which seems familiar and logical to you. Given that both televisions offer roughly the same functionality, which one would you choose?

好比你想买一台电视机,有两种可选的。一种是有很多开关和按钮,大多数开关都不知道是干嘛的,也不知道有用没用。另一种只有几个开关和按钮,看起来又熟悉又容易理解。如果这两台电视功能一样,你选哪个?

Farhana: Obviously the second one with the fewer switches and buttons.

媳妇:当然是开关少的那个。

Shubho: Yes, but why?

本屌:是的,但是为什么呢?

Farhana: Because I don't need the switches and buttons that seem confusing and unnecessary to me.

媳妇:因为我又不需要那些乱七八糟的没用的开关。

Shubho: Correct. Similarly, suppose you have some classes and you expose the functionality of the classes using interfaces so that the outside world can know the available functionality of the classes and the client code can be done against interfaces. Now, if the interfaces are too big and have too many exposed methods, it would seem confusing to the outside world. Also, interfaces with too many methods are less re-usable, and such "fat interfaces" with additional useless methods lead to increased coupling between classes.

本屌:对。类似的,假设你写了一些类,用接口暴露他们的功能。这样客户端代码就可以通过接口调用你写的类。可是,如果接口里的方法太多,那别人调用你的接口的时候就不容易弄明白该怎么用。而且这样的接口也缺少可重用性。这种所谓的“富接口”也增加了类之间的耦合性。

This also leads to another problem. If a class wants to implement the interface, it has to implement all of the methods, some of which may not be needed by that class at all. So, doing this also introduces unnecessary complexity, and reduces maintainability or robustness in the system.

还没完。如果别人需要写一个类实现这个接口,他就得实现接口里所有的方法,其中肯定有很多方法他根本用不着。这就平白增加了代码,降低了系统的可维护性。

The Interface Segregation principle ensures that Interfaces are developed so that each of them has their own responsibility and thus they are specific, easily understandable, and re-usable.

接口分离原则告诉你要让接口简单,只做它必须做的事,这样才易懂易重用。

Farhana: I see. You mean interfaces should contain only the necessary methods and not anything else?

媳妇:明白啦。你是说接口应该只包含尽可能少的必须的方法,其他的都不能有?

Shubho: Exactly. Let's see an example.

本屌:正解。来看个例子。

The following interface is a "Fat interface" which violates the Interface Segregation principle:

下面这个接口就是“富接口”,违反了接口分离原则。

clip_image019

Example of an interface violating the Interface Segregation principle

违反接口分离原则的示例

Note that the IBird interface has many bird behaviours defined along with the Fly() behaviour. Now, if a Bird class (say, Ostrich) implements this interface, it has to implement the Fly() behaviour unnecessarily (Ostrich doesn't fly).

你看IBird接口实现了若干鸟类的行为,包括Fly()这个行为。那如果要给鸵鸟类实现这个接口,就说不通了。因为鸵鸟不会飞啊。

Farhana: That's correct. So, this interface must be split?

媳妇:恩是。那应该把这个接口拆分一下?

Shubho: Yes. The "Fat Interface" should be broken down into two different interfaces, IBird and IFlyingBird, where the IFlyingBird inherits IBird.

本屌:对。这个“富接口”应该拆成两个接口IBird和IFlyBird,让IFlyBird继承IBird。

clip_image020

Correct version of the interface in the Interface Segregation principle example

接口分离原则示例的正解

If there is a bird that can't fly (say, Ostrich), it would implement the IBird interface. And if there is a bird that can fly (say, KingFisher), it would implement the IFlyingBird interface.

如果有不会飞的鸟类,让它实现IBird接口,如果是会飞的鸟类,让它实现IFlyBird接口。

Farhana: So, if I go back to the example with the television with many switches and buttons, the manufacturer of that television must have a blueprint of that television where the switches and buttons are included in the plan. Whenever they want to create a new model of the television, if they need to re-use this blueprint, they would need to create as many switches and buttons as included in the plan. That wouldn't allow them to re-use the plan, right?

媳妇:那我觉得那个N多按钮的电视的例子,厂家有一张设计蓝图,要是厂家想造一种新款式的电视,还想重用之前的蓝图的话,就必须还设计那么多按钮。所以这个蓝图就不能重用了。

Shubho: Right.

本屌:对对。

Farhana: And, if they really want to re-use their plans, they should divide their television's blueprint into smaller pieces so that they can re-use the plan whenever any new kind of television is to be created.

媳妇:厂家要是想重用蓝图,就应该把它分解为小的部分。这样生产新款式的时候原来的蓝图就可以拿来用了。

Shubho: You got the idea.

本屌:你已窥门径。

Topic: The Dependency Inversion Principle

话题:依赖倒置原则

Shubho: This is the last principle among the SOLID principles. Here is the poster:

本屌:这是SOLID原则的最后一个了。看下图先。

clip_image021

(图中文字:依赖倒置原则;你会把灯泡直接焊到火线上吗?)

Dependency Inversion principle poster

依赖倒置原则的海报

It says..

"High level modules should not depend upon low level modules. Rather, both should depend upon abstractions."

意思是:“高层模块不应该依赖于低层模块。两者都应该依赖于抽象。”

Shubho: Let's consider a real world example to understand it. Your car is composed of lots of objects like the engine, the wheels, the air conditioner, and other things, right?

本屌:我们用现实生活中的例子来说明。你的汽车是由很多东西组成的,包括引擎,轮子,空调,其他的等等。是吧?

Farhana: Yes, of course.

媳妇:当然了。

Shubho: OK, none of these things are rigidly built within a single unit; rather, each of these is "pluggable" so that when the engine or the wheel has problem, you can repair it (without repairing the other things) and you can even replace it.

本屌:这些都没有做成连在一起分不开的整体。每一个都是“可插拔”的。如果引擎或者轮子坏了,你可以只修理引擎、轮子或者换掉就行了。

While replacement, you just have to ensure that the engine/wheel conforms to the car's design (say, the car would accept any 1500 CC engine and will run on any 18 inch wheel)

换的时候,你只要确保引擎/轮子符合车子的设计(比如,车子使用1500CC的引擎和18英寸的轮子)。.

Also, the car might allow you to put a 2000 CC engine in place of the 1500 CC, given the fact that the manufacturer (say, Toyota) is the same.

车子可能还允许你装一个2000CC的引擎,如果是统一厂家的产品的话(丰田什么的)。

Now, what if the different parts of your car were not built in such a "pluggable nature"?

现在你想象一下,如果车子各个部分不是“可插拔”的,会怎么样?

Farhana: That would be horrible! Because, in that case, if your car's engine is out of order, you will have to fix the whole car or purchase a new one!

媳妇:那太恐怖了!因为如果引擎坏掉了,我得把整个车子修理一遍或者买个新的。

Shubho: Yes. Now, how can the "pluggable nature" be achieved?

本屌:对,那么怎么实现“可插拔”呢?

Farhana: "Abstraction" is the key, right?

媳妇:关键在于“抽象”吧?

Shubho: Yes. In real world, Car is the higher level module/entity, and it depends on the lower level modules/entities like the Engines or Wheels.

本屌:对。真实世界里,汽车是高层模块,它依赖低层模块,比如引擎和轮子。

Rather than directly depending on the Engines or Wheels, the car depends on the abstraction of some specification of Engine or Wheels so that if any the Engine or Wheel conforms to the abstraction, these could be assembled with the car and the car would run.

车子不是直接依赖引擎和轮子的,而是他们的抽象说明。然后任何满足这个抽象的引擎和轮子都能够组装到车子上了。

Let's take a look at the following class diagram:

看一下下面的类图。

clip_image022

Dependency Inversion principle class hierarchy

依赖倒置原则的类图

Shubho: In the above Car class, notice that there are two properties, and both of these are of abstract type (Interface) rather than concrete type.

本屌:在上面的Car类里,有两个属性,都是抽象类型(接口)的。

The Engine and Wheels are pluggable because the car would accept any object implementing the declared interfaces, and that will not require any change in the Car class.

车子能改引用任何实现了对应接口的引擎和轮子,这就是“可插拔”的,而且换不同类型的引擎和轮子不需要修改Car类型的代码。

Farhana: So, if Dependency Inversion is not implemented in the code, we run the risk of:

媳妇:那么如果不按照依赖倒置原则,就可能发生下面的情况:

· Damaging the higher level code that uses the lower level classes.

· Requiring too much time and effort to change the higher level code when a change occurs in the lower level classes.

· Producing less-reusable code.

· 调用底层类的高层代码被推倒重来

· 底层代码修改时,不得不大量修改高层代码

Shubho: You got it perfect darling!

本屌:你说的太好了亲!

(译者注:本人认为依赖倒置原则这个名字不太好,应该叫做依赖抽象原则)

Summary

再回首

Shubho: There are many other Object Oriented principles other than the SOLID principles. Some are:

本屌:除了SOLID原则外,还有很多别的OOP原则。比如:

· "Composition over Inheritance": This says about favoring composition over inheritance.

· "Principle of least knowledge": This says that "the less your class knows, the better".

· "The Common Closure principle" : This says that "related classes should be packaged together".

· "The Stable Abstractions principle": This says that "the more stable a class is, the more it must consist of abstract classes."

· 组合优于继承:说的是尽量组合,这比继承好。

· 傻瓜原则:说的是你的类知道的东西越少越好。

· 闭包原则:说的是把相关的类打包到一起。

· 稳定抽象原则:说的是想少改动一个类的话,就尽量用抽象的东西定义它。

Farhana: Shouldn't I learn those principles too?

媳妇:我是不是也得学这些啊?

Shubho: Yes, of course. You have the whole World Wide Web to learn from. Just Google on those principles and try to understand. And of course, don't hesitate me to ask me if you need help.

本屌:恩学学总是好的。随便上Google百度一下就是了。当然,需要哥就说话,随叫随到。

Farhana: I've heard there are many Design Patterns built upon those design principles.

媳妇:我听说基于这些设计原则,人们搞出了好多传说中的“设计模式”?

Shubho: You are right. Design Patterns are nothing but some commonly suggested design suggestions in commonly recurring situations. These are mainly inspired by the Object Oriented Design principles. You can think of Design Patterns as "Frameworks" and OOD principles as "Specifications".

本屌:对的。设计模式就是在某些情景下大家推荐使用的设计方法。它们很好的符合OOD的原则。如果说OOD是原则,那么设计模式就是遵照这些原则而出现的宏观设计图。

Farhana: So, am I going to learn Design Patterns next?

媳妇:那我下一步该学设计模式啦?

Shubho: Yes darling.

本屌:没错亲。

Farhana: That would be exiting, right?

媳妇:好激动~

Shubho: Yes, that would be really exiting.

本屌:必须滴!

Next

后文

· How I explained Design Patterns to my wife: Part I

· (译者:未完待续——给媳妇解释设计模式)

License

版权

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

本文及其源代码等文档基于CPOL。

(欢迎转载,请注明出处:http://bitzhuwei.cnblogs.com

原文地址:https://www.cnblogs.com/bitzhuwei/p/How_I_explained_OOD_to_my_wife.html