设计模式(基础篇)-------软件设计七大设计原则(续二)

接口分离原则------------ ISP (Interface Segregation Principle)

     这个是接口设计的基础。接口在程序中好像是一本书的目录一样,定义的接口方法,在实现这个接口时间必须全部实现这个借口的方法。用专业术语简而言之: "客户端不应该被迫依赖于它们不用的接口。" 一个类对另外一个类的依赖是建立在最小的接口上(适合自己)。使用多个专门的接口比使用单一的总接口要好(只定义一个接口,实现N多个方法,就会出现胖接口现象,这个接口的N的方法可能对另一个要实现接口的类来说根本是一堆垃圾)。根据客户需要的不同,而为不同的客户端提供不同的服务是一种应当得到鼓励的做法。就像"看人下菜碟"一样,要看客人是谁,再提供不同档次的饭菜。“胖接口”会导致他们的客户程序之间产生不正常的并且有害的耦合关系。当一个客户程序要求该胖接口进行一个改动时,会影响到所有其他的客户程序。因此客户程序应该仅仅依赖他们实际需要调用的方法。 接口隔离原则确保实现的接口有他们共同的职责,它们是明确的,易理解的,可复用的。

下面是违反接口隔离原则的一个胖接口:

image

注意到IBird接口包含很多鸟类的行为,包括Fly()行为。现在如果一个Bird类(如Ostrich)实现了这个接口,那么它需要实现不必要的Fly()行为(Ostrich不会飞)。

如果这个"胖接口"拆分未两个不同的接口,IBird和IFlyingBird,IFlyingBird继承自IBird。

image

这里如果一种鸟不会飞(如Ostrich),那它实现IBird接口。如果一种鸟会飞(如KingFisher),那么它实现IFlyingBird。

    再举一个例子,你到商场买一台电视机,发现有两台功能完全一样的电视机,但是一个上面全是按键,另一个简洁设计只有需要的按键,你会选择哪一个呢?.再想想Iphone手机被人接受的原因,不正是优秀的简洁设计吗?

 

依赖倒置原则-----------------DIP(Dependence Inversion Principle)

 

听起来很难理解的样子,其简单的意思:”高层模块不应该依赖底层模块,两者都应该依赖其抽象。” 抽象不应当依赖于细节,,细节应当依赖于抽象。也就是面向抽象编程,不依赖具体编程。

现在假设你有一辆丰田,如果车轮子不幸中标爆胎了,你是去卖一辆新车,还是,买一车胎呢?.当然是买一个车胎搞定了。加入你在换车胎时惊讶的发现你买的车子是集成的,也就是说所有的器件是一个整体,无法更换一部分。你会做何感想?我估计你的第一想法是找生产商暴打一顿,有这么坑人的汽车设计吗?幸好幸好,我们汽车的设计都是高成分的模块设计,每一个模块被构造在一个部分,高层模块依赖于底层模块,实现插拔使用,随意更换。这样的设计才是为客户维护考虑的优良产品。我们的软件系统也是一样的。

看下面的一个关于汽车的类图:

clip_image002[4]

注意到上面Car类有两个属性,它们都是抽象类型(接口)。引擎和车轮是可插拔的,因为汽车能接受任何实现了声明接口的对象,并且Car类不需要做任何改动。

如果代码中不用依赖倒置,我们将面临如下风险:

  • 使用低级类会破环高级代码;
  • 当低级类变化时需要很多时间和代价来修改高级代码;
  • 产生低复用的代码;

 

 

 

 

 

 

 

 

