2010-03-22 392 views
21

按照PHP manual,一類是這樣的:通過抽象關鍵字在PHP中的靜態類?

abstract class Example {} 

不能被實例化。如果我需要沒有實例的課程,例如對於一個註冊表模式:

class Registry {} 
// and later: 
echo Registry::$someValue; 

它會被認爲是一種很好的風格,只需將該類聲明爲抽象類?如果不是,與抽象類相比,隱藏構造函數作爲受保護方法的優點是什麼?

問題依據:就我所見,它可能會有一些特性濫用,因爲手冊更像抽象類,對於稍後的類具有實例化可能性的藍圖。

更新:首先,感謝所有的答案!但許多答案聽起來很相似:'你不能實例化一個抽象類,但是對於註冊表,爲什麼不使用單例模式?'

不幸的是,這或多或少是我的問題的重複。什麼是優勢使用單身模式(也稱爲隱藏__construct())相比,只聲明它abstract,而不必擔心呢? (像,例如,它是開發商之間有很強的內涵,即abstract類實際上不是使用左右。)

+0

你能在我的帖子反映的一個,我想我已經提到了明顯的優勢。 – elias 2010-04-19 16:28:12

+0

感謝您的所有答案!不幸的是,獎金的結局在相當繁忙的時間中下降了,所以我無法對答案做出反應,因爲他們應該得到答案。我終於接受了帕斯卡爾的回答,因爲這是第一次,而且在他編輯之後似乎也是最全面和最完整的。然而,我讀了所有並且學到了很多東西。 – Boldewyn 2010-04-21 06:35:29

+2

我想知道是誰處理了所有的-1。謹慎解釋? – Boldewyn 2010-04-21 06:37:36

回答

19

如果你的類並不意味着定義一些超類型,它不應該被聲明爲abstract,我會說。

在你的情況,我寧願去帶班:

  • 定義__construct__clone爲私有方法
    • 因此類不能從外部
    實例化
  • 而且,這樣,你的班級可以創建自己的一個實例


現在,爲什麼要使用一個Singleton,不僅靜態方法?我想,至少有幾個原因可以是有效的:

  • 使用單例意味着使用類的一個實例;更容易將非單身人士班級轉換爲單身人士班級:只需使__construct__clone爲私人,並添加一些getInstance方法即可。
  • 使用單例還意味着您可以訪問您可以在普通實例中使用的所有內容:$this,屬性...
  • 哦,第三個(不知道這一點,但可能有它的重要性):用PHP < 5.3,你有一個靜態方法/數據不太可能:
  • Late Static Binding只被添加了PHP 5.3;並且在使用靜態方法/類時不經常使它更難;特別是在使用繼承時。


這是說,是的,一些這樣的代碼:

abstract class MyClass { 
    protected static $data; 
    public static function setA($a) { 
     self::$data['a'] = $a; 
    } 
    public static function getA() { 
     return self::$data['a']; 
    } 
} 

MyClass::setA(20); 
var_dump(MyClass::getA()); 

將工作...但它並不感到很自然的......,這是一個非常簡單的例子(請參閱我之前提到的使用Late Static Binding和魔術方法)

+2

那麼,這只是一種風格? – Boldewyn 2010-03-23 07:53:39

0

我wouldnt使用抽象類。像你所建議的那樣,使用更類似於帶有受保護/私有構造函數的單例的東西。除了$instance這個實際的註冊表實例外,應該只有很少的靜態屬性。最近香港專業教育學院成爲Zend的框架典型模式的粉絲是這樣的:

class MyRegistry { 

    protected static $instance = null; 

    public function __construct($options = null) 
    { 
    } 

    public static function setInstance(MyRegistry $instance) 
    { 
    self::$instance = $instance; 
    } 

    public static function getInstance() 
    { 
    if(null === self::$instance) { 
     self::$instance = new self; 
    } 

    return self::$instance; 
    } 
} 

這樣你得到一個單身,但基本上你可以注入一個配置實例來使用。這對於測試目的和繼承很方便。

+1

您還應該添加'private function __clone(){}',以便無法克隆它。 – SeanJA 2010-04-19 22:07:22

0

abstract確實是爲了表示一個「藍圖」,就像您所說的那樣,用於類繼承。

