黑马程序员-Java中面向抽象和面向接口设计

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

一、“开闭原则”

在讨论面向抽象和面向接口之前,先来粗略了解下“开闭原则”。
最近在搜索问题的时候总是会看到有人提到“开闭原则”的设计思想。今天就稍微总结下JavaSE中最能体现”开闭”思想的两部分。我觉得在学习早期或多或少的渗透理解一些思想,这种潜移默化的影响对于以后形成强大独到的设计思维体系大有益处。所以先来看下“开闭原则“。

一个好的软件的设计,在于更加容易被复用或维护。当修改某部分时,不会对其它部分造成影响。
“开闭原则”正是面向对象程序语言的可复用设计的基石,其它的模式如里氏替换、依赖注入、迪米特原则都是开闭原则的具体实现工具。

1.定义:一个程序应当对扩展开放,而对修改关闭。
2.好处
(1)在对程序扩展之后,程序拥有了新的功能,使得程序不会因为需求的变化而淘汰。
(2)程序不能再被修改,即自身高度提取的抽象层不能被随意修改,体现了程序的稳定性。
3.实现原则
在开闭原则的设计中,首先要有“闭”的东西才能”开”才能被扩展,所以什么东西需要关闭是关键。
一般在面向对象设计过程中,我们要把对象的共性特征提取出来抽象化,让具体细节由子类实现。这样我们在抽象层面考虑问题就简单化,统一化了。
当把最本质的共性特征提取后,自然就想到了用抽象类和接口来关闭它。

二、面向抽象设计

1.回顾抽象类
抽象类用abstract关键字来声明,抽象类中可以包含抽象方法,抽象方法是只有方法声明部分的特殊方法,用abstract来修饰,通过子类的重写使用。除此之外,抽象类和普通类相同。

2.面向抽象
比如我们要设计一个用来计算几何柱体体积的类:

public class Size{private double floor=0;//底面积private double high=0;//高public Size(double high){this.high=high;}//获取体积的方法public double getSize(){        return floor*high;}//设置三角棱柱底面积public void setTrigonFloor(double l,double h){floor = l*h/2.0;}//设置圆柱底面积public void double setCircleFloor(double r){floor = 3.14*r*r;}}

在本类中可以计算圆柱体的体积,三棱柱的体积,但是计算长方体等其他的就需要修改其中的获取底面积的方法了,这显然是不利于代码维护的。所以我们想到用“开闭”原则去重新设计。

面向抽象第一步:抽象细节
抽象细节就是把变化的,有复杂的实现细节的部分分割出来,将其提取成抽象方法,而不必关心实现的细节。
我们发现计算柱体体积是需要高和底面积,而高是不变的,变化的是底面积的获取方法。所以要考虑把获取底面积的方法抽象化,设计成类。

public abstract class Floor{//返回几何图形的面积public abstract double getFloor();
}

这样就完成了第一步。

面向抽象第二步:面向抽象设计类
面向第一步设计的抽象类,要使用它,就应该面向它来设计一个新类,把它作为成员。如下:

public class Size{//底面积类private Floor floor;//柱体的高private double high;Size(Floor floor,double high){this.floor=floor;this.high=high;}//计算柱体的体积public double getSize(){return floor.getFloor()*high;}}

接下来要使用它就可以通过扩展Floor抽象类来实现。

//这是一个计算三角形面积的实现类
public class Triangle extends Floor{//三角形的低和高double i,h;Triangle(double i,double h){this.i=i;this.h=h;}//重写的方法,获取三角形面积public double getFloor(){return i*h/2;}
}//这是一个计算长方形面积的实现类
public class Cuboid extends Floor{//长方形的长和宽double l,k;Cuboid(double l,double k){this.l=l;this.k=k;}//重写的方法,获取长方形的面积public double getFloor(){return l*k;}
}

下面我们就可以使用了:

public class Test{public static void main(String[] args){// 声明Size类变量Size size;// 声明Floor抽象类变量Floor floor;// 创建长方形面积对象并用floor实例化floor = new Cuboid(52.2, 23.3);// 实例化size变量并用floor初始化size = new Size(floor, 10);// 打印长方体体积的计算结果System.out.println("长方体的体积是:" + size.getSize());// 创建三角形面积对象并用floor实例化floor = new Triangle(20, 58.5);// 实例化size变量并用floor初始化size = new Size(floor, 45);// 打印正三棱柱体积的计算结果System.out.println("三棱体的体积是:" + s   ize.getSize());}
}

总结:面向抽象是把程序中变化的部分抽象提取设计成抽象类,这一步即为“开闭原则”中的“闭”,该抽象类为所有变化部分的共同特性,不可改变。通过扩展抽象类,达到不同的实现,这一步为“开闭原则”中的“开”。最后把抽象类声明为其三、面向接口设计抽象。

三、面向接口设计

1.接口的回顾
(1)接口通过interface 来声明。
(2)接口中方法默认是public abstract。
(3)接口中只能有抽象方法和常量(Java 8 后可以使用default关键字声明默认方法)。

2.接口回调
接口回调是多态的一种体现。通过接口类型变量来接收接口实现类对象,用接口变量调用被实现的方法,这时候是相应的子类对象在调用接口方法,这一过程成为接口回调。

3.面向接口
依然采用上面的例子来说明。
这时我们把求面积方法定义为一个接口,面向该接口设计类。当程序需要添加新的类型时候,只需要再增加一个新的接口的实现类即可。

//把底面积声明成接口
interface Floor{double getFloor();
}

声明实现类:

//声明长方形面积实现类
class Cuboid implements Floor{double l,k;Cuboid(double l,double k){this.l=l;this.k=k;}public double getFloor(){return l*k;}
}
//声明三角行面积实现类
class Triangle extends Floor{       // 三角形的低和高double i, h;Triangle(double i, double h) {this.i = i;this.h = h;}// 获取三角形面积public double getFloor() {return i * h / 2;}
}

测试:

public class Test {public static void main(String[] args) {// 声明Size类变量Size size;// 声明Floor抽象类变量Floor floor;// 创建长方形面积对象并用floor实例化floor = new Cuboid(52.2, 23.3);// 实例化size变量并用floor初始化size = new Size(floor, 10);// 打印长方体体积的计算结果         System.out.println("长方体的体积是:" + size.getSize());// 创建三角形面积对象并用floor实例化floor = new Triangle(20, 58.5);// 实例化size变量并用floor初始化size = new Size(floor, 45);// 打印正三棱柱体积的计算结果System.out.println("三棱体的体积是:" + size.getSize());}
}

总结:面向接口和面向抽象实现原理都是一样的。

四、接口与抽象类的使用环境

1.抽象类可以包含抽象方法,非抽象方法,成员变量,所以它是对多个类进行提取抽象。它隐含着是什么的关系。
2.接口中只能有抽象方法和常量,所以它是比抽象类更高度抽象的。它是对多个功能进行提取抽象,它隐含的是有和没有该功能的关系。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部