interface Runner {
public function run();
}
class Dog implements Runner {
public function run() {
return 'go';
}
}
class Airplane implements Runner {
public function run() {
return 'go';
}
}
Dog 類與 Airplane 類都實現了 Runner 介面,
但重複定義了相同的 run 方法。
請問:
在無法讓 Dog 類與 Airplane 類繼承同一個父類的情況下,
要如何只定義一次 run 方法呢?
無法讓狗和飛機都implement...........
現實來說,你可以告訴我狗和飛機的來源特性嗎?
狗可以繼承自animal
飛機你要他繼承什麼???頂多就是繼承飛行器的公用介面吧。
這二個不同的東西為什麼要implement同一個介面?
雖然名稱同樣是run,但是在原多型的概念上父類別就已經是大不同的東西了。
狗跑是用四隻腳
飛機跑是用輪子………你最多可以考慮看看飛機能不能繼承車子…………
不要隨意亂訂亂使用介面。
什麼是協定?能統一遵守的才是協定。
無法遵守的東西就不應該混在一起。
如果你真的非要這樣幹……………
那你考慮看看trait吧!
雖然我個人極度不建議亂使用這些東西。
一個用的不好,別說別人不好維護,自己也難以維護。
Samと可樂快跑大大:
狗和飛機是我用來舉例的,
不是實際要寫的類別,
沒說明清楚,不好意思。
主要是想問:
如果二個完全不相關的類別,
無法讓他們繼承同一個父類別,
但他們其中有一個方法是相同的,
在不能用繼承的情況下,
如果用介面,
要如何讓方法只定義一次,
而不要重複定義。
不相關是毫不相關?還是源自多型後的不相關?
如果是毫不相關,那就各自定義各自的。
而不需要考慮是否重複定義的問題。
因為你讓二個不相關的東西使用同樣的來源,那就是「重度耦合」。
一但因為某個需求要改功能,而遭到其他人誤改源頭的話。
非死即…………滅(連傷都沒機會)
再來,如果是有特殊共同使用的功能。
你應該做的是獨立那個功能,可以接受參數或其他形式來回應你所需要的結果。
也就是封裝成其他的單獨函式或獨立類別來使用。
但這邊要注意,獨立出來的功能就是自成一個系統的服務。
通常除了原開發人員,任何其他人都不得在未經允許的情形下擅自改動。
這是基於開放封閉原則。
Samと可樂快跑大大:
是毫不相關的類別。
因為常看到 Do not repeat youself 這句話,
所以會想說是不是只要相同的程式碼就要獨立出來,
謝謝您詳細的回覆,
這樣我了解了。
相同的程式碼的確是要獨立出來。
但以你的情況,你所謂的相同的程式碼只是屬於另一個「系統」或「類別」所提供的方法。
這個方法必不致因本身類別本身的性質產生不可預期的狀態干擾。
function isRun() {
return 'go';
}
interface Runner {
public function run();
}
class Dog implements Runner {
public function run() {
return isRun();
}
}
class Airplane implements Runner {
public function run() {
return isRun();
}
}
Samと可樂快跑大大:
對於您說的 "獨立出來特殊共同使用的功能"
是否類似這樣?
如果 Dog 類的 run 方法更改就不會影響到 Airplane 類
對一半
如果你這樣只有return的話………寫run就沒意義了。
<?php
abstract class RunInfoProtocol {
function __construct() {
$this->object;
$this->speed;
$this->fuelType;
$this->runningPattern;
}
function __get($key) {
if ( !isset(static::$$key))
{
throw new Exception('Child class '.get_called_class().' failed to define static `'.$key.'` property');
}
return static::$$key;
}
}
class RunInfo {
function output(RunInfoProtocol $delegate) {
return "
<p>類別名稱:{$delegate->object}</p>
<p>極速:{$delegate->speed}公里/時</p>
<p>燃料型態:{$delegate->fuelType}</p>
<p>跑動型態:{$delegate->runningPattern}</p>
";
}
}
class Dog extends RunInfoProtocol{
public $object = '狗';
public $speed = 50;
public $fuelType = '吃飯';
public $runningPattern = '用四隻腳跑';
function run(RunInfo $runInfo) {
return $runInfo->output($this);
}
}
class Airplain extends RunInfoProtocol{
public $object = '飛機';
public $speed = 800;
public $fuelType = '加航空燃油';
public $runningPattern = '用輪胎跑';
function run(RunInfo $runInfo) {
return $runInfo->output($this);
}
}
$dog = new Dog();
echo $dog->run(new RunInfo);
$airplane = new Airplain();
echo $airplane->run(new RunInfo);
這算是一個概念性的sample
然後因為我寫了太久的objective c和swift所以忘了php的interface是不能設置屬性的。
所以我才會在虛類父類別中先自己呼叫屬性的方式來偵測子類別的屬性是否有宣告。
在這別,這個extends虛擬類別,不要當成是「繼承」。
我這是沒辦法中的辦法。(php特性)
所以類似interface但又不能implement的話。
名稱我就會定義成Protocol(協定)
簡單來說,如果你的run要執行的東西必須改變時。
你只要改動載入run的功能性類別,就可以抽換方法了。
Samと可樂快跑大大:
謝謝您舉的這個例子,
很清楚詳細。
尤其您在 run 方法內把 $this 傳給 RunInfo 類,
這招好用ㄝ,
之前沒想到可以這麼做,
思考類別的互動時又多了一個新觀念,
對我幫助很大,
謝謝您。
用event
https://laravel.com/docs/6.x/events
RunnerRunEvent這樣
但其實你的例子來看 兩個run差別很大就乖乖定義兩次
一樣就繼承就好