網站製作學習誌

記錄學習製作網站的一切

關於PHP的多型

我在 PHP Twbb 台灣論壇資源網回答一個多型的問題:以下有三種不同的多型實作方式,有什麼不同呢?

我的見解如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<font color="#000000"><font color="#007700"><?</font><font color="#0000BB">php
</font><font color="#007700">class </font><font color="#0000BB">shape
</font><font color="#007700">{
function </font><font color="#0000BB">niceDrawing</font><font color="#007700">(</font><font color="#0000BB">$temp</font><font color="#007700">)
{
</font><font color="#0000BB">$temp</font><font color="#007700">-></font><font color="#0000BB">draw</font><font color="#007700">();
}
function </font><font color="#0000BB">draw</font><font color="#007700">()
{
</font><font color="#FF8000">//do nothing;
</font><font color="#007700">}
}
class </font><font color="#0000BB">Circle
</font><font color="#007700">{
function </font><font color="#0000BB">draw</font><font color="#007700">()
{
echo </font><font color="#DD0000">'circle'</font><font color="#007700">;
}
}
class </font><font color="#0000BB">Rectangle
</font><font color="#007700">{
function </font><font color="#0000BB">draw</font><font color="#007700">()
{
echo </font><font color="#DD0000">'rec'</font><font color="#007700">;
}
}
</font><font color="#0000BB">$board </font><font color="#007700">= new </font><font color="#0000BB">shape</font><font color="#007700">();
</font><font color="#0000BB">$obj </font><font color="#007700">= new </font><font color="#0000BB">Circle</font><font color="#007700">();
</font><font color="#0000BB">$obj2 </font><font color="#007700">= new </font><font color="#0000BB">Rectangle</font><font color="#007700">();
</font><font color="#0000BB">$board</font><font color="#007700">-></font><font color="#0000BB">niceDrawing</font><font color="#007700">(</font><font color="#0000BB">$obj</font><font color="#007700">); </font><font color="#FF8000">//will call the draw method of Circle.
</font><font color="#0000BB">$board</font><font color="#007700">-></font><font color="#0000BB">niceDrawing</font><font color="#007700">(</font><font color="#0000BB">$obj2</font><font color="#007700">); </font><font color="#FF8000">//will call the draw method of Rectangle.
</font><font color="#0000BB">?></font></font>

第 1 種方式沒有用到繼承,只是很單純的委託別的類別來處理事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<font color="#000000"><font color="#007700"><?</font><font color="#0000BB">php
</font><font color="#007700">class </font><font color="#0000BB">shape
</font><font color="#007700">{
var </font><font color="#0000BB">$temp</font><font color="#007700">;
function </font><font color="#0000BB">niceDrawing</font><font color="#007700">()
{
</font><font color="#0000BB">$this</font><font color="#007700">-></font><font color="#0000BB">draw</font><font color="#007700">();
}
function </font><font color="#0000BB">draw</font><font color="#007700">()
{
</font><font color="#FF8000">//do nothing;
</font><font color="#007700">}
}
class </font><font color="#0000BB">Circle
</font><font color="#007700">{
function </font><font color="#0000BB">Circle</font><font color="#007700">()
{
</font><font color="#0000BB">shape</font><font color="#007700">::</font><font color="#0000BB">niceDrawing</font><font color="#007700">();
}
function </font><font color="#0000BB">draw</font><font color="#007700">()
{
echo </font><font color="#DD0000">'circle'</font><font color="#007700">;
}
}
class </font><font color="#0000BB">Rectangle
</font><font color="#007700">{
function </font><font color="#0000BB">Rectangle</font><font color="#007700">()
{
</font><font color="#0000BB">shape</font><font color="#007700">::</font><font color="#0000BB">niceDrawing</font><font color="#007700">();
}
function </font><font color="#0000BB">draw</font><font color="#007700">()
{
echo </font><font color="#DD0000">'rec'</font><font color="#007700">;
}
}
</font><font color="#0000BB">$obj </font><font color="#007700">= new </font><font color="#0000BB">Circle</font><font color="#007700">();
</font><font color="#0000BB">$obj2 </font><font color="#007700">= new </font><font color="#0000BB">Rectangle</font><font color="#007700">();
</font><font color="#0000BB">?></font></font>

