• 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),又称为“最少知识原则”,它的定义为:一个软件实体应当尽可能少的与其他实体发生相互作用。
    迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。
    最经典的例子就是:不要和陌生人说话。
     迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。
     
  • 相关阅读:
    store 加载异常处理与加载信息提示
    jQuery和ExtJS的timeOut超时问题和event事件处理问题
    Virtualbox虚拟机安装CentOS6.5图文详细教程
    虚拟机
    20180104 wdcp中的mysql重启不成功
    MySQL取得某一范围随机数(MySQL随机数)
    安装lszrz,用于上传文件
    cpu占用高 20180108
    怎么实时查看mysql当前连接数
    数据库SQL优化大总结之 百万级数据库优化方案
  • 原文地址:https://www.cnblogs.com/hellokitty1/p/4629108.html
Copyright © 2020-2023  润新知