【问题标题】:php same method in different classesphp在不同的类中使用相同的方法
【发布时间】:2018-01-22 12:22:25
【问题描述】:

我有各种类,它们的方法具有相同的代码。维护观点是一种非常糟糕的做法。

这是此类的一个示例:

类 accountController:

<?php

namespace controller\admin;

class accountController extends \controller\baseController
{
    private $table = 'account';

    public function itemslist()
    {
        list($res, $totalcount) = $this->getResultAndCount();

        return $this->twig->render('/admin/accounts.html.twig');
    }
    .
    ... other methods
    .
    private function getResultAndCount()
    {
        $sql = 'SELECT * FROM ' . $this->table;
        $count = $this->pdo->rowCount();
        $rows = $this->pdo->resultset();

        return array($rows, $count);
    }
}

类用户控制器:

<?php

namespace controller\admin;

class userController extends \controller\baseController
{
    private $table = 'user';

    public function itemslist()
    {
        list($res, $totalcount) = $this->getResultAndCount();

        return $this->twig->render('/admin/users.html.twig');
    }
    .
    ... other methods
    .
    private function getResultAndCount()
    {
        $sql = 'SELECT * FROM ' . $this->table;
        $count = $this->pdo->rowCount();
        $rows = $this->pdo->resultset();

        return array($rows, $count);
    }
}

如图所示,具有相同代码的方法 getResultAndCount 是重复的。我不是 OOP 方面的专家,我一直在寻找在某处只有一个代码并在 itemslistAction() 方法中引用它的方法。

我已经看到了执行此操作的方法,例如使用此方法创建另一个类并在父类中调用它、接口、方法……但我很困惑,我想知道实现它的最佳方法是什么以及如何。

【问题讨论】:

  • 两个控制器都扩展\controller\baseController,你为什么不把这个方法放在\controller\baseController
  • 您已经有很好的建议来快速解决您的问题,但我要补充一点,您的控制器可能有太多责任。与数据库交互应该由模型(或实体,如果你使用 ORM)处理,而控制器应该处理整个控制流。
  • 我同意你的看法。我是 MVC 的新手,如果你能提出任何建议,我今天会学到一些东西,这对社区也有帮助。

标签: php class oop


【解决方案1】:

您的两个类都扩展了\controller\baseController 类。您可以将重复函数放在 \controller\baseController 类中,但如果您有其他扩展 \controller\baseController 的类,并且如果您打算仅在您声明的类中使用此函数,则可以创建一个扩展 \controller\baseController 的新类并在该类中实现复制功能。

<?php
namespace controller\admin;

class bridgeClass extends \controller\baseController
{
    //...
    protected function getResultAndCount()
    {
        $sql = 'SELECT * FROM ' . $this->table;
        $count = $this->pdo->rowCount();
        $rows = $this->pdo->resultset();

        return array($rows, $count);
    } 
}
?>

我们在bridgeClass 中实现了上面声明的函数。另请注意,我们将函数的可见性从private 更改为protected,因此子类可以毫无问题地访问该函数。然后你可以让你的两个类都扩展bridgeClass。所以在这种情况下,现在你的两个班级都有 bridgeClass\controller\baseController 作为他们的父母。

【讨论】:

  • baseController 也是其他不需要该方法的类的父类。这样做好吗?或者然后是更好的另一种解决方案。
  • 按照您的建议编辑了答案。这可能是你能做的最好的了。
【解决方案2】:
<?php

namespace controller\admin;

abstract class baseController
{
    private $table;

    public function itemslist()
    {
        list($res, $totalcount) = $this->getResultAndCount();
        return $this->twig->render('/admin/accounts.html.twig');
    }

    private function getResultAndCount()
    {
        $sql = 'SELECT * FROM ' . $this->table;
        $count = $this->pdo->rowCount();
        $rows = $this->pdo->resultset();

        return array($rows, $count);
    }
}

类 accountController:

<?php
namespace controller\admin;

class accountController extends \controller\baseController
{
    private $table = 'account';

    // more method ....
}

