如何使用traits将大类中的代码拆分成几个文件?

时间:2014-06-28 17:11:34

标签: php traits

来自Ruby,我很高兴发现PHP特性。它们类似于ruby的模块系统,我喜欢它。

现在我已经阅读了许多帖子,说PHP特性是邪恶的,因为它们打破了OO设计,你不能像上课一样容易地测试它们等。

我想知道是否使用特征将一个非常大的类分成更易读的部分是一个好主意。

这个课程已经很大了,很可能会长达数千行,而不需要重新分解。

即使方法可以按照关注进行分组,它们也经常需要相互调用,这让我想知道将类拆分成不同的类是否是最佳的。

为了说明,这是我提出的一个简化示例。

大班:

/**
* Class to refactorize
*/

class Tourist
{
    public function gotoRestaurant()
    {

    }

    public function gotoInternetCafe()
    {

    }

    public function checkEmails()
    {

    }

    public function meetGirlfriend()
    {
        if (Girlfriend::location() === 'restaurant')
        {
            $this->gotoRestaurant();
        }
        else
        {
            $this->checkEmails();
        }
    }

    public function checkEmailsAndMeetGirlfriend()
    {
        $this->checkEmails();
        $this->meetGirlfriend();
    }
}

如果使用类重新分解,我将如何做到这一点:

/**
* Solution 1: using classes
*/

class ChoreDoer
{
    public function __construct(Tourist $t)
    {
        $this->tourist = $t;
    }

    public function checkEmails()
    {
        $mover = new Mover($this->tourist);
        $mover->gotoInternetCafe();
    }

    public function meetGirlfriend()
    {
        if (Girlfriend::location() === 'restaurant')
        {
            $mover = new Mover($this->tourist);
            $mover->gotoRestaurant();
        }
        else
        {
            $this->checkEmails();
        }
    }
}

class Mover
{
    public function __construct(Tourist $t)
    {
        $this->tourist = $t;
    }

    public function gotoRestaurant()
    {

    }

    public function gotoInternetCafe()
    {

    }
}

class Tourist
{
    public function checkEmailsAndMeetGirlfriend()
    {
        $cd = new ChoreDoer($this)
        $cd->checkEmails();
        $cd->meetGirlfriend();

    }
}

我有一种不断调用new Mover()new ChoreDoer()的感觉,而且出现的新类会变得很难读写。

那么在这里使用特征分组功能呢?

/**
* Solution 2: using traits
*/

trait TouristMovements
{
    public function gotoRestaurant()
    {

    }

    public function gotoInternetCafe()
    {

    }
}

trait TouristChores
{
    public function meetGirlfriend()
    {
        if (Girlfriend::location() === 'restaurant')
        {
            $this->gotoRestaurant();
        }
        else
        {
            $this->checkEmails();
        }
    }

    public function checkEmails()
    {
        $this->gotoInternetCafe();
    }
}

class Tourist
{
    use TouristMovements, TouristChores;
    public function checkEmailsAndMeetGirlfriend()
    {
        $this->checkEmails();
        $this->meetGirlfriend();
    }
}

这里的每个特征都可以专注于它的目的,代码易于阅读,方法可以自由地互操作。 但是,由于我从不打算在除TouristsMovements以外的任何类别中使用Tourist特征,我觉得我对它们的使用并不是真正的特征。

你会选择解决方案1吗?解决方案2?别的什么?

2 个答案:

答案 0 :(得分:1)

作为替代方案,您可以使用“解决方案1”执行此类操作:

class ChoreDoer
{
    private $mover;

    public function __construct(Tourist $t)
    {
        $this->tourist = $t;
    }

    public function checkEmails()
    {
        $this->getMover()->gotoInternetCafe();
    }

    public function meetGirlfriend()
    {
        if (Girlfriend::location() === 'restaurant')
        {
            $this->getMover()->gotoRestaurant();
        }else
        {
            $this->checkEmails();

        }
    }
    /**
     * lazy-load mover object
     */
    private function getMover()
    {
        if($this->mover == null)
        {
            $this->mover = new Mover($this->tourist);
        }
        return $this->mover;
    }
}

如果您不喜欢使用getter,则可以将属性命名为

private $_mover;

使用魔法__get为你召唤你的吸气剂:

function __get($property)
{
    $method = 'get'.ucfirst($property);
    if(method_exists($method))
    {
        return $this->$method();
    }
}

虽然这不会表现得那么好,但只需拨打电话

即可
$this->mover->gotoInternetCafe();

而且很干净。

答案 1 :(得分:0)

我很好奇 OP 最终做了什么。

目前处于相同的情况。我有一个很大的类,有很多方法可以做不同的事情。

起初我想将它们拆分为各自独立的类,但这需要大量将 $this->xxx() 调用替换为 $actualobj->xxx()。这种方法的好处是我不需要一次加载/包含所有类,因为我可以简单地自动加载需要的内容。

第二种解决方案是使用 trait,在这种情况下,整个事情仍然会被加载到内存中,但至少代码更有条理、可读性和更容易调试,被分离到自己的文件/类中。

我决定分 2 个阶段来做,首先我做 trait 以便分离和清理代码,然后将每个 trait 更改为独立的类,并用 $this->xxx() 调用替换必要的 $actualobj->xxx() 调用路。