2009-04-30 64 views
15

我是一種新的PHP。出於某種原因,在其他類型的編程語言(如JAVA)中,對於每個變量使用setter和getter都沒有問題,但是當我使用PHP進行編程時,可能是因爲它非常靈活,感覺像是浪費時間。將類屬性設置爲大部分時間公開並且像這樣操縱它們感覺更簡單。問題是,當我這樣做時,我覺得我做錯了事,違反了面向對象的原則。真的不錯,不使用setters和getters?

是不是真的沒有使用setter和getters?爲什麼或者爲什麼不?你們大多數時候都會這樣做?

回答

21

不使用屬性訪問器的主要問題是,如果您發現以後需要將某個字段更改爲某個屬性 - 例如將其設置爲子類中的計算屬性,則您將打破客戶端你的API。對於已出版的圖書館來說,這是不可接受的;對於內部人員來說,修理東西的工作量相當大。

對於私人代碼或小應用程序,它可能是可行的。 IDE(或文本編輯器)可讓您生成訪問者樣板並使用代碼摺疊將其隱藏起來。這可以說使得getter和setter在機械上相當容易。

請注意,某些編程語言具有合成默認字段+ getter + setter的功能 - Ruby通過元編程實現它,C#具有自動實現的屬性。而Python通過讓你覆蓋屬性訪問完全迴避了這個問題,讓你可以將屬性封裝在需要它的子類中,而不必先打擾它。 (這是我最喜歡的方法。)

17

獲取者或設置者的意義在於,您仍然可以在一個位置添加邏輯來修改字段,而不是您想修改或檢索字段的每個位置。您還可以在課堂上掌握在該領域發生的事情。

3

您是否考慮過使用魔術函數__set/__ get?使用它們,你可以輕鬆地將所有的getter/setter函數合併到兩個函數中!

+5

