淺談多型以及php的實現方法
先簡單說一下多型
多型的三大特徵:
- 子類繼承父類
- 子類重寫父類
- 父類指向子類
多型實現的前提:必須是類與類之間要有關係,要麼繼承,要麼實現,存在重寫(override),其實就是抽象函式或介面。
多型的應用:父類物件的引用指向子類物件,其實本質上就是一個向上轉型。
舉個模型例子,一家公司有員工類(Employee),還有其子類:銷售(Sales)、市場(Market)、工程師(Engineer)等。某一天老闆招待所有的員工開了個短會,完了之後對所有的員工(Employee)說,大家回去工作吧。在這裡我們可以認為老闆呼叫了所有員工物件的continueToWork()方法,而不是對一個個員工細說做什麼工作,比如對銷售部說你去制定銷售計劃(呼叫makeSalePlan();),對市場部說你去制定產品的價格(呼叫makeProductPrice();)….這種逐個細說的方式並不是面向物件,而是面向個體。可以確定的是,員工類應該有一個方法continueToWork()。而員工如何實現他們工作的方法卻只有精確到子類才能確定,因為不同的員工的工作方式是不一樣的。因此,我們很多時候只需要關心物件的父型別,而忽略更精確的子型別,比如上面老闆叫大家回去工作時,他對全體員工說的,主要指的是全體員工型別。
上述的UML圖:
多型的好處:大大提高程式的擴充套件,增強系統的靈活性,降低模組間的耦合。
具體Java程式碼實現如下:
abstract class Employee{
abstract void continueToWork();
}
class Sales extends Employee{
private void makeSalePlan(){
System.out.println("make sale plan");
}
public void continueToWork(){
makeSalePlan();
}
}
class Market extends Employee{
private void makeProductPrice(){
System.out.println("make product price");
}
public void continueToWork(){
makeProductPrice();
}
}
class Engineer extends Employee{
private void makeNewProduct(){
System.out.println("make new product" );
}
public void continueToWork(){
makeNewProduct();
}
}
錯誤的呼叫示範:
class Demo{
public static void main(String[] args){
Sales s = new Sales();
s.continueToWork();
Market m = new Market();
m.continueToWork();
Engineer e = new Engineer();
e.continueToWork();
}
}
以上的這種呼叫並不能稱為多型,雖然看起來都通過呼叫continueToWork()來得到結果,但是並不是通過同一介面來實現的,這只是不同類的物件都有相同名稱的介面方法,用上述模型來說,就是老闆單獨對銷售部說你去工作,單獨對市場部說你去工作,單獨對工程師說你去工作,而不是對所有的員工說。
正確的呼叫方式:
class Demo{
public static void main(String[] args){
Work(new Sales());//Employee e = new Sales();
Work(new Market());//Employee e = new Market();
Work(new Engineer());//Employee e = new Engineer();
}
public static void Work(Employee e){
e.continueToWork();
}
}
上面通過呼叫統一的介面Work()來工作,這種呼叫才是多型。
還可以利用過載來實現偽多型:(注:本作者認為過載不屬於多型的範疇,但是過載這種語言特性是可以幫助我們來實現偽多型的,具體請見本人的另一篇博文:《過載不應歸在多型的範疇內》)
class Demo{
public static void main(String[] args){
Work(new Sales());
Work(new Market());
Work(new Engineer());
}
public static void Work(Sales s){
s.continueToWork();
}
public static void Work(Market m){
m.continueToWork();
}
public static void Work(Engineer e){
e.continueToWork();
}
}
為什麼我稱上面呼叫是偽多型呢?因為上述的這種方式,細究其實跟第一種錯誤呼叫方式是一樣的,只不過利用了過載加以封裝,使得看起來是通過統一的介面Work()來工作,但其實它並沒有讓父類物件的引用指向子類物件。
最後附上php程式碼實現多型:
<?php
abstract class Employee{
abstract function continueToWork();
}
class Sales extends Employee{
private function makeSalePlan(){
echo "make sale plan";
}
public function continueToWork(){
$this->makeSalePlan();
}
}
class Market extends Employee{
private function makeProductPrice(){
echo "make product price";
}
public function continueToWork(){
$this->makeProductPrice();
}
}
class Engineer extends Employee{
private function makeNewProduct(){
echo "make new product";
}
public function continueToWork(){
$this->makeNewProduct();
}
}
class Demo{
public function Work($employeeObj){
$employeeObj->continueToWork();
}
}
//呼叫
$obj = new Demo();
$obj->Work(new Sales());
$obj->Work(new Market());
$obj->Work(new Engineer());
?>