摘要:注本文內容來深入面向對象模式與實踐中節。面向對象設計與過程式編程面向對象設計和過程式編程有什么不同呢可能有些人認為最大的不同在于面向對象編程中包含對象。面向對象編程和過程式編程的一個核心區別是如何分配職責。
注:本文內容來<<深入PHP面向對象、模式與實踐>>中6.2節。
6.2 面向對象設計與過程式編程??面向對象設計和過程式編程有什么不同呢?可能有些人認為最大的不同在于面向對象編程中包含對象。事實上,這種說法不準確。在PHP中,你經常會發現過程式編程也使用對象,如使用一個數據庫類,也可能遇到類中包含過程式代碼的情況。類的出現并不能說明使用了面向對象設計。甚至對于Java這種強制把一切都包含在類中的語音(這個我可以證明,我在大三的時候學過Java),使用對象也不能說明使用了面向對象設計。
??面向對象編程和過程式編程的一個核心區別是如何分配職責。過程式編程表現為一系列命令和方法的連續調用??刂拼a根據不同的條件執行不同的職責。這種自頂向下的控制方式導致了重復和相互依賴的代碼遍布于整個項目。面向對象編程則將職責從客戶端代碼中移到專門的對象中,盡量減少相互依賴。
??為了說明以上幾點,我們分別使用面向對象和過程式代碼的方式來分析一個簡單的問題。假設我們要創建一個用于讀寫配置文件的工具。為了重點關注代碼的結構,示例中將忽略具體的功能實現。(文后有完整代碼示例,來自于圖靈社區)
??我們先按過程式方式來解決這個問題。首先,用下面的格式來讀寫文本:
key:value
只需要兩個函數:
function readParams( $sourceFile ) { $params = array(); // 從$sourceFile中讀取文本參數 return $params; } function writeParams( $params, $sourceFile ) { // 寫入文本參數到$sourceFile }
readParams()函數的參數為源文件的名稱。該函數試圖打開文件,讀取每一行內容并查找鍵/值對,然后用鍵/值對構建一個關聯數組。最后,該函數給控制代碼返回數組。writeParams()以關聯數組和指向源文件的路徑作為參數,它循環遍歷關聯數組,將每對鍵/值對寫入文件。下面是使用這兩個函數的客戶端代碼:
$file = "./param.txt"; $array["key1"] = "vall"; $array["key2"] = "val2"; $array["key3"] = "val3"; writeParams( $array, $file ); $output = readParams( $file ); print_r( $output );
這段代碼較為緊湊并且易于維護。writeParams()被調用來創建Param.txt并向其寫入如下的內容:
key1:val1
key2:val2
key3:val3
現在,我們被告知這個工具需要支持如下所示XML格式:
my key my val
??如果參數文件以.xml文件結尾,就應該以XML模式讀取參數文件。雖然這不難調節,但可能會使我們的代碼更難維護。這是我們有兩個選擇:可以在控制代碼中檢查文件擴展名,或者在讀寫函數中檢測。我們使用后面那種寫法。:
function readParams( $source ) { $params = array(); if ( preg_match( "/.xml$/i", $source ) ) { // 從$source中讀取XML參數 } else { // $source中讀取文本參數 } return $params; } function writeParams( $params, $source ) { if ( preg_match( "/.xml$/i", $source ) ) { // 寫入XML參數到$source } else { // 寫入文本參數到$source } }
??如上所示,我們在兩個函數中都要檢查XML擴展名,這樣的重復性代碼會產生問題。如果我們還被要求支持其他格式的參數,就要保持readParams()和writeParams()函數的一致性。
??下面我們用類來處理相同的問題。首先,創建一個抽象的基類來定義類型接口:
abstract class ParamHandler { protected $source; protected $params = array(); function __construct( $source ) { $this->source = $source; } function addParam( $key, $val ) { $this->params[$key] = $val; } function getAllParams() { return $this->params; } static function getInstance( $filename ) { if ( preg_match( "/.xml$/i", $filename )) { return new XmlParamHandler( $filename ); } return new TextParamHandler( $filename ); } abstract function write(); abstract function read(); }
??我們定義addParam()方法來允許用戶增加參數到protected屬性$params, getAllParams()則用于訪問該屬性,獲得$params的值。
??我們還創建了靜態的getInstance()方法來檢測文件擴展名,并根據文件擴展名返回特定的子類。最重要的是,我們定義了兩個抽象方法read()和write(),確保ParamHandler類的任何子類都支持這個接口。
??現在,我們定義了多個子類。為了實例簡潔,再次忽略實現細節:
class XmlParamHandler extends ParamHandler { function write() { // 寫入XML文件 // 使用$this->params } function read() { // 讀取XML文件內容 // 并賦值給$this->params } } class TextParamHandler extends ParamHandler { function write() { // 寫入文本文件 // 使用$this->params } function read() { // 讀取文本文件內容 // 并賦值給$this->params } }
??這些類簡單地提供了write()和read()方法的實現。每個類都將根據適當的文件格式進行讀寫??蛻舳舜a將完全自動地根據文件擴展名來寫入數據到文本和XML格式的文件:
$file = "./params.xml"; $test = ParamHandler::getInstance( $file ); $test->addParam("key1", "val1" ); $test->addParam("key2", "val2" ); $test->addParam("key3", "val3" ); $test->write(); // 寫入XML格式中
我們還可以從兩種文件格式中讀取:
$test = ParamHandler::getInstance( "./params.txt" ); $test->read(); // 從文本格式中讀取
那么,我們可以從這兩種解決方案中學習到什么呢?
職責??在過程式編程的例子中,控制代碼的職責(duties)是判斷文件格式,它判斷了兩次而不是一次。條件語句被綁定到函數中,但這僅是將判斷的流程影藏起來。對readParams()的調用和對writeParams()的調用必須發生在不同的地方,因此我們不得不在每個函數中重復檢測文件擴展名(或執行其他檢測操作)。
??在面向對象代碼中,我們在靜態方法getInstance()中進行文件格式的選擇,并且僅在getInstance()中檢測文件擴展名一次,就可以決定使用哪一個合適的子類。客戶端代碼并不負責實現讀寫功能。它不需要知道自己屬于哪個子類就可以使用給定的對象。它只需要知道自己在使用ParamHandler對象,并且ParamHandler對象支持write()和read()的方法。過程式代碼忙于處理細節,而面向對象代碼只需一個接口即可工作,并且不要考慮實現的細節。由于實現由對象負責,而不是由客戶端代碼負責,所以我們能夠很方便地增加對新格式的支持。
??內聚(cohesion)是一個模塊內部各成分之間相互關聯程度的度量。理想情況下,你應該使各個組件職責清晰、分工明確。如果代碼間的關聯范圍太廣,維護就會很困難--因為你需要在修改部分代碼的同時修改相關代碼。
??前面的ParamHandler類將相關的處理過程集中起來。用于處理XML的類方法間可以共享數據,并且一個類方法中的改變可以很容易地反映到另一個方法中(比如改變XML元素名)。因此我們可以說ParamHandler類是高度內聚的。
??另一方面,過程式的例子則把相關的過程分離開,導致處理XML的代碼在多個函數中同時出現。
??當系統各部分代碼緊密綁在一起時,就會產生精密耦合(coupling),這時在一個組件中的變化會迫使其他部件隨之改變。緊密耦合不是過程式代碼特有的,但是過程式代碼比較容易產生耦合問題。
??我們可以在過程代碼中看到耦合的產生。在writeParams()和readParams()函數中,使用了相同的文件擴展名測試來決定如何處理數據。因此我們要改下一個函數,就不得不同時改寫另一個函數。例如,我們要增加一種新的文件格式,就要在兩個函數中按相同的方式都加上相應的擴展名檢查代碼,這樣兩個函數才能保持一致。
??面向對象的示例中則將每個子類彼此分開,也將其余客戶端代碼分開。如果需要增加新的參數格式,只需簡單地創建相應的子類,并在父類的靜態方法getInstance()中增加一行文件檢測代碼即可。
??(orthogonality)指將職責相關的組件緊緊結合在一起,而與外部系統環境隔開,保持獨立。在<
??正交主張重用組件,期望不需要任何特殊配置就能把一個組件插入到新系統中。這樣的組件有明確的與環境無關的輸入和輸出。正交代碼使修改變得更簡單,因為修改一個實現只會影響到被改動的組件本身。最后,正交代碼更加安全。bug的影響只局限于它的作用域之中。內部高度相互依賴的代碼發生錯誤時,很容易在系統中引起連鎖反應。
??如果只有一個類,松散耦合和高聚合是無從談起的。畢竟,我們可以把整個過程示例的全部代碼塞到一個被誤導的類里。(這想想就挺可怕的。)
職責和耦合的英文翻譯原文是沒有的,我通過Goole翻譯加上的。代碼示例
過程式編程
param as $param ) { $params["$param->key"] = "$param->val"; } } else { $fh = fopen( $source, "r" ); while ( ! feof( $fh ) ) { $line = trim( fgets( $fh ) ); if ( ! preg_match( "/:/", $line ) ) { continue; } list( $key, $val ) = explode( ":", $line ); if ( ! empty( $key ) ) { $params[$key]=$val; } } fclose( $fh ); } return $params; } function writeParams( $params, $source ) { $fh = fopen( $source, "w" ); if ( preg_match( "/.xml$/i", $source )) { fputs( $fh, "" ); foreach ( $params as $key=>$val ) { fputs( $fh, " " ); fputs( $fh, " " ); } else { foreach ( $params as $key=>$val ) { fputs( $fh, "$key:$val " ); } } fclose( $fh ); }$key " ); fputs( $fh, "$val " ); fputs( $fh, " " ); } fputs( $fh, "
面向對象設計
source = $source; } function addParam( $key, $val ) { $this->params[$key] = $val; } function getAllParams() { return $this->params; } protected function openSource( $flag ) { $fh = @fopen( $this->source, $flag ); if ( empty( $fh ) ) { throw new Exception( "could not open: $this->source!" ); } return $fh; } static function getInstance( $filename ) { if ( preg_match( "/.xml$/i", $filename )) { return new XmlParamHandler( $filename ); } return new TextParamHandler( $filename ); } abstract function write(); abstract function read(); } class XmlParamHandler extends ParamHandler { function write() { $fh = $this->openSource("w"); fputs( $fh, "" ); foreach ( $this->params as $key=>$val ) { fputs( $fh, " " ); fputs( $fh, " " ); fclose( $fh ); return true; } function read() { $el = @simplexml_load_file( $this->source ); if ( empty( $el ) ) { throw new Exception( "could not parse $this->source" ); } foreach ( $el->param as $param ) { $this->params["$param->key"] = "$param->val"; } return true; } } class TextParamHandler extends ParamHandler { function write() { $fh = $this->openSource("w"); foreach ( $this->params as $key=>$val ) { fputs( $fh, "$key:$val " ); } fclose( $fh ); return true; } function read() { $lines = file( $this->source ); foreach ( $lines as $line ) { $line = trim( $line ); list( $key, $val ) = explode( ":", $line ); $this->params[$key]=$val; } return true; } } //$file = "./texttest.xml"; $file = "./texttest.txt"; $test = ParamHandler::getInstance( $file ); $test->addParam("key1", "val1" ); $test->addParam("key2", "val2" ); $test->addParam("key3", "val3" ); $test->write(); $test = ParamHandler::getInstance( $file ); $test->read(); $arr = $test->getAllParams(); print_r( $arr );$key " ); fputs( $fh, "$val " ); fputs( $fh, " " ); } fputs( $fh, "
本文為作者自己讀書總結的文章,由于作者的水平限制,難免會有錯誤,歡迎大家指正,感激不盡。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30162.html
摘要:閱讀小札一閱讀前自大學課上,就開始接觸設計模式,但對設計模式卻鮮有研究與實踐。第二部分是核心部分,由淺到深講解個設計模式。設計模式遵循的原則所有設計模式罪訓的一條原則就是找出程序中變化的地方,并將變化封裝起來。 閱讀小札 · 閱讀前 自大學Java課上,就開始接觸設計模式,但對設計模式卻鮮有研究與實踐。最近向公司反映和游說技術提升,得以獲得公司提供購書機會,借此認真學習前端學習之路的...
面向對象設計是一類編程方式,此編程方式的落地式需要使用類和目標來達到,因此,面向對象設計本身就是對類和目標的應用,今日給大家介紹一下python面向對象設計開發設計及本質特征,感興趣的小伙伴一起了解一下吧 序言 面向對象設計對新手而言不難理解但無法運用,盡管我們給大家匯總過面向對象戰略部署方式(定義類、創建對象、給目標發信息),可是看似簡單其實不簡單。大量程序編寫訓練與閱讀高質量的編碼有可...
摘要:為什么要采用面向對象編程解決問題更容易設計計算機程序就是為了解決人類的問題。面向對象編程需要對業務及代碼的架構是有一定的要求的。 1. 編程方式 我們目前的編程方式大體可以有以下三種編程方式: 順序編程 過程式編程 面向對象編程 在講面向對象編程時先講一下什么是順序編程,什么是過程式編程,什么是面向對象編程: 順序編程: 就是只用一個單線程去執行一段代碼,執行過程根據代碼依次從上...
小編寫這篇文章的主要目的,主要是來給大家介紹關于Python的一些事情,主要還是涉及到面對面對象編程的一些實例,其中,主要涉及到的內容涵蓋封裝、繼承、多態等多種形式,就具體的形式,下面就給大家詳細解答下。 Python是一門面向對象的語言。面向對象都有三大特性:封裝、繼承、多態?! ∠旅娣謩e來說說這三大特性: 1、封裝 隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。在python中用...
Python 是一種解釋型的、交互式的、面向對象的編程語言,它結合了非凡的功能和非常清晰的語法。Python Library Reference 記錄了內置的和標準的類型、常量、函數和模塊。語法格式:python [參數]常用參數:參數 描述-c 直接運行 python 語句-v 會輸出每一個模塊引用信息-i 運行完 python 腳本文件以后打開一個 python 環境-m 將模塊按照腳本執行命...
閱讀 683·2021-11-22 09:34
閱讀 3822·2021-09-22 15:42
閱讀 1327·2021-09-03 10:28
閱讀 1072·2021-08-26 14:13
閱讀 1901·2019-08-29 15:41
閱讀 1423·2019-08-29 14:12
閱讀 3364·2019-08-26 18:36
閱讀 3307·2019-08-26 13:47