Java 面向抽象编程和面向接口编程
以下內(nèi)容來自《Java 2實(shí)用教程》,主編:耿祥義、張躍平
鑒于面向抽象編程和面向接口編程思維培養(yǎng)的重要性,寫此博客鞏固。
面向抽象編程:
在設(shè)計(jì)程序時(shí),經(jīng)常會(huì)使用到abstract類,其原因是,abstract類只關(guān)心操作,而不關(guān)心這些操作具體的實(shí)現(xiàn)細(xì)節(jié),
可以使程序的設(shè)計(jì)者把主要精力放在程序的設(shè)計(jì)上,而不必拘泥于細(xì)節(jié)的實(shí)現(xiàn)(將這些細(xì)節(jié)留給子類的設(shè)計(jì)者),即避免
設(shè)計(jì)者把大量的時(shí)間和精力花費(fèi)在具體的算法上。例如,在設(shè)計(jì)地圖時(shí),首先考慮地圖最重要的輪廓,不必去考慮諸如城
市中的街道牌號(hào)等細(xì)節(jié),細(xì)節(jié)應(yīng)當(dāng)由抽象類的非抽象子類去實(shí)現(xiàn),這些子類可以給出具體的實(shí)例,來完成程序功能的具體
實(shí)現(xiàn)。在設(shè)計(jì)一個(gè)程序時(shí),可以通過在abstract類中聲明若干個(gè)abstract方法,表明這些方法在整個(gè)系統(tǒng)設(shè)計(jì)中的重要性,
方法體的內(nèi)容細(xì)節(jié)由它的非abstract子類去完成。
使用多態(tài)進(jìn)行程序設(shè)計(jì)的核心技術(shù)之一是使用上轉(zhuǎn)型對(duì)象,即將abstract類聲明的對(duì)象作為其子類對(duì)象的上轉(zhuǎn)型對(duì)象,
那么這個(gè)上轉(zhuǎn)型對(duì)象就可以調(diào)用子類重寫的方法。
所謂面向抽象編程,是指當(dāng)設(shè)計(jì)某種重要的類時(shí),不讓該類面向具體的類,而是面向抽象類,即所設(shè)計(jì)類中的重要數(shù)據(jù)
是抽象類聲明的對(duì)象,而不是具體類的聲明的對(duì)象。
以下通過一個(gè)簡單的問題來說明面向抽象編程的思想。
例如,我們已經(jīng)有了一個(gè)Circle類(圓類),該類創(chuàng)建的對(duì)象Circle調(diào)用getArea()方法可以計(jì)算圓的面積。Circle類
的代碼如下:
Circle.java
1 public class Circle { 2 double r; 3 Circle(double r){ 4 this.r=r; 5 } 6 public double getArea() { 7 return(3.14*r*r); 8 } 9 }? 現(xiàn)在要設(shè)計(jì)一個(gè)Pillar類(柱類),該類的對(duì)象調(diào)用getVolume()方法可以計(jì)算柱體的體積。Pillar類的代碼如下:
Pillar.java
1 public class Pillar {2 Circle bottom; //bottom是用具體類Circle聲明的對(duì)象3 double height;4 Pillar (Circle bottom,double height){5 this.bottom=bottom;6 this.height=height;7 }8 public double getVolume() {9 return bottom.getArea()*height; 10 } 11 }上述Pillar類中,bottom是用具體類Circle聲明的對(duì)象,如果不涉及用戶需求的變化,上面Pillar類的設(shè)計(jì)沒有什么不妥,
但是在某個(gè)時(shí)候,用戶希望Pillar類能創(chuàng)建出底是三角形的柱體。顯然上述Pillar類無法創(chuàng)建出這樣的柱體,即上述設(shè)計(jì)的Pillar
類不能應(yīng)對(duì)用戶的這種需求(軟件設(shè)計(jì)面臨的最大問題是用戶需求的變化)。我們發(fā)現(xiàn),用戶需求的柱體的底無論是何種圖形,但
有一點(diǎn)是相同的,即要求該圖形必須有計(jì)算面積的行為,因此可以用一個(gè)抽象類封裝這個(gè)行為標(biāo)準(zhǔn):在抽象類里定義一個(gè)抽象方法
abstract double getArea(),即用抽象類封裝許多子類都必有的行為。
現(xiàn)在我們來重新設(shè)計(jì)Pillar類。首先,我們注意到柱體計(jì)算體積的關(guān)鍵在計(jì)算出底面積,一個(gè)柱體在計(jì)算底面積時(shí)不應(yīng)該關(guān)心
它的底是什么形狀的具體圖形,只應(yīng)該關(guān)心這種圖形是否具有計(jì)算面積的方法。因此,在設(shè)計(jì)Pillar類時(shí)不應(yīng)該讓它的底是某個(gè)具體
類聲明的對(duì)象,一旦這樣做,Pillar類就依賴該具體類,缺乏彈性,難以應(yīng)對(duì)需求的變化。
下面我們將面對(duì)抽象重新設(shè)計(jì)Pillar類。首先編寫一個(gè)抽象類Geometry,該抽象類中定義了一個(gè)抽象的getArea()方法。
Geometry類如下:
Geometry.java
1 public abstract class Geometry { 2 public abstract double getArea(); 3 }上述抽象類將所有計(jì)算面積的算法抽象為一個(gè)標(biāo)識(shí):getArea(),即抽象方法,不用考慮算法的細(xì)節(jié)。
現(xiàn)在Pillar類的設(shè)計(jì)者可以面向Geometry類編寫代碼,即Pillar類應(yīng)該把Geometry對(duì)象作為自己的成員,該成員可以調(diào)用Geometry
的子類重寫的getArea()方法。這樣一來,Pillar類就可以將計(jì)算底面積的任務(wù)指派給Geometry類的子類的實(shí)例(用戶的各種需求將由
不同的子類去負(fù)責(zé))。
以下Pillar類的設(shè)計(jì)不再依賴具體類,而是面向Geometry類,即Pillar類中的bottom是用抽象類Geometry聲明的對(duì)象,而不是具體類
聲明的對(duì)象。重新設(shè)計(jì)的Pillar類的代碼如下:
Pillar.java
1 public class Pillar {2 Geometry bottom; //bottom是抽象類Geometry聲明的變量3 double height;4 Pillar (Geometry bottom,double height){5 this.bottom=bottom;6 this.height=height;7 }8 public double getVolume() {9 if(bottom==null) { 10 System.out.println("沒有底,無法計(jì)算體積"); 11 return -1; 12 } 13 return bottom.getArea()*height; //bottom可以調(diào)用子類重寫的getArea方法 14 } 15 }下列Circle和Rectangle類都是Geometry的子類,二者都必須重寫Geometry類和getArea()方法來計(jì)算各自的面積。
Circle.java
1 public class Circle extends Geometry{ 2 double r; 3 Circle(double r){ 4 this.r=r; 5 } 6 public double getArea() { 7 return(3.14*r*r); 8 } 9 }Rectangle.java
1 public class Rectangle {2 double a,b;3 Rectangle(double a,double b){4 this.a=a;5 this.b=b;6 }7 public double getArea() {8 return a*b;9 } 10 }注意到,當(dāng)增加了Circle和Recangle類后,我們不必修改Pillar類的代碼。現(xiàn)在,我們就可以用Pillar類創(chuàng)建
出具有矩形底或圓形底的柱體了,如下列Application.java所示,程序運(yùn)行效果如圖5.13所示。
Application.java
1 public class Application {2 public static void main(String args[]) {3 Pillar pillar;4 Geometry bottom=null;5 pillar = new Pillar(bottom,100); //null底的柱體6 System.out.println("體積"+pillar.getVolume());7 bottom=new Rectangle(12,22);8 pillar=new Pillar(bottom,58); //pillar是具有矩形底的柱體9 System.out.println("體積"+pillar.getVolume()); 10 bottom=new Circle(10); 11 pillar =new Pillar (bottom,58); //pillar是具有圓形底的柱體 12 System.out.println("體積"+pillar.getVolume()); 13 } 14 }通過面向抽象類設(shè)計(jì)Pillar類,使得該P(yáng)illar類不再依賴具體類,因此每當(dāng)系統(tǒng)增加新的Geometry的子類時(shí),
例如增加一個(gè)Triangele子類,那么我們不需要修改Pillar類的任何代碼,就可以使用Pillar創(chuàng)建出具有三角形底
的柱體。
通過前面的討論我們可以做出如下總結(jié):
面向抽象編程的目的是為了應(yīng)對(duì)用戶需求的變化,將某個(gè)類中經(jīng)常因需求變化而需要改變的代碼從該類中分離
出去。面向抽象編程的核心是讓類中每種可能的變化對(duì)應(yīng)地交給抽象類的一個(gè)子類去負(fù)責(zé),從而讓該類的設(shè)計(jì)者不
去關(guān)心具體實(shí)現(xiàn),避免所設(shè)計(jì)的類依賴于具體的實(shí)現(xiàn)。面向抽象編程使設(shè)計(jì)的類容易應(yīng)對(duì)用戶需求的變化。
面向接口編程:
抽象類最本質(zhì)的特性是可以包含抽象方法,這一點(diǎn)和接口類似,只不過接口中只有抽象方法而已。抽象類將其抽象方法
的實(shí)現(xiàn)交給其子類,而接口將其抽象方法的實(shí)現(xiàn)交給實(shí)現(xiàn)該接口的類。在設(shè)計(jì)程序時(shí),學(xué)習(xí)怎樣面向接口去設(shè)計(jì)程序。接口
只關(guān)心操作,但不關(guān)心這些操作的具體實(shí)現(xiàn)細(xì)節(jié),可以使我們把主要精力放在程序的設(shè)計(jì)上,而不必拘泥于細(xì)節(jié)的實(shí)現(xiàn)。也
就是說,可以通過在接口中聲明若干個(gè)abstract方法,表明這些方法的重要性,方法體的內(nèi)容細(xì)節(jié)由實(shí)現(xiàn)接口的類去完成。
使用接口進(jìn)行程序設(shè)計(jì)的核心思想是使用接口回調(diào),即接口變量存放實(shí)現(xiàn)該接口的類的對(duì)象的引用,從而接口變量就可以回
調(diào)類實(shí)現(xiàn)的接口方法。利用接口也可以體現(xiàn)程序設(shè)計(jì)的“開-閉原則”,即對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。例如,程序的主要設(shè)計(jì)
者可以設(shè)計(jì)出如下圖所示的一種結(jié)構(gòu)關(guān)系。
從下圖可以看出,當(dāng)程序再增加實(shí)現(xiàn)接口的類(由其他設(shè)計(jì)者去實(shí)現(xiàn)),接口變量variable所在的類不需要做任何修改,
就可以回調(diào)類重寫的接口方法。
當(dāng)然,在程序設(shè)計(jì)好后,首先應(yīng)當(dāng)對(duì)接口的修改“關(guān)閉”,否則,一旦修改接口,例如,為它增加一個(gè)abstract方法,
那么實(shí)現(xiàn)該接口的類都需要作出修改。但是,程序設(shè)計(jì)好后,應(yīng)當(dāng)對(duì)增加實(shí)現(xiàn)接口的類“開放”,即在程序中再增加實(shí)現(xiàn)
接口的類時(shí),不需要修改其他重要的類。
個(gè)人的一點(diǎn)小薄見:
面向抽象編程和面向接口編程的思路都是一樣的,面向抽象編程依靠上轉(zhuǎn)型對(duì)象來實(shí)現(xiàn);面向接口編程依靠接口回調(diào)
來實(shí)現(xiàn);這種思想對(duì)于軟件設(shè)計(jì)十分重要。Java中的一大法寶是多態(tài),多態(tài)分兩種,其中一種就是通過繼承來實(shí)現(xiàn)的,子
類通過定義自己的行為來“展示自己”,每個(gè)子類都有不同的行為,所以展現(xiàn)出多態(tài)性。而我們可以建立一個(gè)類,這個(gè)類
可以幫助“每一個(gè)子類”來展現(xiàn)他們自己,而不用他們自己親自動(dòng)手,這會(huì)大大縮減程序的代碼長度,假如有100個(gè)子類,
如果要求每一個(gè)子類都親自展現(xiàn)自己,必須定義100段不同的代碼;而通過一個(gè)類輪流為它們服務(wù),這會(huì)顯得更方便,這
楊的一個(gè)幫助子類展現(xiàn)自己的類被稱為面向抽象的類(接口也一樣),面向抽象的類為子類實(shí)例的上轉(zhuǎn)型對(duì)象,通過調(diào)用
子類重寫的方法來實(shí)現(xiàn)多態(tài)。
19:17:05
2018-07-07
總結(jié)
以上是生活随笔為你收集整理的Java 面向抽象编程和面向接口编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java基础求三角形的面积
- 下一篇: Java 文件字符输入流FileRead