【问题标题】:How to extend the common parent of two classes in a third party library?如何在第三方库中扩展两个类的公共父级?
【发布时间】:2011-07-28 09:58:31
【问题描述】:

我通过创建扩展 Zend_Test_PHPUnit_DatabaseTestCaseZend_Test_PHPUnit_ControllerTestCase 的类并将方法放入每个子类中,实现了 http://www.davegardner.me.uk/blog/2011/03/04/mocking-iterator-with-phpunit/ 的 mockiterator 解决方案。

不过,我最终还添加了一些调整,但必须在每个类中进行相同的更改以防止出现不同的行为是很痛苦的。

两个 Zend 类都直接继承自 PHPUnit_Framework_TestCase 有没有一种方法可以轻松地在 PHPUnit 和 Zend 类之间插入一个类,而不涉及直接修改 Zend 库代码?我想我正在寻找一种简单的方法来做这样的事情:

+-- PHPUnit_Framework_TestCase
 |
 +-- My_PHPUnit_Framework_TestCase 
  |
  +-- Zend_Test_PHPUnit_ControllerTestCase
  +-- Zend_Test_PHPUnit_DatabaseTestCase

我想,作为替代方案,我可以在我的库中复制两个 Zend 类 - 但同样,每当上传 Zend 框架时可能会出现问题,需要从新的 Zend 测试用例类重写我的库。

【问题讨论】:

    标签: php zend-framework


    【解决方案1】:

    在 PHP 中,您有线性的单父继承。不幸的是,这意味着您不能在不修改 Zend 类的源代码的情况下插入中间代理类。

    如果有必要(例如,你有一些东西需要是两个不同 DO 类的子类),但是有一些方法可以伪造多重继承。

    使用魔术方法,例如,您可以创建多代理:

    class MultiProxy
    {
        private $poxies;
    
        public function __construct( array $arr )
        {
            $this->proxies = $arr;
        }
    
        public function __get( $name )
        {
            foreach( $this->proxies as $proxy )
            {
                if( property_exists( $proxy, $name ) )
                {
                    try
                    {
                        return $proxy->$name
                    }
                    catch( Exception e )
                    {
                        // swallow it.
                    }
                }
            }
            trigger_error( "$name not found on $this" );
        }
    
        public function __set( $name, $val )
        {
            foreach( $this->proxies as $proxy )
            {
                if( property_exists( $proxy, $name ) )
                {
                    try
                    {
                        $proxy->$name = $val;
                    }
                    catch( Exception e )
                    {
                        // swallow it.
                    }
                }
            }
            trigger_error( "$name not found on $this" );
        }
    
        public function __call( $name, $args )
        {
            foreach( $this->proxies as $proxy )
            {
                $proxy = array( $proxy, $name );
                if( is_callable( $proxy ) )
                {
                    try
                    {
                        call_user_func_array( $proxy, $args );
                    }
                    catch( Exception e )
                    {
                        // swallow it.
                    }
                }
            }
            trigger_error( "$name not found on $this" );
        }
    
        public function __toString()
        {
            $ret = "[" . get_class( $this ) . '::{';
            foreach( $this->proxies as $proxy )
            {
                $ret .= get_class( $proxy ) . ',';
            }
            return substr( $ret, 0, -1 ) .'}]';
        }
    }
    
    // use:
    class Foo{ public $a = 'FOO'; }
    class Bar{ public function doSomething(){ echo 'YAY!'; } }
    class Baz{ public $a = 'BAZ'; public function doSomething(){ echo 'BAAAZ!'; } }
    
    $foo = new Foo();
    $bar = new Bar(); // drink!
    $baz = new Baz();
    
    $multi = new MultiProxy( array( $foo, $bar, $baz ) );
    $multi->a = 'MULTI!';
    echo $multi->a == $foo->a? 1:0;
    $multi->doSomething();// YAY!
    

    【讨论】:

      【解决方案2】:

      不要耍花招:将您的通用代码重构为函数,或者更好地重构一个不从任何东西派生的类。然后只需在 Zend_Test_PHPUnit_ControllerTestCase 和 Zend_Test_PHPUnit_DatabaseTestCase 中使用这些函数或类(实例化,而不是作为基类)。

      复杂的继承是邪恶的。

      【讨论】:

      • "复杂的继承是邪恶的。"?为什么?
      • @Tadeck 封装是面向对象的一大优势。继承打破了封装。见en.wikipedia.org/wiki/Design_Patterns
      • 好来源(四人组,不是维基百科)。谢谢。
      猜你喜欢
      • 1970-01-01
      • 2020-12-15
      • 1970-01-01
      • 2014-07-31
      • 1970-01-01
      • 2018-06-21
      • 2014-01-26
      • 2022-08-03
      • 2018-01-12
      相关资源
      最近更新 更多