註冊表通常遵循單例模式,這意味着它們將在私有變量中實例化自己。將其定義爲abstract將會阻止其工作。

1

正如其他人說的,你不能實例化一個抽象類。你可以在你的類中使用靜態方法來防止實例化,但我不是那麼做的粉絲,除非我有合適的理由。

現在我可能有點偏離主題,但在您的示例中,您說過您希望爲Registry模式類提供此功能。你不想實例化它的原因是什麼?爲你想要使用的每個註冊表創建註冊表實例是否更好?

喜歡的東西:

class Registry { 
    private $_objects = array(); 

    public function set($name, $object) { 
     $this->_objects[ $name ] = $object; 
    } 

    public function get($name) { 
     return $this->_objects[ $name ]; 
    } 
} 

我甚至不會在這種情況下使用辛格爾頓。

+0

未實例化的原因:懶惰。如果我要實例化它,我必須在任何函數中聲明該實例爲全局變量。如果我只使用靜態類方法/成員,那麼Registry類本身就是全局的。 – Boldewyn 2010-03-23 09:50:19

+0

全局變量是邪惡的!嘗試使用一些其他選項,例如策略模式:http://en.wikipedia.org/wiki/Strategy_pattern – 2010-03-23 10:25:18

+0

@Boldewyn:我必須同意Ondrej,全局變量是邪惡的,如果可能的話,要避免是的,這適用於我在下面建議的Singleton)。有幾種方法可以避免在每個類中實例化註冊表的需求。除了Ondrej的建議之外,另一種方法是僅實例化一次,然後使用依賴注入將其注入任何需要使用它的類中。 – MicE 2010-03-27 22:31:22

0

抽象類的目的是定義1)對其他類有意義的方法,2)當不在其中一個類的上下文中時,這些方法是沒有意義的。

爲了使一些php文檔相位相反,想象你正在連接到數據庫。連接到數據庫沒有太大的意義,除非你有一個特定類型的數據庫連接到。然而,無論數據庫的類型如何,連接都是您想要做的事情。因此,連接可以在一個抽象的數據庫類中定義並繼承,並且通過一個MYSQL類變得有意義。

根據您的要求,這聽起來像您不打算這樣做,而是隻需要一個沒有實例的類。雖然您可以使用抽象類來強制執行此行爲,但這對我來說似乎很不合適,因爲它濫用了抽象類的目的。如果我遇到一個抽象類,我應該能夠合理地期望這會有一些抽象方法,例如,但是你的類沒有。

因此,單身人士似乎是一個更好的選擇。

但是,如果你希望有一個沒有實例的類的原因只是爲了你可以在任何地方調用它,那麼爲什麼你甚至有一個類呢?爲什麼不只是將每個變量加載爲全局變量,然後您可以直接調用而不是通過類來調用它?

我認爲最好的方法是實例化類,然後用依賴注入傳遞它。如果你懶得這樣做(而且如果你是公平的話,那麼你的代碼,不是我的代碼),那麼根本不要打擾這個班級。

更新:看起來你似乎在2個需求之間衝突:需要快速做事,需要以正確的方式做事。如果你不在意爲了節省時間而攜帶大量的全局變量,那麼你會更喜歡使用抽象而不是單例,因爲它涉及的輸入較少。選擇哪個需求對你更重要,並堅持下去。

這裏的正確方法絕對不是使用單例或抽象類,而是使用依賴注入。最快的方法是擁有大量的全局變量或抽象類。

3

你所描述的是PHP語言允許的,但它不是abstract類的預期用法。我不會使用abstract類的static方法。

這樣做的缺點是:另一個開發人員可以擴展你的抽象類,然後實例化一個對象,這是你想要避免的。例如:

class MyRegistry extends AbstractRegistry { } 
$reg = new MyRegistry(); 

真,你只需要擔心這個問題,如果你移交您的抽象類,另一個開發商誰也不會符合你的使用目的,但是這就是爲什麼你會做一類單太。不合作的開發人員可以重寫私有構造:

class Registry 
{ 
    private function __construct() { } 
} 

class MyRegistry extends Registry 
{ 
    public function __construct() { } // change private to public 
} 

