OOAD之面向对象设计原则

学习这个设计模式 真的觉得很抽象,只有自己多多的领会!

在很多时候,很多的知识都会觉得讲起来是很矛盾的。

本章目标

1 掌握内聚度和耦合度的概念

2 掌握面向对象设计原则 

 

(一)如何衡量软件设计的质量

内聚度:表示一个应用程序的单个单元所负责的任务数量和多样性。内聚与单个类或者单个方法单元相关。(在我自己的理解就是:在一个类中完成自己所有的任务,这些任务都在自己的类中。自己的事情自己做)

耦合度:耦合度表示类之间关系的紧密程度。耦合度决定了变更一个应用程序的容易程度。在紧密耦合的类结构中,更改一个类会导致其它的类也随之需要做出修改。

设计原则:

“高内聚 低耦合”是

在设计时遵循的一定的设计原则:


设计原则名称


设计原则简介


重要性


单一职责原则


类的职责要单一,不能将太多的职责放在一个类中。


开闭原则


软件实体对扩展是开放的,但对修改是关闭的,即在不修改一个软件实体的基础上去扩展其功能。


里氏替换原则


在软件系统中,一个可以接受基类对象的地方必然可以接受一个子类对象。


依赖倒转原则


要针对抽象层编程,而不要针对具体类编程。


接口隔离原则


使用多个专门的接口来取代一个统一的接口。


组合/聚合复用原则


在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚至不使用继承关系。


迪米特法则


一个软件实体对其他实体的引用越少越好,或者说如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,而是通过引入一个第三者发生间接交互。

 

一: 单一职责原则: 

Single Responsibility Principle,SRP

定义:所有的对象都应该有单一的职责,它提供的所有的服务也都仅围绕着这个职责。(对于一个类而言,应该仅有一个引起它变化的原因,永远不要让一个类存在多个改变的理由。)

对单一职责的理解:

eg: 在你一个类中,他即存在属性,又存在一些方法,那么他的职责就不是单一的,他既要担任设计属性,又要担任方法的设计。

 所以一般在一个类中,只存在属性的,即使有一些方法 也只是有getter,setter 方法。把方法写在一个接口中。

单一职责原则的思考:

 1:单一职责原则提出了对对象职责的一种理想期望。

 2:单一职责原则还有利于对象的稳定。

 3:单一职责原则并不是极端地要求我们只能为对象定义一个职责,而是利用极端的表述方式重点强调:在定义对象职责时,必须考虑职责与对象之间的所属关系。(这又有些矛盾了!)

 二:开闭原则  :  开闭原则(Open-Close Principle,简称OCP)是指一个软件实体(类、模块、方法等)应该对扩展开放,对修改关闭

遵循开闭原则设计出来的模块具有两个基本特征:

对于扩展是开放的(Open for extension):模块的行为可以扩展,当应用的需求改变时,可以对模块进行扩展,以满足新的需求。

对于更改是封闭的(Closed for modification):对模块行为扩展时,不必改动模块的源代码或二进制代码。

如何实现开闭原则?

关键在于抽象化

抽象化分为两种情况: 

针对多个领域类的抽象化:

一组对象的共同行为抽象到抽象类或者接口中,而将不同行为的实现封装在子类或者实现类中。接口或抽象类是不能实例化的,因此对修改就是关闭的;而添加新功能只要实现接口或者继承抽象类,从而实现对扩展开放

使用抽象类:在设计类时,对于拥有共同功能的相似类进行抽象化处理,将公用的功能部分放到抽象类中,而将不同的行为封装在子类中。这样,在需要对系统进行功能扩展时,只需要依据抽象类实现新的子类即可。在扩展子类时,不仅可以拥有抽象类的共有属性和共有方法,还可以拥有自定义的属性和方法。

使用接口:与抽象类不同,接口只定义实现类应该实现的接口方法,而不实现公有的功能。在现在大多数的软件开发中,都会为实现类定义接口,这样在扩展子类时必须实现该接口。如果要改换原有的实现,只需要改换一个实现类即可。

eg:

年薪制员工(用Salary类表示)、按小时付费员工(用Hourly类表示)、合同工(用Contractor类表示)共同的行为:计算薪酬  邮寄支票

