在PHP中管理长类文件的策略

时间:2009-12-17 17:47:02

标签: php oop refactoring

我有一堆功能,我想进入一个类。它们目前被分成几个相当长的文件。我不想有一个2500行文件,但据我所知,你不能使用include将一个类分成多个文件。从理论上讲,我可以将函数分组到不同的类中,但它们之间的关系非常密切,我觉得它们属于一体,并且将它们分开会减少我希望从程序方法中脱离出来的一些实用工具。 (使用共享属性,而不是几乎每个函数中的一堆参数)。

我知道这有点模糊,但是有什么建议/指针吗?如果重要,那就是原型,因此代码管理的简易性优先于安全性和性能。

更新:让我看看我是否可以删除一些含糊不清的内容:

此类/函数集输出复杂形式的html。每个部分中有许多不同的部分和变体,具体取决于当前传递给函数的约5或6个参数。我希望将参数一次定义为类的属性,然后从所有节创建方法中访问它们。如果我使用子类,那些属性的值将无法正确初始化,因此需要一个类。 (嗯......除非我把它们定义为静态。我可能刚刚回答了我自己的问题。我将不得不看看是否有任何理由不起作用。)

我目前有很多功能,如:

get_section_A ($type='foo', $mode='bar', $read_only=false, $values_array=array()) {
    if ($this->type == 'foo') { }
    else ($this->type == 'foo') { }
}    

所以我最初想象的是:

class MyForm {
    public $type;          // or maybe they'd be private or 
    public $mode;          // I'd use getters and setters 
    public $read_only;     // let's not get distracted by that :)
    public $values_array;
    // etc.

    function __constructor ($type='foo', $mode='bar', $read_only=false, $values_array=array()) {
        $this->type = $type;
        // etc.
    }

    function get_sections () {
        $result = $this->get_section_A();
        $result .= $this->get_section_B();
        $result .= $this->get_section_C();        
    }      

    function get_section_A() { 
        if ($this->type == 'foo') { }
        else { }
    }
    function get_section_B() {}
    function get_section_C() {}
    // etc. for 2500 lines
}

现在我想的是:

// container class file
class MyForm {
    static $type 
    static $mode
    static $read_only
    static $values_array
    // etc.

    function __constructor ($type='foo', $mode='bar', $read_only=false, $values_array=array()) {
        MyForm::$type = $type;
        // etc.
    }

    function get_sections () {
        $result = new SectionA();
        $result .= new SectionB();
        $result .= new SectionC();
    }      
}

// section A file
class SectionA extends MyForm {
    function __constructor() { 
        if (MyForm::$type == 'foo') { }
        else { }
    }

    function __toString() {
        // return string representation of section
    }
}

// etc.

或者我可能需要一个FormSection的抽象类,其中属性存在。

还有其他想法/方法吗?

6 个答案:

答案 0 :(得分:6)

我会将它们分成任意数量的课程(或许多有意义的课程),然后define an autoloader以避免包含头痛。

修改

好的,在看到更多代码之后 - 我认为你正在接近错误的子类。你有很多针对if的{​​{1}}语句,它告诉我 是多态应该基于的。

$type

答案 1 :(得分:4)

通常我会做这样的事情:

class one
{
    public function __get($key)
    {
        // require __DIR__ / $key . php
        // instanciate the sub class
    }

    public function mainMethod()
    {
    }
}

class one_subOne extends one
{
    public function otherMethod()
    {
    }
}

class one_subTwo extends one
{
    public function anotherMethod()
    {
    }
}

$one->mainMethod();
$one->subOne->otherMethod();
$one->subTwo->anotherMethod();

答案 2 :(得分:3)

就构建视图而言,您可能想尝试CompositeView pattern

这是一个关于它如何在PHP中看起来的小例子。为了这个例子,假装View::$html被封装在一个Template类中,该类可以从磁盘加载html并允许你注入变量,处理输出转义等等。

interface IView {
    public function display();
}

