2013-08-21 194 views
19

好吧,這真的讓我感到困擾,我開始認爲這一切都歸結爲個人選擇,而不是一種更高效或編寫更好代碼的特定方式:我應該還是不應該使用getter/setter方法PHP項目?到目前爲止,我讀過的答案相當矛盾,並不完全適用於PHP,它不是一種編譯語言。例如,在Stack Overflow上採取這個問題(「Why use getters and setters?」)。關於爲什麼我應該在我的代碼中使用它們,有很多很好的理由,但是所有孩子的評論都提到了如何避免,並且在幾個更令人不安的評論之間穿插,這些評論提到應該完全避免它們,因爲它「你的代碼「。我應該還是不應該使用getter和setter方法?

我得到的所有答案都是相互矛盾的,其中沒有一個與解釋的PHP環境相關。是否有人能夠明白爲什麼/爲什麼不應該在PHP中使用它們,以及它們背後的理由?是否真的重要,如果我們可以簡單地定義一個屬性爲私有或保護,而且反正:

封裝getter和setter報價是可笑的薄

...從「SBI」援引(爲什麼?使用getter和setter)

就個人而言,我還沒有看到如何:

Class Foo { 
    public $bar; 

    function setBarType($val) { 
     $this->bar = $val; 
    } 
} 

$fee = new Foo(); 
$fee->setBarType(42); 

優於這樣的:

Class Foo { 
    public $bar; 
} 

$fee = new Foo(); 
$fee->bar = 42; 
+4

你有可能分配前檢查'$ val',如果出現錯誤拋出異常,觸發用戶錯誤或只返回二傳手(第一個建議)在您的第一個例子(實際上,這是封裝的點)。 – Leri

+0

@Leri,這實際上很有道理。但這是否意味着我必須爲類中的每個屬性獲取/設置方法,如果我有50個不同的屬性會發生什麼 - 每個屬性都有一個getter/setter? – ReactingToAngularVues

+1

具有50種不同屬性的類最可能違反SRP。無論如何,這是開發者的選擇。例如,我從不使用DTO的getters/setter,而將它們用於DomainObjects,因爲我僅將DTO用於映射數據(主要是反射),而在DomainObjects中,我需要確保具有_correct_數據。 – Leri

回答

11

因爲如果執行的值設置如何更改(對於數據庫),則不必更改調用者。另一個例子是你可能需要在設置之前對值進行一些檢查。

有一種方法可以讓你攔截變量的設置/獲取,當它看起來不像你需要的時候這樣做會讓你的代碼更友好。

屬性獲取

語言,如C#和JavaScript的最新版本允許你攔截財產閱讀和寫作,所以你可以在支持它的語言使用性能。

對象觀察家

有些語言讓你攔截所有JavaScript's Object.watchPHP's __get讀/設置,或無法訪問性能。這使您可以實現getter和setter,但是由於它們爲每個屬性訪問創建的開銷,因此您會受到性能影響。這個答案談到了獲取者和制定者的其他問題。 Best practice: PHP Magic Methods __set and __get

getter和setter都OK,但...

只是讓樣板getter和setter方法較好,但幾乎是一樣糟糕的公共屬性。如果任何人都可以改變你的對象的狀態(特別是具有多個屬性),它將不會很好地封裝。 http://cspray.github.io/2012/05/13/stop-calling-them-getters-setters.html

+0

好的,但是說我從數據庫中獲取大量信息,可能是10個不同的信息變量,是否必須爲每個*變量設置getter/setter方法,還是隻有從db傳遞的數組?因爲如果是前者,我會發現代碼變得非常複雜,速度非常快。 – ReactingToAngularVues

+0

這取決於你,這超出了這個問題的範圍,我不能評論一個假設。另外stackoverflow.com不是這些問題的地方。程序員交換的是更多的開放式問題,如 –

+0

你也可以用PHP截取屬性讀寫:http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading。成員 – Marek

3

該選擇取決於您。考慮PHP 5的其它重要的特點是魔術的getter/setter方法:

http://www.php.net/manual/en/language.oop5.overloading.php#object.set http://www.php.net/manual/en/language.oop5.overloading.php#object.get

的這個神奇的是,你可以這樣做以下,並決定從何處獲取/設置變量,而不必事先聲明它們:

