1. 程式人生 > >面向對象編程之開放閉合原理的

面向對象編程之開放閉合原理的

經典 表白 () event hide 覆寫 ide 發現 修改

  開放封閉原則(OCP,Open Closed Principle)是所有面向對象原則的核心。軟件設計本身所追求的目標就是封裝變化、降低耦合,而開放封閉原則正是對這一目標的最直接體現。

  關於開放封閉原則,其核心的思想是:   軟件實體應該是可擴展,而不可修改的。也就是說,對擴展是開放的,而對修改是封閉的。   因此,開放封閉原則主要體現在兩個方面:   對擴展開放,意味著有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。   對修改封閉,意味著類一旦設計完成,就可以獨立完成其工作,而不要對類進行任何修改。   “需求總是變化”、“世界上沒有一個軟件是不變的”,這些言論是對軟件需求最經典的表白。從中透射出一個關鍵的意思就是,對於軟件設計者來說,必須在不需要對原有的系統進行修改的情況下,實現靈活的系統擴展
。而如何能做到這一點呢?   只有依賴於抽象實現開放封閉的核心思想就是對抽象編程,而不對具體編程,因為抽象相對穩定。讓類依賴於固定的抽象,所以對修改就是封閉的;而通過面向對象的繼承和對多態機制,可以實現對抽象體的繼承,通過覆寫其方法來改變固有行為,實現新的擴展方法,所以對於擴展就是開放的。      舉個栗子:   技術分享   1.銀行業務辦理    A.沒有實現開放閉合原理的方式:      技術分享
public class BankProcess
    {
        public void Deposite(){}   //存款
        public void Withdraw(){}   //
取款 public void Transfer(){} //轉賬 } public class BankStaff { private BankProcess bankpro = new BankProcess(); public void BankHandle(Client client) { switch (client .Type) { case "deposite": //存款 bankpro.Deposite();
break; case "withdraw": //取款 bankpro.Withdraw(); break; case "transfer": //轉賬 bankpro.Transfer(); break; } } }
View Code

    目前設計中就只有存款,取款和轉賬三個功能,將來如果業務增加了,比如增加申購基金功能,理財功能等,就必須要修改BankProcess業務類。我們分析上述設計就能發現不能把業務封裝在一個類裏面,違反單一職責原則,而有新的需求發生,必須修改現有代碼則違反了開放封閉原則。

    那麽,如何使代碼耦合度更低?而不是牽一發兒動全身,前輩們已經給我們趟出了一些路子:將業務功能抽象為接口,當業務員依賴於固定的抽象時,對修改就是封閉的,而通過繼承和多態繼承,從抽象體中擴展出新的實現,就是對擴展的開放。

   B.實現了開放閉合原理的方式:

技術分享
    public interface IBankProcess //首先聲明一個業務處理接口
    {
        void Process();
    }
    public class DeposiProcess:IBankProcess
    {
        public void Process()         //辦理存款業務
        {
            Console.WriteLine("Process Deposit");
        }
    }
    public class WithDrawProcess:IBankProcess
    {
        public void Process()        //辦理取款業務
        {
            Console.WriteLine("Process WithDraw");
        }
    }
    public class TransferProcess:IBankProcess
    {
        public void Process()        //辦理轉賬業務
        {
            Console .WriteLine ("Process Transfer");
        }
    }
    public class BankStaff
    {
        private IBankProcess  bankpro = null ;
        public void BankHandle(Client client)
        {
            switch (client .Type)
            {
                case "Deposite":      //存款
                    userProc =new WithDrawUser();
                    break;
                case "WithDraw":      //取款
                    userProc =new WithDrawUser();
                    break;
                case "Transfer":      //轉賬
                    userProc =new WithDrawUser();
                    break;
            }
            userProc.Process();
        }
    }
View Code

  銀行工作人員:

技術分享
class BankStaff
{
private IBankProcess bankProc = null;
public void HandleProcess(Client client)
{
bankProc = client.CreateProcess();
bankProc.Process();
}
}
View Code

  客戶:

技術分享
class Client
{
private string ClientType;
public Client(string clientType)
{
ClientType = clientType;
}
public IBankProcess CreateProcess()
{
switch (ClientType)
{
case "存款用戶":
return new DepositProcess();
break;
case "轉賬用戶":
return new TransferProcess();
break;
case "取款用戶":
return new DrawMoneyProcess();
break;
}
return null;
}
}
View Code

  我們辦理業務的時候:

技術分享
class BankProcess
{
public static void Main()
{
EasyBankStaff bankStaff = new BankStaff();
bankStaff.HandleProcess(new Client("轉賬用戶"));
}
}
View Code

  當有新的業務增加時,銀行經理不必為重新組織業務流程而擔憂,你只需為新增的業務實現IBankProcess接口:

技術分享
class FundProcess : IBankProcess
{
//IBankProcess Members
#region IBankProcess Members
public void Process()
{
// 辦理基金業務
throw new Exception("The method or operation is not implemented.");
}
#endregion
}
View Code

  新的設計遵守了開放封閉原則,在需求增加時只需要向系統中加入新的功能實現類,而原有的一切保持封閉不變的狀態,這就是基於抽象機制而實現的開放封閉式設計。

技術分享

  2.系統配置文件讀取

  配置文件有多重文件格式:php,ini,json,xml等

  我們的原則:封裝變化,對擴展開放,對修改閉合

  首先,增加抽象接口:

技術分享
<?php  
interface Configuration{  
    public function toArray($configFilePath);  
}  
?>
View Code

  然後,具體實現類繼承接口:

  phpConfiguration.php

技術分享
<?php  
require_once "configuration.php";  
class phpConfiguration implements Configuration{  
    public function toArray($configFilePath){  
        $config = require_once $configFilePath;  
        return $config;  
    }  
}  
?>
View Code

  jsonConfiguration.php

技術分享
<?php  
require_once "configuration.php";  
class JsonConfiguration implements Configuration{  
    public function toArray($configFilePath){  
        return json_decode(file_get_contents($configFilePath), true);  
    }  
}  
?>
View Code

  給出config.php配置工具類:

技術分享
<?php
require_once "phpConfiguration.php";
class config{
    var $configFilePath;
    //定義一個構造方法初始化賦值
    function __construct($configFilePath) {
        $this->configFilePath=$configFilePath;
    }
    public function configToArray($configuration){
        $result =$configuration->toArray($this->configFilePath);
        $config = is_array($result) ? $result : array();
        return $config;
    }

}
?>
View Code

完整例子下載:配置文件開放閉合原則實例

技術分享

  3.媒體播放器實例

  以電腦中的多媒體播放軟件為例,作為一款播放器,應該具有一些基本的、通用的功能,如打開多媒體文件、快進、音量調劑等功能。不論在什麽平臺下,遵循這個原則設計的播放器都應該有統一的操作規劃和操作習慣,都應該保證操作者能夠很快上手。

  首先,定義一個抽象業務接口:

技術分享
<?php
interface process  
{  
      public function process();  
}  
?>
View Code

  然後,對此接口進行拓展,實現解碼和輸出的功能:

技術分享
<?php
class playerencode implements process   
{  
      public function process()  
      {  
            echo "encode\r\n";  
      }  
}  
  
class playeroutput implements process   
{  
      public function process()  
      {  
            echo "output";  
      }  
}  
?>
View Code

  接下來定義播放器的線程調度處理器:

技術分享
class playProcess  
{  
      private $message = null;  
      public function __construct()  
      {  
  
      }  
      public function callback(event $event) {  
            $this->message = $event->click();  
            if($this->message instanceof process) {  
                  $this->message->process();  
            }  
      }  
}  
View Code

  然後,在定義一個mp4類,這個類相對是封閉的,其中定義時間的處理邏輯。

技術分享
class Mp4  
{  
      public function work() {  
            $playProcess = new playProcess();  
            $playProcess->callback(new event(‘encode‘));  
            $playProcess->callback(new event(‘output‘));  
      }  
}  
View Code

  最後,增加一個事件分揀的處理類,此類負責對事件進行分揀,判斷用戶或內部行為,供播放器的線程調度器調度。

技術分享
class event  
{  
      private $m;  
      public function __construct($me)  
      {  
            $this->m = $me;  
      }  
        
      public function click() {  
            switch ($this->m) {  
                  case ‘encode‘:  
                        return new playerencode();  
                  break;  
                  case ‘output‘:  
                        return new playeroutput();  
                  break;  
            }  
      }  
}  
View Code

完整例子下載:播放器開放閉合原則實例

三個栗子,應該大概能理解開放閉合原理了。

總結一下

實現開發-封閉原則的思想就是對抽象編程,而不是具體編程,因為抽象相對穩定,讓類依賴於固定的抽象,這樣的修改時封閉的;而通過對象的繼承和多態機制,可以實現對抽象類的繼承,通過覆蓋其方法來修改固有行為,實現新的拓展方法,所以對於拓展就是開放的。

(1)在設計方面充分利用“抽象”和“封裝”的思想

一方面也就是在軟件系統中找到各種可能的“可變因素”,並將之封裝起來

另一方面,一種可變因素不應當散落在不同代碼模塊中,而應當被封裝到一個對象中。

(2)在系統功能編程實現方面利用面向接口的編程

當需求發生變化時,可以提供接口新的實現類,以求適應變化

面向接口編程要求功能類實現接口,對象聲明為借口類型。

參考文獻:

https://yq.aliyun.com/articles/45638 設計模式六大原則——開放封閉原則(OCP)

http://blog.csdn.net/u011250882/article/details/47358519 設計原則之開放閉合原則(OCP)

http://blog.csdn.net/dnidong/article/details/57401935 php面向對象的設計原則之開發-封閉原則(OCP)

面向對象編程之開放閉合原理的