类用户控制器:

<?php
namespace controller\admin;

class userController extends \controller\baseController
{
    private $table = 'user';

    // more method ....
}

【讨论】:

  • 感谢您的所有回答。我在命名空间上犯了一个打字错误。 baseController 位于命名空间“controller”中,accountController 位于命名空间“controller\admin”中。我还有其他具有相同类名的命名空间,例如“controller\customer\accountController”。我正在尝试您提出的解决方案,但我遇到了名称空间错误。我试过所有的组合都没有运气。有什么建议吗?
【解决方案3】:

只是刮擦:

interface IValue{
    public function value();
}

class DBQuery implements IValue{
    private $pdo;
    private $sql;

    public function __construct(...){
        ....
    }

    public function value(){
        // execute $this->sql
    }    
}

class LazyTemplate{
    private $twig;
    private $template;
    private $data;

    public function __construct(..., IValue $data){
        ....
    }

    public function render(){
        return $this->twig->render(
            $this->template, 
            $this->data->value()
        );
    }
}

namespace controller\admin;

class accountController extends \controller\baseController
{
    private $items;

    public function __construct(...){
        $this->items = new LazyTemplate(
            $this->twig,
            '/admin/accounts.html.twig',
            new DBQuery($this->pdo, 'SELECT * FROM account')
        );
    }

    public function itemslist()
    {
        return $this->items->render();
    }
}

class userController extends \controller\baseController
{
    private $items;

    public function __construct(...){
        $this->items = new LazyTemplate(
            $this->twig,
            '/admin/users.html.twig',
            new DBQuery($this->pdo, 'SELECT * FROM user')
        );
    }

    public function itemslist()
    {
        return $this->items->render();
    }
}

P。 s。我不知道 Twig API,所以可能有更好的方法。


许多人建议使用继承来消除重复代码。确实,继承可以让您快速简单地做到这一点,但请考虑这种方法的缺点:

  • 我们的类越来越大,尽管领域模型不需要它。

  • 我们可能很容易需要几个列表,但我们只提供了一个继承。进一步炸毁基类?

  • 其他地方可能需要列表。复制那里的代码并再次获得副本?

  • 我们只是使测试复杂化。有必要添加一个易于测试的非常小的方面,但现在我们有了整个层次结构的这一部分。

它遵循我们的意图。我们真正想用重复的代码说什么? - 我会这样说:“有必要以某种方式获取数据并*以某种方式*将它们传输到模板”。 “不知何故”是抽象的边界,应该在单独的实体中突出显示的地方。

以某种方式将它们(数据)传输到模板” - 是某种实体,它根据需要获取数据,将其提供给模板并返回渲染的结果。我称这个实体为LazyTemplate,因为它执行“惰性计算”(即使没有缓存,因为它通常会发生)。

LazyTemplate 可以立即接收数据,也可以通过闭包接收数据,但我选择了接口IValue。接口是“严格类型”,我们可以很容易地通过 PHP 本身(5.0+)提供参数检查。此外,在紧急时刻之前,我们不需要这些数据。如果我们的列表是隐藏的,那么执行对数据库的查询是没有意义的。 IValue 只是LazyTemplate 与其环境之间的契约。

以某种方式获取数据” - 这是另一个实体。理想情况下,该实体不应指定此数据的来源,但为简单起见,我决定立即描述类DBQuery。此外,我们已经有了IValue,它执行相同的功能。

当然,我们不得不引入几个额外的抽象(非常小的,值得注意),但现在我们可以重用它们,扩展它们(例如,通过创建类CachedValueDynamicTemplate等.) 并且只测试特定的功能。

控制器仍然有类似的代码行,但不要盲目尝试消除重复。用户列表和帐户列表是两个不同列表,它们将来可能变得完全不兼容。

P。 s。对于任何错误,我深表歉意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-23
    • 1970-01-01
    • 2013-06-13
    • 2015-07-04
    • 1970-01-01
    • 2012-08-11
    • 2014-03-23
    • 1970-01-01
    相关资源
    最近更新 更多