我有一个基对象(form_field_base),它被其他对象以以下形式扩展/继承:
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个文件,需要找到,打开,解析等,只是为了支持一个表单。
难道一定有更好的办法吗?您有您的继承链,并且您希望在每个项目的基础上修改基本实现,同时仍然保持公共代码和类型(并且不被迫严格修改现有项目)。
方法是将公共功能与项目特定的自定义解耦。使用decorator模式,它甚至允许您在项目之间共享自定义。您的情况适用于所有现有项目:
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()
装饰器模式要求你为每个想要扩展的对象(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中你使用then:
A1 instead of A
B1 instead of B
C1 instead of C
你的A1, B1和C1可能被允许在他们的构造函数中创建他们的A, B, C的实例,尽管你可以通过实例来启用多个装饰。在这种情况下,您将需要适当的接口,例如IA, IB, IC。然后您的A1可以具有方法setA(IA theA)
,其中theA可能是精确的A,甚至是A1或A2或A3…但这是更高级的,你可以通过谷歌搜索装饰器模式找到更多的信息,你可能需要一点接口和多态的经验。
完全
:
保持继承链不变
为每个自定义项目创建一个装饰链
将装饰器链接到它们的原始类,并委托公共功能。
在自定义项目中使用装饰器而不是原始类。
这取决于您想如何处理帮助文本。也许,你可以这样做:
1 -根据需要创建帮助文本对象。2 -在form_field_base
类中创建一个可选的构造函数参数。如果设置了,它将是帮助文本类的私有实例。在子类中,检查是否存在帮助文本对象。如果已设置,则执行附加操作。
也许这并不适合你想要做的事情。虽然不太确定,但这是我能想出的最好办法了!
如果您已经为所有表单字段提供了一个很好的基类,并且不需要任何帮助文本,那么就让它保持原样。相反,创建一个表示表单组件的类。这可以是更高层次的抽象:
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包装字段,将显示标签,提示容器,错误容器和其他一切你需要的