软件工程之面向对象的设计原则

一、面向对象介绍

1、面向过程

以“过程”为中心的编程思想,分析出解决问题所需的步骤,然后用函数把这些步骤一步步的实现,使用的时候一个个依次调

用就好。面向过程强调的是模块化

2、面向对象

面向对象是相对于面向过程来说的,以“对象”为中心的编程思想,把问题看成一个个对象,通过对象的属性和行为,将问题

解决的。面向对象强调的是把事物对象化

复杂来说面向对象是以“对象”为基本单元构建系统,对象是把数据和数据操作方法放在一起,作为一个相互依存的整体。把

同类的对象抽象出其共性,形成类。所以说类是对象的抽象,对象是类具体化的体现(类是大范围的,一个类可以有多个对

象),类中的大多数数据只能通过本类的方法实现,类包含说明和实现,说明部分被外界所看到,通过简单的外部接口与外

界进行联系;实现部分不被外界所看到,在内部通过不同方法的构建,实现不同的功能。对象与对象之间通过消息进行沟

通,程序流程由用户在使用中决定。

3、面向对象优点

解决越来越复杂的需求,提高了软件的重用性,灵活性和拓展性。

二、面向对象的设计原则

1、开闭原则(OCP)(对可变性的封装原则)

(1)简介

开闭原则是面向对象程序设计的第一原则,即一个软件实体应该对扩展开放,对修改关闭。当一个软件需要增加或者修改某

些功能时应该尽可能的只是在原来的实体中增加代码,而不是修改代码。

(2)优点

        保证了系统具有一定的稳定性,同时也保证了系统的灵活性。使我们设计出来的系统是扩展性良好的系统。

(3)实例

        这种设计在进行业务扩展时需要修改原代码。一种较好的设计方式是:将计价策略合并到Part的getPrice()方法中。

        Part和ConcretePart的示例如下:

//Part类是所有计价策略类的父类
public class Part{
    private double basePrice;
    public void setPrice(doube price){basePrice = price;}
    public double getPrice(){return basePrice;}
}
//子类实现计价策略
public class ConcretePart extends Part{
    public double getPrice(){
        //return (1.45 * basePrice);    //计算额外的费用
        return (0.90 * basePrice);    
    }
}

(4)如何在OO中引入OCP原则

        把对实体的依赖改为对抽象的依赖即可。

(5)怎样做到开闭原则

        1)多使用抽象类:

在设计类时,对于拥有共同功能的相似类进行抽象化处理,将公用的功能部分放到抽象类中,所有的操作都调用子类。这

样,在需要对系统进行功能扩展时,只需要依据抽象类实现新的子类即可。

        2)多使用接口:

与抽象类不同,接口只定义子类应该实现的接口函数,而不实现公有的功能。在现在大多数的软件开发中,都会为实现类定

义接口,这样在扩展子类时实现该接口。如果要改换原有的实现,只需要改换一个实现类即可。

2、里氏替换原则(LSP)

(1)简介

对于基类(父类)是合法的操作,对于子类也一定合法。换言之,基类出现的地方,子类也可以出现。子类必须能够替换掉

它们的父类,并出现在父类能够出现的任何地方。经过替换,代码还能正常工作。

(2)优点

        能够保证继承关系的正确性。

(3)实例

        ①正方形不是长方形的子类

//长方形类
class Rectangle{
    double length;
    double width;
    public double getLength(){
        return length;
    }
    public void setLength(double length){
        this.length = length;
    }
    public double getWidth(){
        return length;
    }
    public void setWidth(double width){
        this.width = width;
    }
}
//正方形类
class Square extends Rectangle{
    //由于正方形长宽必须相等所以在方法setLength和setWidth中对长宽赋值相同
    public void setWidth(double width){
        super.setWidth(width);
        super.setLength(width);
    }
    public void setLength(double length){
        super.setWidth(length);
        super.setLength(length);
    }
}
//测试类
class TestRectangle{
    //模拟长方形宽度逐渐增长的效果
    public resize(Rectangle rec){
        while(rec.getWidth()<=rec.getLength()){
            rec.setWidth(rec.getWidth()+1);
        }
    }
}

在以上的代码实例中,我们运行时就会发现,把一个普通的长方形作为参数传入resize方法,就会看到长方形长度逐渐增

加,当宽大于长时代码就会停止,这种行为符合我们的预期。当我们传入一个正方形传入resize方法后,会看到正方形的宽