如果你自己使用這個類,你只會記得實例化的類。那麼你不需要任何機制來阻止它。因此,由於您正在設計此功能以供其他人使用,因此您需要一些方法來防止這些人繞開您的預期用途。

所以我給予這兩個可能的選擇:

  1. 堅持使用Singleton模式,並確保該構造也final因此沒有人能夠擴展您的類並更改構造非私營:

    class Registry 
    { 
        private final function __construct() { 
        } 
    } 
    
  2. 讓你的註冊表支持靜態和對象用法:

    class Registry 
    { 
        protected static $reg = null; 
    
        public static function getInstance() { 
        if (self::$reg === null) { 
         self::$reg = new Registry(); 
        } 
        return self::$reg; 
        } 
    } 
    

    然後,您可以靜態調用Registry::getInstance(),或者如果您想要一個對象實例,則可以致電new Registry()

    然後你可以做一些漂亮的事情,比如在你的全局註冊表中存儲一個新的註冊表實例! :-)

    我實現這個作爲Zend框架的一部分,在Zend_Registry

1

設置一類抽象的,只有定義靜態屬性/方法不會產生真正的效果。你可以擴展這個類,實例化它並調用一個方法,它會改變靜態類的屬性。顯然很混亂。

摘要也是誤導。摘要是定義一個實現某些功能的類,但需要更多的行爲(通過繼承添加)才能正常工作。最重要的是,它通常是一個不應該與靜態一起使用的功能。你實際上正在邀請程序員錯誤地使用它。

簡答:私人構造函數會更具表現力和安全性。

0

從我的理解來看,沒有實例的類是你不應該在OOP程序中使用的,因爲類的全部(和唯一)目的是作爲新對象的藍圖。 Registry::$someValue$GLOBALS['Registry_someValue']之間的唯一區別在於前者看起來很「fancier」,但都不是真正的面向對象。

因此,要回答你的問題,你不希望有一個「單班」,你想有一個單獨的對象,選配一個工廠方法:

class Registry 
{ 
    static $obj = null; 

    protected function __construct() { 
     ... 
    } 

    static function object() { 
     return self::$obj ? self::$obj : self::$obj = new self; 
    } 
} 

... 

Registry::object()->someValue; 

顯然abstract不會在這裏工作。

+0

在PHP中,沒有實例的類可能非常有用。我使用它們作爲一種名稱空間;他們可以被自動加載,他們不需要被聲明爲全局用於函數內部,並且如果你爲該類選擇一個好名字,代碼更容易閱讀。 它成功地讓我很滿意一個非常簡單的實現。不管它是好還是不好/我們應該使用它們還是不使用它們?我真的不在乎,因爲它很容易使用,理解,而且非常強大。 – Savageman 2010-04-19 22:21:34

0

我會說這是編碼習慣的問題。當你想到一個抽象類時,它通常是你需要繼承以便使用的東西。所以宣佈你的課堂摘要是反直覺的。

除此之外,它只是在你的方法中使用self :: $ somevar而不是$ this-> somevar,如果你將它作爲一個單例實現的話。

1

面向對象中存在常見且廣爲人知的模式。以一種非常規方式使用abstract可能會引起混淆(對不起,我的一些例子是在Java中,而不是PHP):

  • 抽象類 - 意味着概念化一個共同的祖先,但實際情況是不是一類意味着存在(例如形狀是矩形和三角形的抽象超類)。
    普遍實施:
    • 使用abstract修改器類,以防止直接實例,但允許從類派生
  • 實用類 - 並不在表示一個對象類解決方案空間,而是一組有用的靜態操作/方法,例如Java中的數學課程。
    • 化妝類非可導,例如:
      通常通過實現Java使用的類final修改,並
    • 防止直接實例 - 沒有提供構造函數和隱藏或禁用任何暗示或默認構造函數(和拷貝構造函數)
  • 單例類 - 任何一類表示對象在解決方案空間中,但其實例化受到控制或限制,通常可確保只有一個實例。
    • 化妝類非可導,例如:
      通常通過實現Java使用的類final修改,並
    • 防止直接實例 - 沒有提供構造函數和隱藏或禁用任何暗示或默認構造函數(和拷貝構造函數),並且
    • 提供獲取實例的具體手段 - 靜態方法返回的唯一實例(通常getInstance())或實例的數量有限