面向對象三大特性--封裝、繼承、多態

封裝

  • 簡單地說,封裝就是對客戶端代碼隱藏數據和功能。封裝也是面向對象的重要概念之一。
  • 要實現封裝,最簡單的辦法是將屬性定義爲privateprotected。通過對客戶端代碼隱藏屬性,我們創建了一個接口並防止在偶然情況下污染對象中的數據。
  • 多態是另外一種封裝。通過把不同的實現放在公共接口之後,我們對客戶端代碼隱藏了功的實現。也就是說,任何在接口背後發生的改變對外界的系統來說都是可忽略的。我們可以增加新類或改變類中的代碼,而不會產生錯誤。接口與其背後的工作機制是分開來的。這些機制越獨立,改進或修改代碼對系統的影響越小。
  • 從某種程度上來說,封裝是面向對象編程的關鍵。我們的目標是使系統中的每一部分都儘可能獨立。類和方法應當能夠接收到足夠的信息來執行它承擔的任務,而這些任務應該非常清晰,有明確的定義和範圍。
  • 在PHP5版本以上,我們可以在需要類選擇時打破常規,使用instanceof操作符來找出所用對象屬於哪一個具體的子類。

    function subjectWithStudy (Study $subject)
    {
        if ($subject instanceof PhpClass) {
            // php科目特有的操作
    
        } elseif ($subject instanceof JavaClass) {
            // java科目特有的操作
    
        }
    }

    • 這樣做通常有很好的理由,但通常來說會帶來一些不確定的因素。在代碼中查詢特定子類型時會產生一個依賴關係。如果我們利用多態來隱藏子類型的特性,Study 的繼承關係改變就不會產生任何錯誤。但上面的代碼會破壞這種情況。使用上面的代碼時,如果我們改寫了 PhpClassJavaClass 類,就有可能在 subjectWithStudy() 方法中產生預想不到的錯誤。

    這個例子說明了兩件事
    首先,封裝可以幫助我們創建正交(指非常獨立的,依賴性很小的)代碼。
    其次,封裝的範圍不怎麼重要,無論封裝的規模是大是小,類和客戶端代碼都必須同時關注封裝的實現。

繼承

  • 如果一個類A繼承自另一個類B,就把這個A稱爲“B的子類”,而把B稱爲“A的父類”。繼承可以使得子類具有父類的各種屬性和方法,而不需要再次編寫相同的代碼。在令子類繼承父類的同時,可以重新定義某些屬性,並重寫某些方法,即覆蓋父類的原有屬性和方法,使其獲得與父類不同的功能。另外,爲子類追加新的屬性和方法也是常見的做法。
class Shoes {
    private $name;
    public $type;

    protected function getShoeName($name)
    {
        $this->name = $name;
        $shoesName = '這雙鞋子的名稱叫' . $this->name;
        return $shoesName;
    }
}

class Nike extends Shoes {

    private $name = 'Nike';

    public function getName()
    {
        $res = $this->getShoeName($this->name);
        return $res;
    }
}
$nike = new Nike;
$resp = $nike->getName();
var_dump($resp);

response:這雙鞋子的名稱叫Nike

  • Nike類中的getShoeName是繼承了父類 Shoes 之後纔有的方法。

  • 繼承方式常使用如下三種關鍵字給予表示

    • public - 所有代碼都可訪問
    • private - 只有當前類可以訪問
    • protected - 只有當前類和它的子類可以訪問

多態

  • 多態或稱“類切換”是面向對象系統的基本特性之一。
  • 多態是指在一個公用接口後面維護多個實現。如果代碼中存在大量條件語句,就說明需要使用多態。
  • 多態的實現是離不開繼承的,沒有繼承就沒有多態。
/**
 * 定義抽象基類
 */
abstract class ParamHandler {
    protected $source;
    protected $params = array();

    function __construct($source)
    {
        $this->source = $source;
    }

    function addParam($key, $val)
    {
        $this->params[$key] = $val;
    }

    function getAllParasms()
    {
        return $this->params;
    }

    static function getInstance() {
        if (preg_match("/\.xml$/i", $filename)) {
            return new XmlParamHandler($filename);
        } else {
            return new TextParamHandler($filename);
        }
    }

    abstract function write();

    abstract function read();
}

/**
 * xml具體子類
 */
class XmlParamHandler extends ParamHandler {

    function read()
    {
        //讀取xml文件內容
    }

    function write()
    {
        //寫入xml文件
    }
}

/**
 * text具體子類
 */
class TextParamHandler extends ParamHandler {

    function read()
    {
        //讀取text文件內容
    }

    function write()
    {
        //寫入text文件
    }
}

$test = ParamHandler::getInstance($file);
$test->read(); //可能是XmlParamHandler::read()或TextParamHandler::read()
$test->addParam('subject', 'php');
$test->write(); //可能是XmlParamHandler::write()或TextParamHandler::write()
  • 要特別注意的是多態並沒有消除條件語句。像ParamHandler::getInstance這樣的方法經常要通過 switch 或 if 語句決定要返回的對象,但多態可以把條件代碼集中到一個地方
  • 就像我們看到的那樣,PHP強制接口由抽象類定義。這非常有用,因爲我們可以確定子類將會實現父類中定義的所有方法,包括類型提示和方法的訪控制。客戶端代碼因此可以使用一個公共父類的任意子類而不需要改寫代碼(只要客戶端代碼僅依賴於父類中定義的功能)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章