建立一个抽象类:employee

  有属性:id,name,address;

  有方法:computePay,mailheck,

建立三个类:Salary,Hourly,Contractor,继承抽象类employee,拥有自己的属性,计算薪酬的方法。

 

针对单个领域类的抽象化

将单个领域类中可能会发生变化的行为进行封装,也就是找出类中可能需要变化之处,把它们封装成抽象类或者接口,从而将变化点与不需要变化的代码分离。

 开闭原则是核心:开闭原则是面向对象设计的核心所在。遵循这个原则可以带来灵活性、可重用性和可维护性。

其它设计原则(里氏替换原则、依赖倒转原则、组合/聚合复用原则、迪米特法则、接口隔离原则)是实现开闭原则的手段和工具。

OOAD理论的知识很多,实践的比较少的。理解起来呢 真的比较抽象的,有些时候讲的知识也感觉是矛盾的。

三:里氏替换原则(The Liskov Substitution Principle,LSP)的定义:在一个软件系统中,子类应该能够完全替换任何父类能够出现的地方,并且经过替换后,不会让调用父类的客户程序从行为上有任何改变。

里氏替换原则是使代码符合开闭原则的一个重要的保证,同时,它体现了:

类的继承原则:里氏替换原则常用来检查两个类是否为继承关系。在符合里氏替换原则的继承关系中,使用父类代码的地方,用子类代码替换后,能够正确的执行动作处理。换句话说,如果子类替换了父类后,不能够正确执行动作,那么他们的继承关系就是不正确的,应该重新设计它们之间的关系。

动作正确性保证:里氏替换原则对子类进行了约束,所以在为已存在的类进行扩展,来创建一个新的子类时,符合里氏替换原则的扩展不会给已有的系统引入新的错误。

eg:正方形不是长方形

 

 1 package com.lovo.chap2;
 2 /**
 3  * 长方形 父类
 4  * @author acer
 5  *
 6  */
 7 public class Rectangle {
 8     private int width;
 9     private int height;
10
11     public int getWidth() {
12         return width;
13     }
14     public void setWidth(int width) {
15         this.width = width;
16     }
17     public int getHeight() {
18         return height;
19     }
20     public void setHeight(int height) {
21         this.height = height;
22     }
23
24
25 }

 

 1 package com.lovo.chap2;
 2 /**
 3  * 正方形继承长方形
 4  * @author acer
 5  *
 6  */
 7 public class Square extends Rectangle  {
 8     @Override
 9     public void setHeight(int height) {
10         super.setHeight(height);
11         super.setWidth(height);
12     }
13
14 }

 

 1 package com.lovo.chap2;
 2
 3 public class test {
 4     public void design(Rectangle rc){
 5         while(rc.getHeight()<=rc.getWidth()){//这里为什么还要用等于呢
 6             System.out.println(rc.getHeight());
 7             rc.setHeight(rc.getHeight()+1);
 8
 9         }
10
11     }
12     public static void main(String[] args) {
13         test t=new test();
14         Rectangle re=new Rectangle();
15         re.setHeight(50);
16         re.setWidth(100);
17         t.design(re);
18
19         Square s=new Square();
20         s.setHeight(200);
21         t.design(s);
22
23     }
24
25 }

里氏代换原则给我们的启示:

类的继承原则:如果一个继承类的对象可能会在基类出现的地方出现运行错误,则该子类不应该从该基类继承,或者说,应该重新设计它们之间的关系。
动作正确性保证:符合里氏代换原则的类扩展不会给已有的系统引入新的错误。 

四:依赖倒转原则

依赖倒转原则(Dependency Inversion Principle,简称DIP)是指将两个模块之间的依赖关系倒置为依赖抽象类或接口。

具体有两层含义:

1 高层模块(是指调用的类)不应该依赖于低层模块(是指被调用的类),二者都应该依赖于抽象;

2 抽象不应该依赖于细节,细节应该依赖于抽象。

eg:

人可以通过看书 看报 看网页来学习

 