class View implements IView {
    public $html = ''; 

    public function display() {
        echo $this->html;
    }   
}

class CompositeView implements IView {
    private $views;
    public function addPartial(IView $view) {
        $this->views[] = $view;
    }   
    public function display() {
        foreach ($this->views as $view) {
            $view->display();
        }   
    }   
}

IView接口的原因是允许您使用其他复合视图构建复合视图。

所以现在考虑一个包含三个部分的表单:header,body和footer。

class HeaderView extends View {
    public function __construct() {
        $this->html .= "<h1>Hi</h1>\n";
    }   
}

class BodyView extends View {
    public function __construct() {
        $this->html .= "<p>Hi there.</p>\n";
    }   
}

class FooterView extends View {
    public function __construct() {
        $this->html .= "<h3>&copy; 2012</h3>\n";
    }   
}

(同样,你不会只是将HTML写入该公共变量并处理输出自行转义。你可能会引用模板文件名并通过模板的界面注册你的数据。)

然后,将它们放在一起你会去:

$view = new CompositeView();
// here you would make decisions about which pieces to include, based
// on your business logic.  see note below.
$view->addPartial(new HeaderView());
$view->addPartial(new BodyView());
$view->addPartial(new FooterView());
$view->display();

所以现在你的视图可以被组合并且片段可以重复使用,但是你很容易弄乱构建它们的代码,特别是如果你有很多条件和许多不同的可能结果(这听起来像你做的那样)。 )在这种情况下,战略模式可能会有所帮助。

如果您还没有阅读UncleBob's SOLID article,请先做其他事情!至少是单一责任原则。我还建议在某些时候阅读Joshua Kerievsky的Refactoring to Patterns。

答案 3 :(得分:1)

如果您想要执行OOP,请将关注点分开并将它们封装到适当的类中。通过扩展它们或通过组合或更好的聚合来组合它们。删除所有重复的代码。不要重复自己。

在您的情况下,将与任何表单相关的内容与您的特定表单中的内容分开。可用于任何Form的代码是您要放入通用Form类的代码。您可以在以后的项目中重复使用它。有关非常复杂的Form类的示例,请查看Zend_Form

您的代码中与/特定表单相关的任何内容都会进入一个扩展通用表单的类。假设从代码中给出的type属性,您可能最终会得到多个特殊用途的表单类(而不是一种类型适合所有形式),这可能会消除{{1}的复杂性方法并使您的代码更容易维护,因为您可以专注于特定类型的表单应该看起来和做什么。

最后,如果您有代码从表单中获取表单的数据,或者与表单构建没有直接关系,请删除它并将其放入单独的类中。请记住,您想要分离关注点,而您的表单类关注的是构建表单,而不是获取数据或其他内容。数据是您希望通过构造函数或专用setter传递给表单的。

答案 4 :(得分:0)

它们都在不同的文件中,这意味着它们与文件分组不同。在将它们构建到类中时,只需采用相同的逻辑。我有一个Page对象来处理构建页面。从技术上讲,我的页面标题的HTML是页面的一部分,但我将它分成一个Page_HTML类,以保持我的理智,而不是创建巨大的类。

另外,我倾向于使sub_classes(如本例中的Page_HTML)静态,而不是实例化它。这样我可以访问Page类中的$ this变量,但仍然将它分组到另一个类中。

class Page
{
    function buildPage($content)
    {
        $content = Page_HTML::getHeader() . $content;
    }
}

class Page_HTML
{
    function getHeader()
    {
    }
}

答案 5 :(得分:0)

  

此类/函数集输出   复杂形式的html。

为什么不从等式中删除PHP?您似乎正在使用PHP来组织可以使用文件系统轻松完成的视图。只需用尽可能少的PHP编写HTML视图。然后使用PHP将请求映射到视图。我假设你正在使用PHP处理表单,你可以继续这样做。但是,您的课程将变得更小,因为他们只接受,验证并可能保存输入。