PHP和对象 - 扩展其他对象继承的公共对象

时间:2011-08-18 09:13:52

标签: php oop class inheritance object

我有一个基础对象(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个文件了需要被发现,打开,解析等,只是为了支持表格。

有必要有一个更好的方法吗?

3 个答案:

答案 0 :(得分: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()

装饰器模式要求您为要扩展的每个对象(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在其构造函数中创建其A,B,C实例,尽管您可以传递实例以启用多个装饰。在这种情况下,您将需要适当的接口,比如IA,IB,IC。然后您的A1可以使用方法setA(IA theA),其中A可能是精确的A或甚至是A1或A2或A3 ...但这是更高级的,您可以通过Google搜索装饰模式找到更多信息,您可能需要有点接口和多态的经验。


共:

  1. 保留您的继承链

  2. 为每个自定义项目创建装饰器链

  3. 将装饰器链接到原始类并委派常用功能。

  4. 在自定义项目中使用装饰器而不是原始类。它们现在与原件相同,还有您想要添加的其他方法。

答案 1 :(得分:0)

这取决于您要对帮助文本执行的操作。也许,虽然你可以这样做:

1 - 根据需要创建帮助文本对象。 2 - 在form_field_base类中创建可选的构造函数参数。如果设置,它将是帮助文本类的私有实例。 3 - 在子类中,检查是否存在帮助文本对象。如果已设置,请执行其他操作。

但是,这可能不适合您尝试做的事情。但是,如果不是更确定,那就是我能想到的最好的结果!

答案 2 :(得分: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包装添加到将显示标签,提示容器,错误容器以及您需要的所有其他内容的字段中。