




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1. 单一职责原则(SRP)单一职责原则(SRP),就一个类而言,应该仅有一个引起它变化的原因。也就是说,不要把变化原因各不相同的职责放在一起,因为不同的变化会影响到不相干的职责。再通俗一点地说就是,不该你管的事情你不要管,管好自己的事情就可以了,多管闲事害了自己也害了别人。在软件设计中,如果一个类承担的职责过多,就等于吧这些职责耦合在一起,而一个职责的变化可能会削弱和抑制这个类完成其他职责的能力。这耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。如果多于一个的动机去改变一个类,那么这个类就具有多余一个的职责,就应该
2、要考虑类的职责分离。2. 开放-封闭原则(The Open-Closed Principle 简称OCP)开放-封闭原则,或叫开-闭原则,是说软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改。不修改的意思就是是“你可以随便增加新的类,但是不要修改原来的类”。从这个角度去理解就好多了,其实这里还是一个隔离变化的问题。这个原则的两个特征:一个是对于扩展是开放的;另一个是对于更改是封闭的。我们在设计开发任何系统时,都不可能指望系统一开始就需求确定,就不再变化(要这样就太幸福了,哈哈),这是不现实的也是不科学的想法。既然需求是有一定变化的,那么如何在面对需求变化时,设计的程序可以相对容易的修
3、改,不至于说,新需求一来,就要把整个程序推倒重来(这样会让程序员疯了不可,哈哈,你不想疯吧)。怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出的新版本呢?开放-封闭原则就是我们的答案。在程序设计时,我们要时刻考虑尽量把类设计的足够好,写好了就不要去修改,如果有新的需求来了,我们增加一些类来完成新的需求,原来的代码能不动就不动。绝对的对修改关闭是不可能的,无论模块是多么的封闭,都会存在一些无法对之封闭的变化,既然不能完全封闭,设计人员必须对他设计的模块应该对那种变化封闭做出抉择、他必须事先猜测出最有可能发生变化的变化种类,然后构建抽象来隔离那些变化。开放-
4、封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所生成的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现出频繁变化的部分都做出抽象,然后,对于应用程序中的每部分都刻意定进行抽象同样不是一个好主意,拒绝不成熟的抽象和抽象本身一样重要。3. 依赖倒转原则(DIP Dependency Inversion Principle)依赖倒转原则:抽象不应该依赖于细节。细节应该依赖于抽象;高层不应该依赖于底层,两者都应该依赖于抽象。说白了就是要针对接口编程,不要针对实现编程。抽象的东西才是最稳定的,也就是说,我们依赖的是它的稳定。依赖倒转其实可以说是面向对象设计
5、的标志,用哪种语言来编写程序不重要,如果编写是考虑的都是如何针对抽象编程而不是针对细节编程,即程序中的所有的依赖关系都是终止与抽象类或者接口,那就是面向对象的设计,反之就是过程化设计了。4. 里氏代换原则里氏代换原则(LSP):子类型必须能够替换掉他的父类型。说白了就是一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而他察觉不出父类对象和子类对象的区别,也就是说,在软件里面,把父类都替换成他的子类,程序行为没有变化。有了里氏替换原则,才是继承复用成为可能,只有当子类可以替换掉父类时,软件的功能不受到影响,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。有了里氏代换原则,
6、才能使开放-封闭原则成为可能,正是由于子类型的可替换性才使得父类型的模块在无需修改的情况下扩展。5. 接口隔离原则(ISP)接口隔离原则(ISP):不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。6. 迪米特法则(LoD)迪米特法则(Law of Demeter或简写LoD)又叫最少知识原则(Least Knowledge Principle或简写为LKP):如果两个类不彼此之间直接通信,那么这两个类就不应当发生
7、直接的相互作用。如果其中一个类需要调用另一个类的某个方法的话,可以通过第三者转发这个调用。 迪米特法则首先强调的前提是在类的结构设计上,每一个类都应当尽量降低成员的访问权限。 迪米特法则其根本思想强调的是类之间的松耦合。类之间的耦合越弱,越利于复用,一个处于弱耦合的类被修改,不会对有关系的类造成波及。7. 合成/聚合复用原则(Composite/Aggregate Reuse Principle或CARP)合成/聚合复用原则(Composite/Aggregate Reuse Principle或CARP):经常又叫做合成复用原则(Composite Reuse Principle或CRP),
8、就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。我白了就是要尽量使用合成/聚合,尽量不要使用继承。本文来自CSDN博客,转载请标明出处:黑箱复用法则“封装、继承、多态”是面向对象编程的三大特性。“美丽、智慧、大方”是(我认为的)女人应具有的三大优点。然而我可以经常夸奖一个女人“最美丽,最智慧,最大方”;但我从来不敢自吹自己写的程序“最封装,最继承,最多态”。因为“封装、继承、多态”之间属于相形相克。相形者,指三者中缺少任意一个,则余下二个都将不存在;相克者,则是三者中任意一个如果被发挥或表现到极限,则余者同样无法生存。正由于此
9、,可以说,在抽象意义上,任何时候我们进行的程序设计1,都是力图在针对当前的问题,调整出这三个特性的各自的最佳“实现度”。这也是所有程序员在不断培养,苦苦追求的设计能力。什么叫高手、老手?什么叫新手、生手?虽然我懂得“盐是咸的,味精是甜的,姜是辛”,但我始终做不出一手好汤。虽然我也明白“油门、刹车、方向盘”的作用,但当舒马赫在F1赛道上艺术地操作这三者时,我还在某个坡路上流汗:“又要刹车又要加油门为难人啊!”是的,编程的难点与技艺正在于此。好的程序设计得让人几乎要归之为“艺术”;而糟糕的设计,就像一个蹩脚的厨子走了,留下一桌恶心的菜,你却不得不去咀嚼它,消化它,其间之苦,真非言语所能表达的。很不
10、幸,我就是这样一个蹩脚的厨子,说起来“饭菜”也做了10年矣,但依然无法用“封装、继承、多态”做“面向对象”这一道菜。我这学习编程的10载,倒几近完整地见证了中国软件开发行业发展的全过程。我也看到了很多程序员都和我一样,在不断的摸索、碰壁中缓慢地成长。好在,编程界的泰斗终于感到于心不忍了,开始提供编程界的“菜谱”。这就是如今火热之至的“设计模式”2。当然,正如万有引力一直存在着,而不是等到牛顿被苹果砸了以后才出现。“设计模式”其实一直存在于优秀程序员的设计里。不过是没有形诸于文字,而是表现在代码中。当然,也远远没有泰斗们所归结出来的模式那样具备抽象性、概括性和通用性。说到设计模式,我只是想进一步
11、证明,一个程序员面对不同问题,其驾驭面向对象三大特性的能力的重要性。这篇小文不准备讲那23个经典模式。我想以最基本的“白箱、黑箱复用”为例,开始我的论题。首先,不要看到“黑白箱”就想到测试。“继承、聚合”分别二者的原意,在不同的编程语言里可能有不同的术语,这里我们用最直观的“白箱”,“黑箱”来表述。所引用的代码,来自于早些日子我在“非程序员”(一个国内专讲UML的网站)的BBS的发言。那时有一位可能比我还菜的家伙在上面质疑复用为何要分“白箱”“黑箱”,我一时技痒,上去口水了一番。下面我会通过一个有关“项目”的故事,将我当时用来论证的代码,串成一次代码设计的演进。先得说说“继承”。C+ 提供了三
12、种三继承“private, protected, public”。考虑到JAVA和C# 均只能支持最通用的 public 继承,我们这里就仅以此为继承的标准。所谓的public 继承,字面意思是“公开继承”。要个比方就是除了“老子”声明要带到阴间的财产,其它的,它继承者,都可以获得,使用。(这是一个蹩脚的比喻,但我想你会承认它确实表达了公开继承和其它继承的不同L )。“公开继承”就是一种最常见的白箱复用的设计。它表示:“B复用A的功能,并且B可以了解A的内部细节”。接下来我们讲“黑箱复用”。可以推测,它表示:“B复用A的功能,但B无法看到A的内部细节”。这在像C#或JAVA这样不支持私有或保护
13、继承,也不支持多重继承的语言,是一种极其常见的设计。“黑箱复用”的实现方法是:如果B类想复用A类的功能,不是从A类派生,而是将A类的对象,声明成为B类的成员数据。嗯,是该来举一个“实际项目”了。通过演示这个“项目”的实作,我想,就算你是外行人,你也应能了解一点:事实上程序员最后用指头敲写代码,其实那不算是编程,真的编程,在于之前他的大脑必须做的分析与设计(这句话一会儿我会继续重复)。(声明一下,以下故事纯属虚构)10年的编程生涯,嗯,我的家里有5台电脑了。书房和卧室各有一间,但因为搬家所以常常换,老婆也有一个(不允许再多,也不允许换)。前三年又有了一个女儿,由于出生“脑香门第”,所以最近小家伙
14、也开始用上我的电脑。这些算是项目的背景和资源。项目的初始需求是这样:结婚后每天晚上我都在书房时和电脑打交道到很晚。于是我老婆认为应该把那台笔记本搬到卧室,并责令我写个程序,可以实现她在卧室通过电脑向我发号施令。这样就有了本项目的产生。作为“客户”,老婆当然希望她可以发各种各样的命令;而作为该项目的产品经理、技术架构师,开发负责人,代码撰写者及测试师于一身的我,当然明白正确地引导客户的需求是一个项目是否成功的最重要前提之一,同时也是对客户负责的表现。我向她解释了一个无所不包的软件,首先将让用户界面变得繁杂无比,用户极易操作失误,而失去耐心;其次是众多功能之间将互相牵制,导致表面上得到一个无所不包
15、的软件,实际功能却强项不强,弱项更弱等等最后我也委婉地提到了它对开发周期的可能的影响,以及在开发和后期维护费用上恐怕会出现几何级的增长最后约定是只实现最为常见两条命令的发送:a)“老公限N分种内来睡觉,否则门将反锁。”;b)“脚已洗好,请来端盆。”有了具体的需求描述,这下显得清楚多了。当然,老婆也不吃素的。在具体功能之外,也提出一些速度,性能的要求(这样就可以杜绝我在限定时间内无反应,会推托是软件传送命令太慢等后路),最重要一点也提到该系统应具备一定的扩展性,以备今后增加新的命令的要求等等需求之后是概要设计,首先我确定通过局域网,采用SOCKET来实现传输,而不是通过串口并口红外线或蓝牙。无论
16、是硬件还是软件,这方面的资源均充备,这算是对开发资源做了认真详实的调研并确定。然后我把数据流图画到了概要设计。在概要设计内,我也决定了将有采用.Net + C# 来进行开发,当然,也提到了采用Win32+ C或C+或JAVA 的可能性。最后我也在概要设计里提出,由于该系统的简小,在速度,性能,及扩展性并无太多要求,所以应将设计的天平侧向于“易用性”(以博取老婆欢心)。界面上的东西,及第二条命令数据的流程,均略。之后开始详细设计,秉承概要设计的思想,我觉得将两条命令的发送分别提供。那么要不要采用“多态”?即是否将发送两条命令的发送动作取同一命名?考虑到以后可能会有新的命令扩展,这里采用多态会带来
17、麻烦。所以我在这一步详细设计里,放弃多态特性之一。很显然,我对“扩展性”虽然没有完全忽略,甚至是在概要和详细设计里都可见“扩展性”的影响,但问题我缺少对“扩展”与“易用”做深入的,更具体的考虑。所以下一步的错误的根本,已经埋下来。下面我开始提供设计的伪代码。假设C# 提供基类Socket,用于在网络发送数据。我没有标出函数参数 data 的数据类型。但显然,作为该类的设计者,他并不知道你要发送什么样的数据(老婆的命令?老板的命令?)所以这个Send () 可以发送的 data 肯定是无具体含义的。我们可称为无格式的数据。而我们要发送的两个有着具体意义的命令。根据前面设计。我们需要为这两个类分别
18、提供发送函数。当然,这两个发送具体命令的函数,最终肯定是要调用上述系统提供的Send()命令来完成实际发送操作。让我们来继承它:是的,我派生了一个新类:“卧室的Socket”。这一命名表征了我心里其实很清楚,我要设计一个仅供卧室那端的人使用的Socket。而我对两个具体的命令,提供了名字直观的两个函数,这也充分体现了我正在按概要设计的要求进行详细设计:请看,通过我对原来的Socket 类的派生,以及我对它的Send()动作的扩展,就在原来抽象的,无特别应用方向的类的基础上,得到一个新类,它有具体应用方向,也有具体意义的动作。这比起拿起Socket就直接使用的人(这类人往往是C的高手);或者比起
19、为了“多态”而“多态”,从而把新加的两个函数也命名为Send()的人(这往往是刚接触C+才几天的人),我的这个设计,确实显得很正确。然而,事实上,这个设计在面向对象编程的世界里,仍然是一个拙劣的设计。在面向对象编程领域里有经验的程序员,我想已经看出其中的欠妥之处。假设这个项目付诸实施了。当老婆的人倒也没有提出什么扩展。光阴荏苒,结婚三年过去了,我们有了一个孩子;然后又是三年过去了,我们的孩子也开始会在电脑上施展她的天才。对这个软件提出了她看法:“爸爸,应该增加一个给我送牛奶的命令”。扩展需求终于出现了。然而,6年过去了,我对这个软件的记忆是零。没有看设计文档,我就开始看代码。然后我看到一个类:
20、BedroomSocket。我开始使用它,然后我看到它有三个有关发送的方法:bool Send();bool SendSackCommand();bool SendFootBathCommand();作为一个使用者,我并不想去花时间了解BedroomSocket的具体细节,所以我并不知道其中那个Send() 其实是来自Socket这个基类(在实际大型项目开发中,比如大型ERP,专门写上层业务逻辑的程序员,甚至是没有权限可以看到他所使用的类的设计文档,更看不到源代码)。我错误地认为当初设计BedromSocket时,可能是为了易于对付一些新加的命令,所以提供了一个通用的Send()方法。就这样,
21、纵然有100个项目经理,也无法在第一时间内阻止我义无反顾地通过BedroomSocket的实例来调用Send(),我会发现这个Send()实在太好用了,什么格式的数据都可以发送。也就这样,一个项目原来的设计倾向开始出现偏差。如果这种情况在多人之间出现多次,那么一个项目的设计风格与模式,就将被每个人的理解而肢解成五花八门。不仅仅是在人的方面:理解,改错,扩展等方面会增加难度,而且对于代码本身,也必然由于模块之间接合困难,而需要增加很多附加代码,最终是程序运行效率低下。你可以怪罪后来者(在这个例子里仍然是“我”),不去深入学习需求,概要,设计文档。但正如我前面所言,对一个大的项目,会按设计的层次分
22、成多个子项目; 要每一个人都去学习每一个项目的详细设计文档,并且最好是从需求开始看起,这是不可能的。再考虑那些中间件的实现,通常都凝聚了一个软企的核心技术这种情况下,分配在实现业务逻辑的程序员,没有权限去学习中间件的具体设计思路。大家看到的,永远只是对方的接口。类似于我看到了Bedroom接口透露出来的三个方法,但我不知道这些方法的实现背景。针对这个例子中碰上的问题。我们可以将“白箱复用”(这里是继承),改为“黑箱复用”。在这次设计中,Socket 的对象成为类 BedroomCommand的一个成员。类BedroomCommand不再是通过“继承”来获得网络发送的能力。而是通过“拥有”一个S
23、ocket对象来获得该对象所有公开的能力。由于Socket 的对象sender 在BedroomCommand 里被声明为私有(private,,或者也可以是保护protected),所以,有关Socket网络发送的能力,仅有BedroomCommand 的设计者可以直接获取和使用。这就是“黑箱复用”的一种常见方法。BedroomCommand 的使用者不再需要面对 Send() 。它所能看见和用到的接口,是BedroomCommand 提供三个意义明确的发送方法: public bool SendSackCommand (); public bool SendFootBathCommand
24、(); public bool SendMilkCommand();这样,我们就解决前面的问题。我们实现了一个类,它提供了它应有的功能,同时杜绝提供它不该有的功能。这正是一个良好的设计的基本标准。这么看来,是不是黑箱复用总是白箱复用来得正确?答案当然不是如此,下面我们继续给这个设计制造问题想要给“设计”制造问题,最好的办法就是修改“需求”了。我们假设原来的 Socket 类在除了提供一个公开对外的Send()方法以外,还提供了一个保护的SetOptions()方法。该方法用于对网络发送做一些参数调整,以便可以定制出更符合具体要求的网络发送能力。class Socketpublic Send (
25、data); /发送数据 protected SetOptions(); /定制网络条件 ;Socket 的设计者,认为SetOptions这一能力是不能直接对外公开,所以SetOptions被设计为“保护(protected)”。这就使得:除非是Socket本身或它的继承它的类,否则就无法使用到SetOptions。我们前面讲的“白箱复用”,正是继承。这就给我们出现一个两难:如果使用“白箱复用”,那么我们可以获得我们想要的SetOptions,但同时我们却不得不公开了我们不想公开的Send。如果使用“黑箱复用”,那么们可以不公开Send。但却无法获得SetOptions的能力。由此产生了“复
26、合复用”。(一般来说,SetOptions() 在 Socket 里不会被设置成 virtual,所以在C# 里,我们加上 new 指示符,而在C+,最直接的方法是另取一个名字,比如叫 SetMyOptions(),如此可以避免关于编译器说我们覆盖了基类同名函数的小问题。如果SetOptions是virtual类,则不存在该问题。另外,在C+里, base.SetOptions(),应写成: Socket:SetOptions())问题得以完美解决。Socket提供的超强能力,只有BedroomCommand的设计者能获得,使用。并且通过BedroomCommand的设计者来决定要对外公开哪些能力。任何一个后来的程序员,无论他是老手还是新手,都不会在使用BedroomCommand上出现偏差。就算是我在下一个6年之后,我也能正确地使用BedroomCommand。这样的一个设计,针对当前问题,做到
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 学校521管理制度
- 室内游泳馆管理制度
- 小培训公司管理制度
- 敬老院遗产管理制度
- 林业局风险管理制度
- 标本交接及管理制度
- 档案全覆盖管理制度
- 检查院内部管理制度
- 检验标准品管理制度
- 槽罐车安全管理制度
- 西藏自治区2022年事业单位考试真题与答案解析
- GB/T 42605-2023移动式压力容器修理导则
- 稀磁半导体与自旋电子学
- GA 1807-2022核技术利用单位反恐怖防范要求
- 2023-2024学年江苏省太仓市小学语文五年级期末自测试卷
- 发展汉语(第二版)高级综合Ⅰ第8课课件
- 《工程伦理论文3700字(论文)》
- GB/T 730-2008纺织品色牢度试验蓝色羊毛标样(1~7)级的品质控制
- GB/T 3672.1-2002橡胶制品的公差第1部分:尺寸公差
- 五年级语文下册词句段运用专项复习教学设计
- 优秀集体评选-会计12级
评论
0/150
提交评论