1 package com.lovo.dip;
2
3 public interface Study {
4     public void read();
5
6 }

 

 1 package com.lovo.dip;
 2 /**
 3  * 读书
 4  * @author acer
 5  *
 6  */
 7 public class ReadBooks implements Study {
 8
 9     @Override
10     public void read() {
11     System.out.println("读书学习");
12     }
13
14 }

 

 

 1 package com.lovo.dip;
 2 /**
 3  * 报纸
 4  * @author acer
 5  *
 6  */
 7 public class ReadPaper implements Study {
 8
 9     @Override
10     public void read() {
11       System.out.println("读报纸学习");
12     }
13
14 }

 

 

 1 package com.lovo.dip;
 2 /**
 3  * 网页
 4  * @author acer
 5  *
 6  */
 7 public class Online implements Study {
 8
 9     @Override
10     public void read() {
11      System.out.println("网页学习!");
12     }
13
14 }

 

1 package com.lovo.dip;
2
3 public class StudyMethods {
4
5     public void read(Study s) {
6        s.read();
7     }
8
9 }

 

 1 package com.lovo.dip;
 2 /**
 3  *
 4  * dip 依赖倒转原则
 5  * @author acer
 6  *
 7  */
 8 public class Test {
 9
10     public static void main(String[] args) {
11         StudyMethods st=new StudyMethods();
12         st.read(new ReadPaper());
13         //st.read(new ReadBooks());
14         //st.read(new Online());
15
16
17     }
18
19 }

 

依赖倒转原则给我们的启示:要针对接口编程,不要针对实现编程。(Program to an interface. not an implementation)

五:组合/聚合复用原则

组合/聚合复用原则(Composite/Aggregation Reuse Principle,CARP)是指要尽量使用组合/聚合而非继承来达到复用目的。另一种解释是在一个新的对象中使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象委托功能达到复用这些对象的目的。 

六:接口隔离原则

接口隔离原则(Interface Segregation Principle,简称ISP)是指客户不应该依赖它们用不到的方法,只给每个客户它所需要的接口。

接口隔离原则两层意思(1):

接口的设计原则:接口的设计应该遵循最小接口原则,不要把用户不使用的方法塞进同一个接口里。如果一个接口的方法没有被使用到,则说明该接口过胖,应该将其分割成几个功能专一的接口,使用多个专门的接口比使用单一的总接口要好。

比如:a 要使用b接口中的test1,test2方法,但是在b接口中有test1,test2,test3,test4方法,那么最好就是把test1,test2 方法分出来 放在另一个接口中。

接口隔离原则两层意思(2):

接口的继承原则:如果一个接口A继承另一个接口B,则接口A相当于继承了接口B的方法,那么继承了接口B后的接口A也应该遵循上述原则:不应该包含用户不使用的方法。反之,则说明接口A被B给污染了,应该重新设计它们的关系。

七:迪米特法则

迪米特法则(Law of Demeter,简称LOD),又称为“最少知识原则”,它的定义为:一个软件实体应当尽可能少的与其他实体发生相互作用。

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

最经典的例子就是:不要和陌生人说话。

 迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。

 

时间: 2024-09-22 00:30:48

OOAD之面向对象设计原则的相关文章

《设计模式》学习笔记1——七大面向对象设计原则

前言 根据这一次的学习计划,系统学习设计模式之前,先系统学习和理解设计原则.面向对象设计原则有如下几类. 原则一:单一职责原则 这是面向对象最简单的原则,对于定义,引用书中所说: 单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因 这里最重要的地方,我个人觉得应该是一个功能领域这一句. 设计的前提是思考,只有进行了思考才能谈得上设计,所以实际设计过程中最重要的还

Java程序员应当知道的10个面向对象设计原则

(设计原则)底线是永远追求高内聚.低耦合的编码或设计. Apache 和 Sun的开源代码是学习Java和OOPS设计原则的良好范例.它们向我们展示了,设计原则在Java编程中是如何使用的.Java JDK 使用了一些设计原则:BorderFactory类中的工厂模式.Runtime类中的单例模式.java.io 类中的装饰器模式.顺便说一句,如果您真的对Java编码原则感兴趣,请阅读Joshua Bloch 的Effective Java,他编写过Java API.我个人最喜欢的关于面向对象设

程序员应知道这十大面向对象设计原则

