2011-08-18 118 views
0

繼承我有一個基礎對象(form_field_base),其被擴展/其它對象的形式繼承:PHP和對象 - 延伸的公共對象,該對象的其他對象從

class form_field_base { 
    // Lots of code 
} 
class form_field_text extends form_field_base { 
    // Lots of code 
} 
class form_field_email extends form_field_text { 
    // Extending the text object to update validation, and set input type="email" 
} 
class form_field_file extends form_field_base { 
    // Lots of code, for example uploading files 
} 

的「form_field_base」規定所有表單字段類型使用的幫助器方法,例如html()函數調用特定對象(form_field_email :: html_input)以獲取字段,然後將其放入具有標準標記的字符串中,等等。

所有這些對象都被許多項目使用。

但是,我正在處理的這個最新項目需要定製「form_field_base」對象以允許設置一些幫助文本,這是沒有其他項目需要的功能,並且如果將來的項目需要,它可能會完成不同。

那麼這應該如何組織?

理想情況下,我不會擁有「form_field_base」的完整副本,因爲這會導致代碼重複。

而且它似乎是相當多的開銷有假中間對象:

class form_field_base_common { 
    // Lots of code 
} 
class form_field_base extends form_field_base_common { 
    // By default is empty 
} 

class form_field_text_common extends form_field_base { 
    // Lots of code 
} 
class form_field_text extends form_field_text_common { 
    // ... 
} 

class form_field_email_common extends form_field_text { 
    // Extending the text object to update validation, and set input type="email" 
} 
class form_field_email extends form_field_email_common { 
    // ... 
} 

class form_field_file_common extends form_field_base { 
    // Lots of code, for example uploading files 
} 
class form_field_file extends form_field_file_common { 
    // ... 
} 

以每一個都有它自己的文件,該文件是自動加載(無論是從項目的具體位置,如果它存在,或者來自所有項目都可以訪問的公共文件夾)...已經有8個文件需要被發現,打開,解析等,只是爲了支持表單。

蘇利必須有更好的方法嗎?

+0

您是否嘗試過私人方法和數據?或者你是什麼意思?你是否需要隱藏來自後代的新功能,或者是否使用相同代碼庫的其他項目? – J0HN

+0

更多的是,「form_field_base」已經非常大,並且爲一個小項目(當它被許多項目使用時)添加更多的功能/代碼似乎是浪費。 –

回答

1

你有你的產業鏈,並要修改在每個項目基地的基本實現,同時仍保持通用代碼和類型(而不是被迫嚴重修改現有的項目)。

要走的路是去耦項目特定的自定義常用功能。使用裝飾模式甚至可以在項目之間共享自定義。

您的情況是所有現有項目:

A <- B <- C 

A->a() 
B->a(), B->b() 
C->a(), C->b(), C->c() 

您的新的項目(可以說項目1)應具有:

A1 <- B <- C 

A1->a(), A1->a1(), 
B->a(), B->a1(), B->b() 
C->a(), C->a1(), C->b(), C->c() 

Decorator模式要求你創建一個裝飾爲每個對象你想擴展(A1,B1,C1)。你希望A1的自定義方法也可以在裝飾B1和C1中使用,所以你需要像原始類一樣鏈接它們。

A1 decorates A 
B1 decorates B 
C1 decorates C 

A1 <- B1 <- C1 

A1->a1() 
B1->a1() 
C1->a1() 

你還是要A,B,C的功能還你的裝修類,所以你需要創建的每個裝飾和裝潢源類之間的聯繫,並委託相應的方法:

A1 hosts a reference of A 
B1 hosts a reference of B 
C1 hosts a reference of C 

A1->a() ----> $this->myA->a(); 
B1->a() ----> $this->myB->a(); 
B1->b() ----> $this->myB->b(); 

所有自定義項目1個方法是直接執行:

A1->a1() ----> $this->a1(); 

