面向對象設計過程中重要的一點是如何進行抽象,即把“問題空間”中的元素與“方案空間”中的元素建立理想的一對一的映射關系。抽象類和接口便是抽象過程中的產物。
一、抽象類
我們知道,對象是通過類來產生的,但是並非所有的類都可以描述具體的對象。
如果一個類中不包含足夠的信息來描述具體的對象,就成為了抽象類。抽象類是對一類有著相同特征,但細節上卻有著不同表現的對象的抽象。比如,鳥類都會叫,但是不同的鳥叫法肯定是不同的,可以抽象出“叫”這個概念。
定義下面一個鳥類:
public abstract class Bird {
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Bird(){
}
public abstract void sing();//鳴叫
}
//喜鵲
class Magpie extends Bird{
public void sing() {
System.out.println("I can sing in a whisper. ");
}
}
抽象類特征:
(1)抽象類用abstract關鍵字修飾
(2)抽象類中的抽象方法用abstract關鍵字修飾,沒有方法體(具體實現)。
(3)抽象類可以包含非抽象方法
(4)抽象類可以不包含抽象方法(設計成抽象類就沒什麼意義了吧。。。),但包含抽象方法的類一定是抽象類
(5)抽象類本質也是類,只能單繼承
(6)抽象類不能實例化,不能new。我們前面說過它不描述具體的對象,肯定也不能實例化了
(7)抽象類可以有實例變量和構造方法
二、接口
接口是一組方法特征的集合,是契約,規定了你可以做什麼。軟件設計過程中要依賴抽象,而非具體實現。
像我們電腦上的usb接口,無論你是硬盤、u盤、還是手機,只要你實現了usb規定的接口,便能夠聯通電腦。
上面抽象類的例子中,我想加入一個“飛”的功能,考慮到並非所有的鳥都會飛,比如:企鵝,鴕鳥,鴨子等等,該怎麼辦呢?
修改抽象類肯定是不合適的:1、違反開閉原則,2:會使得所有的子類都繼承到“飛”這一功能,我們會看到滿天的鴨子在飛了^_^。
我們可以定義下面一個接口:
public interface IFly {
void fly();
}
//喜鵲可以實現IBird中的接口:
class Magpie extends Bird implements IFly {
public void sing() {
System.out.println("I can sing in a whisper. ");
}
public void fly(){
System.out.println("我會飛了!");
}
}
喜鵲實現了IFly中的fly接口,終於可以飛起來了。
那如果需要給鳥類增加一個游泳的功能呢?自己去想。。。
接口的特征:
(1)修飾符:public,abstract,default(不寫)
(2)關鍵字:interface
(3)接口中的方法都是抽象方法,不可以有實現。
(4)接口中的方法默認都是public abstract的,實現類中必須使用public修飾。
(5)接口中的所有方法都必須在實現類(抽象類除外)中實現。
(6)接口中的變量默認為public static final的。
(7)一個類可以實現多個接口。
三、應用場景
1)抽象類體現的是“is a”的關系,如果某一些類的實現有共通之處,則可以抽象出來一個抽象類,讓抽象類實現共通的代碼,而個性化的方法則由各個子類去實現。
2)接口體現的是“like a”的關系,表現的是不同類對象在行為上的抽象。比如飛機和鳥都會飛,可以抽離出飛的接口,但他們非同類。
3)在軟件設計中,當你需要接口與實現分離,封裝變化的時候,面向接口編程顯得尤為重要。
比如Ioc思想,客戶端不關心你具體是什麼類,具體對象由容器來注入。
再比如兩個系統交互,良好的設計是雙方提供接口,不關心內部實現,減少耦合性的同時,封裝了變化。
很多設計原則、設計思想以及設計模式都體現出面向接口編程的重要性:開閉原則,接口隔離,依賴倒置,適配器模式等等。
關注老姜談技術,微信號:helojava,或者掃描下面二維碼。
每日一帖,技術雞湯。