合成/聚合复用原则 --------------- CARP (Composite/Aggregate Reuse Principle)

 

       合成/聚合复用原则,经常又被人们称为合成复用原则(Composite Reuse Principle,简称为CRP).合成聚合复用原则是指在一个新的对象中使用原来已经存在的一些对象,是这些原来已经存在的对象称为新对象的一部分,新的对象通过向这些原来已经具有的对象委派相应的动作或者命令达到复用已有功能的目的。表达结果是:要尽量使用合成和聚合,尽量不要使用继承。咋一听好像是很难理解的,为什么就成这么好的编程思想弃之不用呢?

      这是因为:第一,继承复用破坏包装,它把父类的实现细节直接暴露给了子类,这违背了封装的原则;第二:如果父类发生了改变,那么子类也要发生相应的改变,这就直接导致了类与类之间的高耦合,不利于类的扩展、复用、维护等,也带来了系统僵硬和脆弱的设计。而是用合成和聚合的时候新对象和已有对象的交互往往是通过接口或者抽象类进行的,就可以很好的避免上面的不足,而且这也可以让每一个新的类专注于实现自己的任务,符合单一职责原则。

      聚合(Aggregation)是关联关系的一种,用来表示一种整体和部分的拥有关系。整体持有对部分的引用,可以调用部分的能够被访问的方法和属性等,当然这种访问往往是对接口和抽象类的访问。作为部分可以可以同时被多个新的对象引用,同时为多个新的对象提供服务。在UML图形中聚合用实心菱形箭头表示,箭头指向下级聚合元素。

     合成(Composition)也是关联关系的一种,但合成是一种比聚合强得多的一种关联关系。在合成关系里面,部分和整体的生命周期是一样的。作为整体的新对象完全拥有对作为部分的支配权,包括负责和支配部分的创建和销毁等,即要负责作为部分的内存的分配和内存释放等。从这里也可以看出来,一个合成关系中的成员对象是不能喝另外的一个合成关系共享的。在UML图形中合成用空心菱形箭头表示,指向合成元素。

迪米特法则------LoD(Law of Demeter )

      迪米特法则(Law of Demeter,简写LoD )又叫做最少知识原则(LeastKnowledge Principle 简写LKP),也就是说,一个对象应当对其他对象尽可能少的了解,不和陌生人说话.在类中能用Private修饰符实现的,都尽量不用Public.

    迪米特法则最初是用来作为面向对象的系统设计风格的一种法则,于1987年秋天由lanholland在美国东北大学为一个叫做迪米特的项目设计提出的。被UML的创始者之一Booch等普及。后来,因为在经典著作《 The PragmaticProgrammer》阐述而广为人知。

     狭义的迪米特法则是指:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一类的某一个方法的话,可以通过第三者转发这个调用。

     广义的迪米特法则是指:一个模块设计的好坏的一个重要标志就是该模块在多大程度上讲自己的内部数据与实现的有关细节隐藏起来。

一个软件实体应当尽可能少的与其他实体发生相互作用。

     每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

迪米特法则的目的在于降低类与类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,是的相互间存在尽可能少的依赖关系。

在运用迪米特法则到系统的设计中时,要注意以下几点:

第一:在类的划分上,应当创建弱耦合的类,类与类之间的耦合越弱,就越有利于实现可复用的目标。

第二:在类的结构设计上,每个类都应该降低成员的访问权限。

第三:在类的设计上,只要有可能,一个类应当设计成不变的类。

第四:在对其他类的应用上,一个对象对其他类的对象的应用应该降到最低。

第五:尽量限制局部变量的有效范围。

但是过度使用迪米特法则,也会造成系统的不同模块之间的通信效率降低,使系统的不同模块之间不容易协调等缺点。同时,因为迪米特法则要求类与类之间尽量不直接通信,如果类之间需要通信就通过第三方转发的方式,这就直接导致了系统中存在大量的中介类,这些类存在的唯一原因是为了传递类与类之间的相互调用关系,这就毫无疑问的增加了系统的复杂度。解决这个问题的方式是:使用依赖倒转原则(通俗的讲就是要针对接口编程,不要针对具体编程),这要就可以是调用方和被调用方之间有了一个抽象层,被调用方在遵循抽象层的前提下就可以自由的变化,此时抽象层成了调用方的朋友。

posted @ 2012-03-26 11:13  这里显示的是昵称  阅读(289)  评论(0编辑  收藏  举报
新浪微博