面向对象设计原则是OOPS编程的核心, 但我见过的大多数Java程序员热心于像Singleton (单例) . Decorator(装饰器).Observer(观察者) 等设计模式, 而没有把足够多的注意力放在学习面向对象的分析和设计上面.学习面向对象编程像"抽象"."封装"."多态"."继承" 等基础知识是重要的,但同时为了创建简洁.模块化的设计,了解这些设计原则也同等重要.我经常看到不同经验水平的java程序员,他们有的不

《面向对象设计实践指南:Ruby语言描述》目录—导读

内容提要 面向对象设计实践指南:Ruby语言描述 本书是对"如何编写更易维护.更易管理.更讨人喜爱且功能更为强大的Ruby应用程序"的全面指导.为帮助读者解决Ruby代码难以更改和不易扩展的问题,作者在书中运用了多种功能强大和实用的面向对象设计技术,并借助大量简单实用的Ruby示例对这些技术进行全面解释. 全书共9章,主要包含的内容有:如何使用面向对象编程技术编写更易于维护和扩展的Ruby代码,单个Ruby类所应包含的内容,避免将应该保持独立的对象交织在一起,在多个对象之间定义灵活的接

面向设计原则理解

    面向对象设计(OOD)核心原则让我的程序模块达到"高内聚低耦合",这是来自于30年前兴起的结构化设计(structured Design),但是同样适用于我们的OOD. 1.高内聚:     高内聚是指某个特定模块(程序,类型)都应完成一系列相关功能,描述了不同程序,类型中方法,方法中不同操作描述的逻辑之间的距离相近.高内聚意味可维护性,可重新性,因为模块对外部的依赖少(功能的完备性).如果两个模块之间的修改,互不影响这说明模块之间是高内聚的.模块的内聚和其担当的职责成反比,即

《面向对象设计实践指南:Ruby语言描述》—第1章 1.2节设计工具

1.2 设计工具 面向对象设计实践指南:Ruby语言描述 设计可不是遵循一套固定规则就完事的动作.它是每次沿着一条分支前进的旅行,在这条路径上早期的选择关闭了某些选择,同时又会打开其他新的选择.在设计过程中,你会徘徊于各种错综复杂的需求中,这里的每个关键时刻都代表着一个决策点,它会对将来产生影响. 像雕塑家有凿子和文稿一样,面向对象的设计师也有自己的工具-原则和模式. 1.2.1 设计原则 SOLID原则首先由Michael Feathers提出,再由Robert Martin进行了推广.它代表

这样做违反了JAVA设计原则吗?

问题描述 我们也许都写过下面的代码现在随着业务的发展,除了D类外,其它子类都需要增加doOtherThing方法,于是我们这样处理:同时D类的doOtherThing方法的写法是:publicvoiddoOtherThing(){//donothing;}请问这么做有什么问题?违反了面向对象设计原则中的哪个原则?如何改进呢? 解决方案 解决方案二:不会影响运行效率放着就放着吧解决方案三:没什么问题,你要是违反了面向对象设计原则中的哪个原则就是D类不应该出现doOtherThing这个方法用接口去

C#面向对象设计的七大原则_C#教程

本文我们要谈的七大原则,即:单一职责,里氏替换,迪米特法则,依赖倒转,接口隔离,合成/聚合原则,开放-封闭 . 1.   开闭原则(Open-Closed Principle, OCP) 定义:软件实体应当对扩展开放,对修改关闭.这句话说得有点专业,更通俗一点讲,也就是:软件系统中包含的各种组件,例如模块(Modules).类(Classes)以及功能(Functions)等等,应该在不修改现有代码的基础上,去扩展新功能.开闭原则中原有"开",是指对于组件功能的扩展是开放的,是允许对其

Delphi面向对象设计的经验原则

Delphi面向对象设计的经验原则 (1)所有数据都应该隐藏在所在的类的内部. (2)类的使用者必须依赖类的共有接口,但类不能依赖它的使用者. (3)尽量减少类的协议中的消息. (4)实现所有类都理解的最基本公有接口[例如,拷贝操作(深拷贝和浅拷贝).相等性判断.正确输出内容.从ASCII描述解析等等]. (5)不要把实现细节(例如放置共用代码的私有函数)放到类的公有接口中. 如果类的两个方法有一段公共代码,那么就可以创建一个防止这些公共代码的私有函数.  (6)不要以用户无法使用或不感兴趣的东