您的位置:首页 > 其它

设计模式六大原则:里氏替换原则(五)

2016-05-12 12:42 302 查看
转载请标明:/article/7548887.html

面向对象其它六大原则

单一职责原则-带你走梦幻西游(一)

依赖倒置原则(二)

开闭原则(三)

迪米特原则-带你走进梦幻西游(四)

接口隔离原则(六)

里氏代换原则是由麻省理工学院(MIT)计算机科学实验室的Liskov女士,在1987年的OOPSLA大会上发表的一篇文章《Data Abstraction and Hierarchy》里面提出来的,主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中的蕴涵的原理。2002年,我们前面单一职责原则中提到的软件工程大师Robert C. Martin,出版了一本《Agile Software Development Principles Patterns and Practices》,在文中他把里氏代换原则最终简化为一句话:“Subtypes must be substitutable for their base types”。也就是,子类必须能够替换成它们的基类。

我们把里氏代换原则解释得更完整一些:在一个软件系统中,子类应该可以替换任何基类能够出现的地方,并且经过替换以后,代码还能正常工作。

定义一:

如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。

定义二:

所有引用基类的地方必须能透明地使用其子类的对象。

//
//  里氏替换原则.cpp
//  c++
//
//  Created by 刘龙玲 on 16/5/12.
//  Copyright © 2016年 liulongling. All rights reserved.
//

#include <iostream>
using namespace std;

//案例:鸵鸟非鸟
class Bird
{
public:
bool fly() {
//鸟类可以飞;
return true;
};
int getVelocity()
{
return this->velocity;
}
private:
int velocity;
};

//鸵鸟
class Ostrich: public Bird
{
bool fly() {
//鸵鸟不可以飞;
return false;
};
int getVelocity()
{
return 0;
}
};

int main()
{
//计算鸟飞越黄河所需的时间。
Bird* bird = new Ostrich;
cout<<"h:"<<3000/bird->getVelocity()<<endl;
return 0;
}


由于C++标准没有把除0错当成标准异常,和原生数组访问越界为什么不是一异常一样,主要还是为了“效率/性能”。C++之父在谈C++语言设计的书(The Design and Evolution of C++)里谈到:

“low-level events, such as arithmetic overflows and divide by zero, are assumed to be handled by a dedicated lower-level mechanism rather than by exceptions. This enables C++ to match the behaviour of other languages when it comes to arithmetic. It also avoids the problems that occur on heavily pipelined architectures where events such as divide by zero are asynchronous.”

简单翻译一下: “底层的事件,比如计算上的溢出和除0错,被认为应用有一个同样底层的机制去处理它们,而不是异常。这让C++在涉及到算术领域的性能,可以和其它语言竞争。再者,它也避免了当某些事件,比如除0错是异步的情况下(译者注:比如在别一个线程里发生)所可能造成的‘管道重重’的架构这一问题。”所以我换成用java代码实现这个案例

package com.lll.test;
//鸟
public class Bird {
private int velocity;

public int getVelocity() {
return velocity;
}

public void setVelocity(int velocity) {
this.velocity = velocity;
}
}


package com.lll.test;
//鸵鸟
public class Ostrich extends Bird{

public int getVelocity() {
return 0;
}
}


package com.lll.test;

public class main {

public static void main(String[] args) {
Bird bird = new Bird();
bird.setVelocity(100);
int h = flyTime(bird);
System.out.println("飞行时间是:"+h);
}

public static int flyTime(Bird bird)
{
return 3000/bird.getVelocity();
}
}


运行结果正确:

飞行时间是:30

package com.lll.test;

public class main {

public static void main(String[] args) {
Bird bird = new  Ostrich();
bird.setVelocity(100);
int h = flyTime(bird);
System.out.println("飞行时间是:"+h);
}

public static int flyTime(Bird bird)
{
return 3000/bird.getVelocity();
}
}


运行结果异常:

Exception in thread “main” java.lang.ArithmeticException: / by zero

at com.lll.test.main.flyTime(main.java:11)

at com.lll.test.main.main(main.java:7)

我们知道,面向对象的语言的三大特点是继承,封装,多态,里氏替换原则是依赖于继承,多态这两大特性。里氏替换原则的定义是,所有引用基类的地方必须能透明地使用其子类的对象。通俗来讲是只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误和异常。而我们在使用flyTime方法时 ,当使用者flyTime方法里的参数Bird被Ostrich替换掉后,结果出现了异常,那么它明显违背了里氏替换原则。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: