面向對象編程之開放閉合原理的
開放封閉原則(OCP,Open Closed Principle)是所有面向對象原則的核心。軟件設計本身所追求的目標就是封裝變化、降低耦合,而開放封閉原則正是對這一目標的最直接體現。
關於開放封閉原則,其核心的思想是: 軟件實體應該是可擴展,而不可修改的。也就是說,對擴展是開放的,而對修改是封閉的。 因此,開放封閉原則主要體現在兩個方面: 對擴展開放,意味著有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。 對修改封閉,意味著類一旦設計完成,就可以獨立完成其工作,而不要對類進行任何修改。 “需求總是變化”、“世界上沒有一個軟件是不變的”,這些言論是對軟件需求最經典的表白。從中透射出一個關鍵的意思就是,對於軟件設計者來說,必須在不需要對原有的系統進行修改的情況下,實現靈活的系統擴展public class BankProcess { public void Deposite(){} //存款 public void Withdraw(){} //View Code取款 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; } } }
目前設計中就只有存款,取款和轉賬三個功能,將來如果業務增加了,比如增加申購基金功能,理財功能等,就必須要修改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)
面向對象編程之開放閉合原理的