第 2 種方式也是一樣,只不過採用呼叫 shape 的靜態方法,使物件在初始化時就開始動作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<font color="#000000"><font color="#007700"><?</font><font color="#0000BB">php
</font><font color="#007700">class </font><font color="#0000BB">Message </font><font color="#007700">{
var </font><font color="#0000BB">$message</font><font color="#007700">;
function </font><font color="#0000BB">setMessage</font><font color="#007700">(</font><font color="#0000BB">$message</font><font color="#007700">)
{
</font><font color="#0000BB">$this</font><font color="#007700">-></font><font color="#0000BB">message </font><font color="#007700">= </font><font color="#0000BB">$message</font><font color="#007700">;
}
function </font><font color="#0000BB">getMessage</font><font color="#007700">()
{
return </font><font color="#0000BB">$this</font><font color="#007700">-></font><font color="#0000BB">message</font><font color="#007700">;
}
}
class </font><font color="#0000BB">PoliteMessage </font><font color="#007700">extends </font><font color="#0000BB">Message </font><font color="#007700">{
function </font><font color="#0000BB">PoliteMessage</font><font color="#007700">()
{
</font><font color="#0000BB">$this</font><font color="#007700">-></font><font color="#0000BB">setMessage</font><font color="#007700">(</font><font color="#DD0000">'How are you today?'</font><font color="#007700">);
}
}
class </font><font color="#0000BB">TerseMessage </font><font color="#007700">extends </font><font color="#0000BB">Message </font><font color="#007700">{
function </font><font color="#0000BB">TerseMessage</font><font color="#007700">()
{
</font><font color="#0000BB">$this</font><font color="#007700">-></font><font color="#0000BB">setMessage</font><font color="#007700">(</font><font color="#DD0000">'Howzit?'</font><font color="#007700">);
}
}
class </font><font color="#0000BB">RudeMessage </font><font color="#007700">extends </font><font color="#0000BB">Message </font><font color="#007700">{
function </font><font color="#0000BB">RudeMessage</font><font color="#007700">()
{
</font><font color="#0000BB">$this</font><font color="#007700">-></font><font color="#0000BB">setMessage</font><font color="#007700">(</font><font color="#DD0000">'You look like *%&* today!'</font><font color="#007700">);
}
}
class </font><font color="#0000BB">MessageReader </font><font color="#007700">{
var </font><font color="#0000BB">$messages</font><font color="#007700">;
function </font><font color="#0000BB">MessageReader</font><font color="#007700">(&</font><font color="#0000BB">$messages</font><font color="#007700">)
{
</font><font color="#0000BB">$this</font><font color="#007700">-></font><font color="#0000BB">messages </font><font color="#007700">= &</font><font color="#0000BB">$messages</font><font color="#007700">;
</font><font color="#0000BB">$this</font><font color="#007700">-></font><font color="#0000BB">readMessages</font><font color="#007700">();
}
function </font><font color="#0000BB">readMessages</font><font color="#007700">()
{
foreach (</font><font color="#0000BB">$this</font><font color="#007700">-></font><font color="#0000BB">messages </font><font color="#007700">as </font><font color="#0000BB">$message</font><font color="#007700">)
{
if (</font><font color="#0000BB">is_a</font><font color="#007700">(</font><font color="#0000BB">$message</font><font color="#007700">, </font><font color="#DD0000">'Message'</font><font color="#007700">))
echo </font><font color="#0000BB">$message</font><font color="#007700">-></font><font color="#0000BB">getMessage</font><font color="#007700">() . </font><font color="#DD0000">'<br />'</font><font color="#007700">;
}
}
}
</font><font color="#0000BB">$classNames </font><font color="#007700">= array(</font><font color="#DD0000">'PoliteMessage'</font><font color="#007700">, </font><font color="#DD0000">'TerseMessage'</font><font color="#007700">, </font><font color="#DD0000">'RudeMessage'</font><font color="#007700">);
</font><font color="#0000BB">$messages </font><font color="#007700">= array();
</font><font color="#0000BB">srand</font><font color="#007700">((float)</font><font color="#0000BB">microtime</font><font color="#007700">() * </font><font color="#0000BB">1000000</font><font color="#007700">); </font><font color="#FF8000">// Prepares random shuffle
</font><font color="#007700">for (</font><font color="#0000BB">$i </font><font color="#007700">= </font><font color="#0000BB">0</font><font color="#007700">; </font><font color="#0000BB">$i </font><font color="#007700">< </font><font color="#0000BB">10</font><font color="#007700">; </font><font color="#0000BB">$i</font><font color="#007700">++)
{
</font><font color="#0000BB">shuffle</font><font color="#007700">(</font><font color="#0000BB">$classNames</font><font color="#007700">);
</font><font color="#0000BB">$messages</font><font color="#007700">[] = new </font><font color="#0000BB">$classNames</font><font color="#007700">[</font><font color="#0000BB">0</font><font color="#007700">]();
}
</font><font color="#0000BB">$messageReader </font><font color="#007700">= new </font><font color="#0000BB">MessageReader</font><font color="#007700">(</font><font color="#0000BB">$messages</font><font color="#007700">);
</font><font color="#0000BB">?></font></font>

第 3 種方式用到了繼承,這是常見的多形技巧。 MessageReader 能接受一個 Message 類別所產生的物件,當然 Message 的子類別也同樣地能被 MessageReader 接受。對 MessageReader 來說, $messages 陣列裡所有的類別都繼承於 Message 類別,但因為它們在實作方法上有所不同,故在執行期間能夠產生不同的動作,所以能夠被稱之為多型。

第 3 種方式從比較廣義的角度來說,可以稱為「策略模式」。 MessageReader 在執行期間,不必在乎 $messages 陣列裡的物件的實作 (也就是演算法) ,只要這些物件屬於 Message 類別,那麼它就能呼叫 Message 類別中的 getMessage 方法。

不過實際上, PHP4 並沒有比較漂亮的辦法在傳入參數時就分別出 $messages 陣列中的物件是否真的屬於 Message 類別 (只能在函式中用 is_a ) ,在 PHP5 就能夠用 Type Hinting 來辨認。

我不一定是對的,所以歡迎大家提出不同的見解。

Comments