PHP和对象——扩展一个公共对象,其他对象从它继承


PHP and Objects - Extending a common object, which other objects inherit from

我有一个基对象(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. 为每个自定义项目创建一个装饰链

  3. 将装饰器链接到它们的原始类,并委托公共功能。

  4. 在自定义项目中使用装饰器而不是原始类。

这取决于您想如何处理帮助文本。也许,你可以这样做:

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包装字段,将显示标签,提示容器,错误容器和其他一切你需要的