整理。請記住,只有在您嘗試訪問無法訪問的屬性時纔會調用__set和__get。這意味着,如果你想使用__get和__set,你需要完全避免定義屬性,它伴隨着它自己的頭痛。 (當然,您不必完全避免定義屬性,但是如果您有混合定義的屬性和未定義的屬性,最終會產生混淆和模糊性,並且避免混淆和模糊性是我們應該通過「最佳實踐」 。 – 2009-04-30 19:01:12

+0

@Alan,我認爲你需要做的就是將屬性定義爲「protected」或「private」,這使得它們無法訪問。http://php.net/language.oop5.overloading – matpie 2009-05-01 21:19:45

+0

@sirlancelot,你可以將它們定義爲私有或受保護的,但它們仍然可以在* some範圍內直接訪問。根據setter/getter中邏輯的複雜性,這可能是一個真正的痛苦。 – 2009-06-18 18:51:00

1

如果你在腳本中訪問這些變量很多時間,並且如果你經常更新yoru類,你應該使用setter和getter,因爲如果你不這樣做,那麼當你提高你的類時你必須更新所有使用這個變量的文件。

第二個主要原因是你不應該直接訪問變量,因爲類結構可能會改變,這些數據可能會有所不同。當你從類中獲取數據時,你不應該關心如何生成這些數據。類必須關心這個數據處理,所以你只需要關心你會得到什麼。

7

我可能不會在這一個上得到很多upvotes,但親自獲得者和甚至更多的setter人感覺像一個代碼味道給我。設計應該是行爲驅動的,而不是數據驅動的。當然,這只是一個意見。如果有一個對象依賴於另一個對象的特定數據字段,則這是非常緊密的耦合。相反,它應該取決於那個比它的數據脆弱得多的對象的行爲。

但是,是的,像getter和setter這樣的屬性是直接依賴字段的一個步驟,因爲這個原因。它不那麼脆弱並且放鬆了物體之間的耦合。

2

有效仿的get/set實際上並沒有使用get/set函數類的方式,讓你的代碼保持整潔:

$person->name = 'bob'; 
echo $person->name; 

看看this class我已經編碼。

通常,使用此類時,您將聲明所有受保護屬性(或私有屬性)。如果您希望在屬性上添加行爲,請在「name」屬性上說strtolower()+ ucfirst(),您只需在類中聲明受保護的set_name()函數並行爲應該自動獲取。同樣可以用get_name()來完成。

// Somewhere in your class (that extends my class). 
protected function set_name($value) { $this->name = ucfirst(strtolower($value)); } 
// 

// Now it would store ucfirst(strtolower('bob')) automatically. 
$person->name = 'bob'; 

P.S. 另一個很酷的事情是,你可以彌補不存在的領域,如

echo $person->full_name; 

,而不必等領域(只要有一個get_full_name()函數)。

15

如果我們在這裏嚴格談論PHP而不是談論C#,Java等(編譯器會優化這些東西),我發現getters和setters是浪費資源的地方,你只需要代理值的私人領域,別無其他。

在我的設置,我做了兩個糟糕的類,一個有五個私有字段由代理所述場(這看起來幾乎正是像Java代碼,有趣的是)五個的getter/setter對和另一封裝五個公共領域,並在創建實例後在末尾調用memory_get_usage()。帶有getter/setter的腳本使用了59708字節的內存,腳本的公用字段使用了49244字節。

在任何重要大小的類庫(例如網站框架)中,這些無用的getter和setter可以爲內存添加一個巨大的黑洞。我一直在爲我的僱主開發一個PHP框架(他們的選擇,不是我的,如果我有選擇,但我說過,PHP不會對我們施加任何不可逾越的限制),當我重構類庫使用公有字段而不是getter/setters,整個shebang最終每個請求的內存使用量減少了25%。

__get(),__set()和__call()'magic'方法真的很適合處理界面變化。當您需要將字段遷移到getter/setter(或將getter/setter遷移到字段)時,他們可以使該過程對任何相關代碼都是透明的。使用解釋型語言,即使對Eclipse PDT或Netbeans提供的代碼敏感性提供了相當好的支持,也很難找到所有字段或方法的用法,所以這些神奇方法對於確保舊界面仍然可以委託給新功能。假設我們有一個使用字段而不是getter/setter開發的對象,並且我們想將名爲'field'的字段重命名爲'fieldWithBetterName',因爲'field'不合適,或者不再準確地描述使用,或者顯然是錯誤的。說我們想了一個名爲「域2」字段從數據庫中,因爲它沒有使用一個getter最初被更改爲延遲加載它的價值...

class Test extends Object { 
    public $field; 
    public $field2; 
} 

成爲

class Test extends Object { 
    public $fieldWithBetterName = "LA DI DA"; 
    private $_field2; 

    public function getField2() { 
     if ($this->_field2 == null) { 
      $this->_field2 = CrapDbLayer::getSomething($this->fieldWithBetterName); 
     } 
     return $this->_field2; 
    } 

    public function __get($name) { 
     if ($name == 'field')) { 
      Logger::log("use of deprecated property... blah blah blah\n".DebugUtils::printBacktrace()); 
      return $this->fieldWithBetterName; 
     } 
     elseif ($name == 'field2') { 
      Logger::log("use of deprecated property... blah blah blah\n".DebugUtils::printBacktrace()); 
      return $this->getField2(); 
     } 
     else return parent::__get($name); 
    } 
} 
$t = new Test; 
echo $t->field; 
echo $t->field2; 

(如一個側面說明,'擴展對象'位僅僅是一個基本類,我幾乎用於所有具有__get()和__set()聲明,當訪問未聲明字段時拋出異常的所有內容)

你可以倒退與__call()。這個例子是很脆弱的,但它並不難清理:

class Test extends Object { 
    public $field2; 

    public function __call($name, $args) { 
     if (strpos($name, 'get')===0) { 
      $field = lcfirst($name); // cheating, i know. php 5.3 or greater. not hard to do without it though. 
      return $this->$field; 
     } 
     parent::__call($name, $args); 
    } 
} 

Getter和在PHP setter方法是好的,如果二傳手必須做一些事情,或者如果吸氣有延遲加載的東西,或者保證的東西已經被創建,或者其他任何東西,但是如果他們除了代理這個領域什麼都不做,那麼他們是不必要的和浪費的,特別是使用像上面那樣的一些技術來管理接口變化。

相關問題