度和长度都在不断增加,代码会一直执行,知道系统产生溢出错误。

        所以Rectangle类型不能被Square替代。他们之间的继承关系违反了里氏替换原则,即正方形不是长方形。

(4)总结

1)面向对象的设计关注的是对象的行为。它是使用“行为”来对对象进行分类的,只有行为一致的对象才能抽象出一个类来。

2)类的继承关系就是一种"Is-A"关系,实际上是指行为上的“Is-A"关系,可以把它描述为“Act-As”。

3)继承关系要求子类要具有基类全部的行为。

4)所有派生类的行为功能必须和使用者对其基类的期望保持一致,如果派生类达不到这一点,那么必然违反里氏替换原则。

3、依赖倒置原则(DIP)

(1)简介

1)高层模块不应该依赖于底层模块,二者都应该依赖于抽象

2)抽象不应该依赖于细节,细节应该依赖与抽象,要针对接口编程,不要针对实现编程。

注:

低层模块是指实现了一些基本的或初级的操作的低层次的类。

高层次模块是指封装了某些复杂逻辑的类,并且其依赖于低层次的类。

依赖是指在程序设计中,如果一个模块A使用/调用了另一个模块B,我们称模块A依赖模块B。

高层模块为什么不能依赖底层模块?

高层模块包含了一个应用程序中的重要的策略选择和业务模型,正是这些高层模块才使得其所有的应用程序区别于其他,如

果高层依赖与低层,那么对低层模块的改动就会直接影响到高层模块,从而迫使它们依次做出改动。

(2)具体原则

1)任何变量都不能拥有一个具体类的指针或者引用。

2)任何类都不应该从具体类派生。

3)任何方法都不应该覆写基类中已经实现的方法。

也就是说应当使用接口和抽象类进行变量类型声明、参数声明、方法返还类型说明,以及数据类型的转换等,而不要用具体

类进行变量的类型声明、参数类型声明、方法返还类型说明,以及数据类型的转换等。

要保证做到这一点,一个具体的类应当只实现接口和抽象类中声明过的方法,而不要给出多余的方法。

(3)类结构设计方式

上层类(High Level Classes)->抽象接口层(Abstraction Layer)->低层类(Low Level Classes)

(4)实例

这种设计的缺点:耦合太紧密,Light发生变化将影响ToggleSwitch

解决办法一:将Light变成Abstract,然后具体子类继承自Light。


优点:ToggleSwitch依赖于抽象类Light,具有更高的稳定性,而BulbLight与TubeLight继承自Light,可以根据开闭原则进行扩展。

缺点:如果用ToggleSwitch控制一台电视就很困难了。

解决办法:

(5)总结

依赖倒置原则要求用户尽量依赖于抽象而不是实现。

传统的过程式的程序设计方法倾向于使得高层模块依赖低层模块,DIP原则把这个依赖关系倒转过来。一般而言,高抽象层次包含的是应用系统的商务逻辑和宏观的控制过程,而低层次模块包含了一些具体的算法。

低层次代码经常会变动,高层次则相对稳定一些。为了更有效的保持系统稳定性,应该使得低层次的模块依赖于高层次的米快,从复用的角度来看,只有实现依赖倒置原则,才会避免当低层模块发生改变的时候不会倒置高层次模块的修改。

4、接口隔离原则(ISP)

(1)描述

尽量使用多个小而专用的接口而不是单一的接口

(2)实例

实例中Agent接口中的方法过多。 修改如下

5、合成聚合复用原则(CARP)

(1)描述

优先使用合成和聚合,而不是继承来达到复用的目的。终态类不可继承,要复用终态类的代码,唯一的办法就是合成或者聚

合。因为一个非终态类的内部成员向子类公开,因此,继承关系是一种“白箱”复用。而白箱复用是一种破坏封装原则的复用

办法。

6、迪米特法则(LoD)(最少知识法则)

(1)描述

指一个对象应当对其它对象尽可能少的了解,并尽可能少的与其他对象发生联系。与模块化程序设计中的模块低耦合高内聚

是同样的道理。

7、单职责原则(SRP)

(1)描述

一个类应该有且仅有一个职责。职责的定义:所谓一个类的职责是指引起该类变化的原因,如果一个类具有一个以上的职

责,那么就会有多个不同的原因引起该类变化,其实就是耦合了多个互不相关的职责,就会降低这个类的内聚性。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值