2011-08-18 85 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

只是想说声谢谢你的例子,最后我没有使用这种方法,因为在每个领域增加了一个额外的包装(该项目包含几百个领域),但这是一个很好的解决方案,可以帮助其他项目。 –