OO面相对象设计的五大原则

S.O.L.I.D

  1. The Single Responsibility Principle(单一职责SRP)
  2. The Open/Closed Principle(开闭原则OCP)
  3. The Liskov Substitution Principle(里氏替换原则LSP)
  4. The Interface Segregation Principle(接口分离原则ISP)
  5. The Dependency Inversion Principle(依赖反转原则DIP)
简单的解释:

1. SRP(Single Responsibility Principle 单一职责原则) 
所谓单一职责,就是一个设计元素只做一件事。
2. OCP :开闭原则
“对变更关闭;对扩展开放”。
3.LSP——里氏替换原则 
4.DIP 依赖倒置原则 
依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。 
5.ISP 接口隔离原则 
使用多个专门的接口比使用单一的总接口要好。


具体详解:

单一职责(OO五大原则之三)
确切的说就是松耦合
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者会让这个类完成其他职责的能力。使程序之间的耦合度增加,当变化发生时,设计会遭受到意想不到的破坏。
软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责。

                              开放关闭原则(OO五大原则之二)

这个原则有两个特征,对于扩展是开放的,对于更改是封闭的。怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本呢?开放-封闭给我们答案。

无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。等到变化发生时立即采取行动。在我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化。面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。

我们希望的是在开发工作展开不久就知道可能发生的变化。查明可能发生的变化所等待的时间越长,要创建正确的抽象就越困难。开放-封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所生成的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然后,对于应用程序中的每个部分都可以地进行抽象同样不是一个好注意。拒绝不成熟的抽象和抽象本身一样重要。


                                  简单工厂模式

简单工厂模式:有一个父类需要做一个运算(其中包含了不同种类的几种运算),将父类涉及此运算的方法都设成虚方法,然后父类派生一些子类,使得每一种不同的运算都对应一个子类。另外有一个工厂类,这个类一般只有一个方法(工厂的生成方法),这个方法的返回值是一个超类,在方法的内部,根据传入参数的不同,分别构造各个不同的子类的对象,并返回。客户端并不认识子类,客户端只认识超类和工厂类。每次客户端需要一中运算时,就把相应的参数传给工厂类,让工厂类构造出相应的子类,然后在客户端用父类接收(这里有一个多态的运用)。客户端很顺理成章地用父类的计算方法(其实这是一个虚方法,并且已经被子类特化过了,其实是调用子类的方法)计算出来结果。如果要增加功能时,你只要再从父类中派生相应功能的子类,然后修改下工厂类就OK了,对于客户端是透明的。


                              策略模式

就是将算法封装到不同的类中,然后根据客户端传入的对象和数据的不同分别调用不同的对象和算法方法

策略模式:策略模式更直接了一点,没有用工厂类,而是直接把工厂类的生成方法的代码写到了客户端
。客户端自己构造出了具有不同功能的子类(而且是用父类接收的,多态),省掉了工厂类。策略模式定义
了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客
户。这里的算法家族和简单工厂模式里的父类是同一个概念。当不同的行为堆砌在一个类中时,就很难避

免使用条句来选择合适的行为,将这些行为封装在一个个独立的策略子类中,可以在客户端中消除条件语句。

                  迪米特法则
迪米特法则(Law of Demeter或简写LoD)又叫最少知识原则(Least Knowledge Principle或简写为LKP),也就是说,一个对象应当对其它对象有尽可能少的了解。

     就是说一个对象在了解其他的对象的时候,只需要认识更少的对象,不需要全部都认识,
通俗的说法,比如一个人去自动取款机去取款,他只需要找到取款机就可以了,他不需要知道工商银行的取款机,或者农业银行的取款机,或者邮政的取款机,只需要拿着自己标示有银联的银行卡就可以取到钱,因为银联公司已经集合了所有银行的业务


    合成/聚合复用原则
合成/聚合复用原则(Composite/Aggregate Reuse Principle或CARP)经常又叫做合成复用原则(Composite Reuse Principle或CRP),就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。

通俗的说法,比如,我们要制造一个汽车,组成汽车的零件有车体,轮胎,发动机等零件,在没有组装汽车之前,这些零件都是单一的一个类,在组装汽车的时候我们把这些零件都放到汽车上,所以汽车的类里面已经有了车体类,轮胎类,发动机类的对象,是这些类能够复用

      接口隔离ISP (OO五大原则之一)

一个类对另一个类的依赖应该表现成依赖尽可能小的接口。

这个原则是用来处理胖接口的缺陷,避免接口承担太多的责任。比如说一个接口内的方法可以被分成好几组,分别为不同的客户程序服务,说明这个接口太胖了。当然,确实也有一些类不需要内聚的接口,但这些类不应该做为单独的类被客户程序直接看到,而应该通过抽象基类或接口来关联访问。

接口污染
所谓接口污染就是为接口添加了不必要的职责。在接口中加一个新方法只是为了给实现类带来好处,以减少类的数目。持续这样做,接口就被不断污染,变胖。实际上,类的数目根本不是什么问题,接口污染会带来维护和重用方面的问题。最常见的问题是我们为了重用被污染的接口,被迫实现并维护不必要的方法。

分离客户程序就是分离接口。如果客户程序是分离的,那么相应的接口也应该是分离的,因为客户程序对它们使用的接口有反作用力。通常接口发生了变化,我们就要考虑所有使用接口的客户程序该如何变化以适应接口的变化。如果客户程序发生了变化呢?这时也要考虑接口是否需要发生变化,这就是反作用力。有时业务规则的变化不是那么直接的,而是通过客户程序的变化引发的,这时我们就需要改变接口以满足客户程序的需要。

分离接口的方式一般分为两种,委托和多继承。前者把请求委托给别的接口的实现类来完成需要的职责,后者则是通过实现多个接口来完成需要的职责。两种方式各有优缺点,通常我们应该先考虑后一个方案,如果涉及到类型转换时则选择前一个方案。

胖接口会导致客户程序之间产生不必要的耦合关系,牵一发而动全身。分解胖接口,使客户程序只依赖它需要的方法,从设计上讲,简单易维护,重用度也高。


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

     
     里氏替换原则(OO五大原则之四)
    通俗的说就是子类替换父类

      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,当存在继承关系时,子类中方法的前置条件必须与超类中被覆盖的方法的前置条件相同或者更宽松;而子类中方法的后置条件必须与超类中被覆盖的方法的后置条件相同或者更为严格。


             .             DIP 依赖倒置原则(OO五大原则之五)
依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。 
简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述: 
抽象不应当依赖于细节;细节应当依赖于抽象; 
要针对接口编程,不针对实现编程。 
通俗的说就是我们在客户端只依赖与强类型的类,比如说接口或者抽象类,不应该把具体实现接口或者抽象类的具体类放到客户端


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值