【问题标题】:workaround for multiple inheritances in PHP?PHP中多重继承的解决方法?
【发布时间】:2010-08-17 17:54:47
【问题描述】:

在我的很多 PHP 类中,我都有以下代码:

private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;


public function isError() {
    return $this->blnError;
}

public function getErrorCode() {
    return $this->intErrorCode;
}

private function setError( $strError, $intErrorCode = NULL ) {
    $this->blnError = TRUE;
    $this->intErrorCode = $intErrorCode;
    $this->strError = $strError;
}

关键是外部代码可以知道一个对象是否有错误状态,错误的字符串是什么等等。但是在一堆不同的类中拥有这个确切的代码是重复的!

我很想有一个我可以做的双重扩展

class childClass extends parentClass, error {
    ...
}

并且天生就有这些属性和方法,但是 PHP 不支持多重继承。我正在考虑做的是创建一个存在于每个类中的错误类。如果我把它公开,我可以直接通过对象调用它

if ( $myObject->error->isError() ) {...}

但这不会使其错误状态也可以从包含类之外设置,

$myObject->error->setError("I shouldn't be doing this here");

我宁愿避免?

或者我可以在包含类中编写“网关”函数,对错误对象进行适当的调用,并防止从外部设置错误状态,

class childClass extends parentClass {

    private $error;

    public function __construct(...) {
        ...
        $error = & new error();
        ...
    }

    public function isError() {...}
    public function getError() {...}
    public function getErrorCode() {...}
    private function setError() {...}

    ...
}

但这会导致(部分)我试图避免的代码重复。

这里的最佳解决方案是什么?我正在尝试为许多对象提供错误状态的功能,以便外部世界可以看到它们的错误状态,并且重复最少。

【问题讨论】:

  • 我猜你可以做类似BaseClass 的事情,所有父类都从它继承。它并不优雅,但这是我能想到的。

标签: php oop multiple-inheritance


【解决方案1】:

使用组合而不是继承。

class Errors {

    private $strError = "";
    private $intErrorCode = NULL;
    private $blnError = FALSE;


    public function isError() {
        return $this->blnError;
    }

    public function getErrorCode() {
        return $this->intErrorCode;
    }

    private function setError( $strError, $intErrorCode = NULL ) {
        $this->blnError = TRUE;
        $this->intErrorCode = $intErrorCode;
        $this->strError = $strError;
    }

}

现在使用私有实例变量来引用它:

class childClass extends parentClass {
    private $errors = new Errors();
    ...
}

private 可见性可防止您在课堂外引用 $errors

也无需在childClass 内创建isError()getError() 等(因此无需担心代码重复)。只需调用$this->errors->isError()$this->errors->getError() 等即可。如果您仍然希望实现这些方法,如下所示,您可以指定一个接口。

【讨论】:

  • 让我们添加一个 interfacechildClass 以实现 setErrorsgetErrors
  • 另一方面,如果您有错误类扩展父类并且所有子类都扩展错误类,您将不需要一个衬里“private $errors = new Errors();”在你所有的孩子班上。 (不是说更好的解决方案,只是说。)
  • 我完全糊涂了吗? to 呼叫errors 的重点不是在课堂外吗?我想从外部查看该类是否有错误,而 $errors 只是一种跟踪它的方法。我不想从课堂外引用的唯一setError()。我对吗?我的意思是,对于一个班级来说,这是一种在内部跟踪其错误的好方法,但在某些时候,我确实想将它传达给外部世界,而这首先是重点——找出是否$myObject 处于错误状态。
  • @user childClass 的实例拥有所有可用的 Errors 类方法。
  • @user 在这种情况下,按照 Gordon 的建议进行操作,并实现一个接口,例如 ErrorCheckable,它只公开您想要的方法。我更喜欢这种少量的重复,而不是以不应该的方式弯曲继承层次结构。
【解决方案2】:

您也可以滥用__call 魔术方法来做同样的事情:

public function __call($name, array $arguments) {
    $name = strtolower($name);
    if (isset($this->methods[$name])) {
        array_unshift($arguments, $this);
        return call_user_func_array($this->methods[$name], $arguments);
    }
    throw new BadMethodCallException('Method does not exist');
}

请注意,我说的是滥用...理想情况下,我会考虑不同的架构,而不是到处都有所有这些“通用方法”。为什么不使用异常而不是检查$foo->isError?如果这不合适,为什么不装饰一个班级?

class Errors 
    protected $object = null;
    public function __construct($object) {
        $this->object = $object;
    }
    public function __call($method, array $arguments) {
        $callback = array($this->object, $method);
        if (is_callable($callback)) {
                return call_user_func_array($callback, $arguments);
        }
        throw new BadMethodCallException('Method does not exist');
    }
    public function __get($name) { return $this->object->$name; }
    public function __set($name, $value) { $this->object->$name = $value; }
    //  Your methods here
    public function isInstance($name) { return $this->object instanceof $name; }
}

然后将现有对象“包装”在该类中:

$obj = new Errors($obj);
$obj->foo();

【讨论】:

  • 这是解决这个问题的一个非常有趣的方法,我可能会尝试在我的下一个项目中实现它。我喜欢它!
  • 它有一些非常大的缺点。首先,你不能天真地使用接口,因为它不会实现你的接口(这就是isInstance 方法的原因)。其次,调试可能非常困难,因为您不确定“装饰器”或动态方法的确切链或顺序……第三,它可能会导致一些非常有趣的“此方法在哪里定义”的问题。它在某些情况下绝对有用(这就是我发布它的原因)。但是要非常小心你如何实现它......
【解决方案3】:

从 PHP 5.4 开始,您可以使用Traits

例如,您可以像这样将 Trait 称为 ErrorTrait:

trait ErrorTrait {
    private $strError = "";
    private $intErrorCode = NULL;
    private $blnError = FALSE;


    public function isError() {
        return $this->blnError;
    }

    public function getErrorCode() {
        return $this->intErrorCode;
    }

    private function setError( $strError, $intErrorCode = NULL ) {
        $this->blnError = TRUE;
        $this->intErrorCode = $intErrorCode;
        $this->strError = $strError;
    }
}

然后你会像这样定义你的子类:

class childClass extends parentClass {
    use ErrorTrait;

    ...
}

特征的工作方式基本上类似于复制/粘贴,因此特征中的所有代码都可以在类中使用(没有代码重复)。

【讨论】:

    猜你喜欢
    • 2023-04-08
    • 2019-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-16
    • 2013-05-20
    相关资源
    最近更新 更多