在您的新項目1使用,那麼:

A1 instead of A 
B1 instead of B 
C1 instead of C 

你的A1,B1和C1可以被允許創建自己的,B,C正確的情況下,在其構造雖然你可以通過實例來使多個裝飾。在這種情況下,您將需要適當的接口,可以說IA,IB,IC。然後你的A1可以有方法setA(IA theA)其中A可能是一個確切的A,或者甚至是一個A1或A2或A3 ...但是,這是更先進的,你會發現更多的信息通過搜索裝飾模式,你可能需要一點點體驗界面和多元化。


共有:

  1. 離開你的產業鏈,因爲它是

  2. 爲每個自定義項目創建一個裝飾鏈

  3. 鏈接的裝飾到原來的類和委託共同的功能。

  4. 使用裝飾器代替自定義項目中的原始類。現在它們與原件相同,並且還有其他要添加的方法。

+0

謝謝Jens,雖然我可能做的事情略有不同,但基本上我是如何解決這個問題的......雖然我發現越多使用面向對象的編程,您似乎需要使用的解決方案越複雜(也許這就是隻是我雖然)。 –

0

這取決於您想要對幫助文本做什麼。也許,雖然你可以這樣做:

1 - 創建幫助文本對象,你想要的目的。 2 - 在您的form_field_base類中創建可選的構造函數參數。如果設置,它將是幫助文本類的私人實例。 3 - 在您的子類中,檢查是否存在幫助文本對象。如果已設置,請執行附加操作。儘管如此,也許這並不適合你想要做的事情。儘管沒有更確定,但這是我能想到的最好的!

+0

感謝您的建議,我必須承認,我希望不要以這種方式編輯現有代碼,因爲儘管您的方式可以允許以不同方式(以某種方式)處理的自定義幫助文本,但我肯定會有其他隨機(一次性)功能出現在將來也需要這種編輯。 –

0

如果你已經有了一個很好的基礎類,它可以在沒有任何幫助文本的情況下很好地工作,那麼保持原樣。相反,創建一個代表表單組件的類。這可能是更高的抽象水平:

abstract class FormComponent { 
    public __construct(FormField $field) { 
     $this->_field = $field; 
    } 

    public html() { 
     // Here you can choose any way to integrate the field's input HTML into 
     // the component's HTML. You can wrap it in a label, for example. 
     return $self->_get_html() . $self->_field->html_input(); 
    } 
} 

更新:您不必觸碰form_field繼承鏈,你可以離開它,因爲它是。如果你做這種方式,你將不得不以注入實例化的領域對象到組件類是這樣的:

$field = new form_field_email($arguments); // Whatever arguments it takes 

// Here the FormComponent_Email class inherits the FormComponent class 
$component = new FormComponent_Email($field); 
$component->set_hint('Some hint'); // Set your help text here 
$component->set_label('Enter your email'); 

// You can even add an error container if you wish 
if ($some_email_error) { 
    $component->set_error('Your email has an error!'); 
} 

$component_html = $component->html(); 

這裏的電子郵件組件類的html方法中,你可以HTML包裝添加到字段即會顯示標籤,提示容器,錯誤容器和其他所有你需要的東西。

+0

嗨伊戈爾,這聽起來很有趣...我只是看看文檔,但你有更多關於如何工作的例子...如果你說我可以放棄「擴展form_field_base 「從文本/電子郵件/文件/ etc類中,以便它們可以以某種方式從該類繼承,或從另一個(自定義)類繼承,然後從form_field_base繼承,那將會很好。 –

+0

我已經更新了答案,請參閱代碼片段。瞭解如何使用常規窗體對象並將其傳遞給新對象的構造函數。 –

+1

只是想說聲謝謝你的例子,最後我沒有使用這種方法,因爲在每個領域增加了一個額外的包裝(該項目包含幾百個領域),但這是一個很好的解決方案,可以幫助其他項目。 –