2010-01-05 54 views
2

我有兩個類:如何在沒有構造函數的情況下強制實現類變量的初始化?

抽象實體摘要ClassA的延伸實體ClassB的擴展ClassA的

實體是所有類型的實體,如汽車或學生的泛型類。有一個名爲$_entity_name的靜態類變量,它包含ER圖中實體的確切類名。

大問題:實體無法設置此變量的值,但使用它!所以我必須在實體中定義它,但我不能在ClassA中重新定義它。

ClassA是特殊ORM實體的自動生成基類(可以說是「學生」)。用戶應該繼承這個基類以添加他自己的業務邏輯。

ClassB由用戶創建並擴展ClassA。這是他添加業務邏輯的地方。他不應該觸摸$_entity_name或設置此值。

我看到的唯一方法是這樣的:

ClassA的定義哪些初始化這些「系統級」的實例變量爲合理值的init()方法。

我希望用戶不必考慮任何不尋常的事情。我不知道是否通常在PHP中明確地調用父類的構造函數,或者不。但我想這不是。

問題: A)在實例化類之後調用init()的最方便的方法是什麼? B)有沒有辦法以這種方式實現這一點,即創建ClassB的用戶一定不會想到調用init()? C)我還有什麼其他選擇?

+0

至於調用父類的構造。如果父類具有定義的構造函數,而子類沒有,則在創建子類的對象時將調用父類的構造函數。你也可以在調用父構造函數的子類中定義一個構造函數{parent :: __ construct(); }然後再做其他事情。 – RMcLeod 2010-01-05 11:57:08

回答

1

不完全的回答你的問題,但也許你的問題的解決方案:具有包含實際的類名稱的屬性的
相反(這是你想要的,不是嗎?),你可以使用get_called_class()(截至PHP 5.3.0)來獲取已調用靜態函數的類的名稱。

class Entity { 
    public static function foo() { 
    echo 'name=', get_called_class(), "\n"; 
    } 
} 

class ClassA extends Entity { } 
class ClassB extends ClassA { } 

Entity::foo(); 
ClassA::foo(); 
ClassB::foo(); 

打印

name=Entity 
name=ClassA 
name=ClassB 
+0

雖然聽起來不錯,但不幸的是我沒有介紹PHP類,而是ER圖中的實體名稱。這有點不同。 – openfrog 2010-01-05 12:30:14

+0

I.e.開發商_必須提供一個有意義的價值,而這個價值無法從您的課程第一次使用時獲得的信息中扣除。或多或少像'abstract public static $ _entity_name;'(這在php中不可用)? – VolkerK 2010-01-05 12:50:11

1

它實際上是相當普遍的調用使用parent::__construct()的父類的構造。你可以接受以下事情嗎?

class B extends A { 
    function __construct($entity_name = '') { 
     parent::__construct($entity_name); 
    } 
} 

abstract class A extends Entity { 
    function __construct($entity_name = '') { 
     parent::__construct($entity_name); 
    } 
} 

abstract class Entity { 
    static $entity_name; 
    function __construct($entity_name = '') { 
     self::$entity_name = $entity_name; 
    } 
} 

$b = new B('my_name'); 
+0

謝謝,但ClassB是業務邏輯層的一部分,由用戶創建,因此不應該混淆框架邏輯。但在其他任何情況下,這都會很棒! – openfrog 2010-01-05 12:32:07

0

VolkerK的回答是正確的 - 但你也可以看看instanceof運算符和get_class()函數。

「可惜我沒彪PHP類,但在ER圖中的實體名稱。這是一個有點不同」(openfrog)

然而,這是一個完全不同的問題,(假設你不能修復你的實體命名系統中的這個缺陷)在2個列表之間映射條目是不容易的。

您在運行時使用屬性的解決方案非常混亂且不靈活。它依賴於隱式映射,其細節散佈在整個代碼庫中,任何非線性啓發式維護/調試都非常困難(例如,如果多於一個類映射到ERD中的同一個實體)。

C.

0

ClassB的下方,我看到了兩個辦法,以確保ClassA的初始化:

  1. 指示將ClassB的構造函數調用任意或parent::__construct()不存在。

  2. 創建掛鉤init由ClassA調用並由ClassB實現的函數。這樣一來,ClassA的處理ClassA的初始化在__construct()和ClassB與$this->init()

1

據我理解你的問題:你有ClassB那就是ClassA一個子類,當ClassB對象實例化,你想擁有一種方法(例如init())將在ClassA級別上調用,而不需要您的用戶在ClassB代碼中寫入任何類型的任何內容。

這是構造函數的用途。

但是,你暗示,因爲ClassB將由用戶來寫的,你不想逼他/她打電話parent::__construct()從ClassB的構造函數,所以這就是爲什麼就想到了使用init()方法和也是爲什麼原因你正在尋找一種方式有init()被自動叫做__construct()的方式,我是對嗎?那麼,這個怎麼樣:

  • ClassA用戶有加空init()方法重載時,他/她寫ClassB
  • 你想在ClassA的的__construct()做的東西(如初始化$_entity_name)在ClassA的的__construct()致電$this->init()
  • 將ClassA的__construct()設置爲final,以便用戶不能重載構造函數。
  • 告訴你的用戶,他們有超載init()方法爲ClassA

利弊的子類定製的初始化:你可以通過ClassA完成初始化的東西,而無需用戶寫任何東西。

缺點:用戶不能超載__construct()但可以重載init(),而不是做定製的初始化在ClassB

0

這個建議不能被重寫這將滿足您的需求。

Class A{ 
    final public function __construct() { 
     // do your stuff here 

     // optionally add something like this: 
     $rc = new ReflectionClass($this); 
     if(in_array('init', $rc->getMethods()) { 
      $this->init(); 
     } 
    } 

如果你想要什麼不做(即你希望從類派生A到能夠使用__construct()),你應該想想你的封裝類,而不是從它繼承。

0

有一個額外的方式和/或方式!

我發現它有用通過使函數返回類的直接new例如像這樣的實例:

class Foo { 
    public $name; 
    public $age; 
    public function __construct($nameArg, $ageArg) { 
     $this->name= $nameArg; 
     $this->age= $ageArg; 
    } 
    public function bar() { 
     return "Hello, ".$this->name."! Are you really ".$this->age." years old?"; 
    } 
} 
function foo($name, $age) { 
    return new Foo($name, $age); 
} 

// And then you can do: 
echo foo("john", 22)->bar(); // outputs: "Hello, john! Are you really 22 years old?"; 
echo foo("caroline", 27)->name; // outputs: caroline 
echo foo("caroline", 27)->age; // outputs: 27 
相關問題