Class Foo { 
    private $data = array(); 

    function __set($name,$val) { 
     $this->$data[$name] = $val; 
    } 
    function __get($name,$val) { 
     if (array_key_exists($name, $this->data)) { 
     return $this->data[$name]; 
    } 
} 

瞧,這樣的事情現在怎麼自動地:

$費=新的Foo(); $ fee-> bar = 42;

+2

魔法屬性真的很慢,如果可以的話,避免它們 –

3

如果您不使用像PHP這樣的語言的getter和setter,它不是類型安全的,那麼您將如何確保對象屬性的類型是正確的?

我可能會完全錯過了點,但如果你需要訪問從對象之外的財產,我認爲它仍然使用存取的最佳選擇...

此外,作爲咒怨門德斯提到:當屬性本身被解決時,有些語言會提供你攔截。 This may also be coming to PHP

你甚至可以有一個公共的二傳手,和一個受保護的吸氣劑:

class TimePeriod { 
    protected $Seconds = 3600; 

    public $Hours { 
     get { return $this->Seconds/3600; } 
     set { $this->Seconds = $value * 3600; } 
    } 

    // This property is read only as there is no setter 
    public $Minutes { 
     get { return $this->Seconds/60; } 
    } 

    /* public getter, protected setter */ 
    public $Milliseconds { 
     get { return $this->Seconds * 1000; } 
     protected set { $this->Seconds = $value/1000; } 
    } 
} 
4

一個偉大的利潤使用getter和setter的是,當你需要做出改變,你只需要修改getter和二傳手。

我會嘗試用一個例子來解釋:

想象是有原因的日期應始終爲一天增加。你將不得不在整個項目中搜索你的班級成員。

但是,通過使用getter和setter你可以將代碼更改爲:

protected $date; 
public function getDate(){ 
    return $this->date; 
} 
public function setDate($date){ 
    $date = new DateTime($date); 
    $date->modify('+1 day'); 
    $this->date = $date; 
} 
2

IMO使用getter和setter方法是一個很好的做法,提高了代碼的可讀性了。 除了在設置時檢查數值之外,還有另外一個例子,考慮你是否撥打getMoviesList()。現在這個get方法可以被實現,它可以從本地數據庫獲取電影列表,從在線Web服務獲取電影列表,等等......所以做出決定的代碼可能在這個函數中。無論你來自哪裏,你都不在乎這個地點,以及它是如何得到它的。 U只需即可獲得電影列表

5

一個OOP principe是封裝。一個類負責該類中包含的所有變量。

通過設置一個公共變量,你打破了這個principe。

通過創建一個訪問器(setter),您可以間接打破該原則,讓該類別以外的值更改。

getter的優點是調用方法不需要關心數據是如何檢索的。它只知道它會得到預期的數據,就這些。這裏的封裝原則受到尊重。

setter的優點是具有類應用前檢查新價值的機會。在一個完美的世界中,不需要訪問器,因爲一個類可以完全管理他所有的變量。在現實世界中,有時你需要設置一個價值或從外部獲取價值。

接近完美的世界,最好的辦法是accesors僅限制爲必須進行修改很少的變量。如果可能,請檢查設定值。

關於你的榜樣,因爲你保存在PHP堆棧一個級別的第二個解決方案是更好的。你的訪問器是無用的,因爲你的變量是公開的(所以沒有封裝)並且不執行任何額外的檢查。但是,如果可能的話,你應該使用訪問器來檢查數據。如果數據在應用程序的其他地方不需要,那麼不要執行任何訪問器。

+0

'僅限於極少數必須修改的變量「 但即使對於不會被修改的屬性,我也不需要使用無論如何都可以使用getter方法訪問對象中的每個屬性?從而口述至少1個吸氣方法到1個房產比率? – ReactingToAngularVues

+0

如果需要從外部訪問這些數據,那麼總是使用訪問者。調用代碼不必知道如何檢索數據。通過使用getter,您可以從數據庫,文件或其他內容中獲取數據。通過直接使用變量,您不能在沒有重大重構的情況下更改數據的檢索方式。 – Atrakeur

15

您鏈接到開始有一個關鍵句的博客文章(強調):

每一個getter和setter在你的代碼表示失敗封裝並創建不必要的耦合。

封裝是面向對象編程的最重要思想。它基本上歸結爲通過將其整齊地包裝到類中來隱藏複雜性。在一個理想的世界,當你使用一個類時,你不應該要知道什麼任何有關其內部運作或它的狀態。有些人(比如這位博客作者)會爭辯說,讓吸氣者和安裝者已經有了太多有關這個班級內部的信息。在他們看來,一個類只能有方法,使我們能夠指示對象做一些,別提如何做它還是什麼狀態時,它是,利用一個設置是不是「告訴對象做一些事情「,它從外面沾染着對象的狀態。

而不是做這個的:

$a = myObject(); 

// Querying object state, making a decision outside the object, and changing its state 
if ($a->getWarbleFizz() < 42) { 
    $a->setYourWarbleFizzTo(42); 
} 

// Gee, I hope I made the right decision... 
$a->nowDoSomethingUseful(); 

你應該寫這樣的代碼:

$a = myObject(42); 
$a->doStuff(); 

或者這樣:

$a = myObject(); 
$a->doStuff(42); 

相關閱讀:Tell, don't ask

相關問題