繁体   English   中英

是否可以扩展对象的功能或将方法导入PHP中的对象?

[英]Is it possible to extend the functionality of an object or to Import methods into an object in PHP?

我面临着一个严重的设计问题,这使我发疯。 我认为只能通过多重继承或其他方式解决。 所以这是我想做的:

假设我以这种方式定义了一个称为OrdinaryUser的基本用户类:

class OrdinaryUser
{
 private $id,$name;

 public function __construct($id)
 {
  $this->id = $id;
  $this->name = fictionalDB::getUserNameById($id);
 }

 public function getName()
 {
  return $this->name;
 }

 public function getId()
 {
  return $this->id;
 }

}

我有一个名为AdminUser的子类,具有其他功能:

class AdminUser extends OrdinaryUser
{
 public function deleteUser($id)
 {
  echo "Deleting user where id=$id";
 }
}

问题 :如果我已经实例化了一个类型为“ OrdinaryUser”的对象并将其即时变为AdminUser对象,该怎么办? 有没有一种“扩展对象”的方法,以避免实例化子类,而不必用相同的数据重新填充新对象的字段?

另一个相关的问题 :以后可能定义了许多其他类别的用户,每种类别都有自己独特的行为,但始终是基本行为,在这种情况下创建层次结构没有任何意义,因为大多数情况下,一种类型的对象应该最好不要从另一种类型继承方法,尽管可能希望将一种类型的附加功能动态地“导入”到另一种类型。

目前无法在PHP中立即实现。 为了便于实现,这里有一些替代方案。

首先 ,如果您知道相关类之间的所有可能转换,则可以轻松地创建一个方法,该方法采用当前对象,填充新类的干净实例并返回它。 这就是您在原始问题中提到的想法。 这是您可以做的最简单,最安全的事情。

其次 ,如果您使用的是足够现代的PHP版本,则可以使用Serializable接口进行巧妙的操作。 如果实现该接口,则永远不会调用__sleep / __wakeup__sleep __wakeup也不会调用。 这意味着您可以以便宜的方式使用这些方法。 这是一些没有界面演示的愚蠢的演示代码:

[mcg@mcg-workstation ~]$ php -a
Interactive shell

php > class Foo { public $a; public $b; }
php > class Bar extends Foo { public $c; }
php > class Baz extends Foo { public $c; }
php > $one = new Bar();
php > $one_s = serialize($one);
php > echo $one_s;
O:3:"Bar":3:{s:1:"c";N;s:1:"a";N;s:1:"b";N;}
php > $one_s = explode(':', $one_s, 4);
php > print_r($one_s);
Array
(
    [0] => O
    [1] => 3
    [2] => "Bar"
    [3] => 3:{s:1:"c";N;s:1:"a";N;s:1:"b";N;}
)
php > $two_s = $one_s;
php > $two_s[1] = strlen('Baz'); $two_s[2] = '"Baz"';
php > $two_s = join(':', $two_s);
php > echo $two_s;
O:3:"Baz":3:{s:1:"c";N;s:1:"a";N;s:1:"b";N;}
php > $two = unserialize($two_s);
php > echo get_class($two);
Baz

如果您不遵循,则此代码将替换序列化数据中的类名称。 通过这样做,我将Bar转换为具有所有相同属性的Baz 仅当属性相同时,这才真正起作用。 我不确定如果属性不匹配,PHP将做什么,并且如果您实现Serializable,则您的serialize和unserialize方法将需要处理转换。

这也是一个巨大的漏洞,可能导致您的代码的未来维护者想要跟踪您并伤害您。 可能还会有轻微的性能损失。 如果最终使用它,请确保进行基准测试。 并雇用保镖。 或者至少确保将来的维护者不会成为能够找到您地址的谋杀性精神病患者。

第三 ,回到基于类的构造,而不是基于对象的构造:如果您可以等待PHP 5.4(或当前的主干最终成为现实),则可以在属性中放置匿名函数并像调用它们一样他们是方法。 尽管您可以在早期版本的属性中放置匿名函数,但至少从PHP 5.3起,这些函数不能引用$this ,因此作为对象的一部分是完全没有用的。 此限制也是导致您无法使用__call magic方法立即实现同一功能的原因。

第四 ,这是一个完全没有答案的方法,如果您想使用此类弯曲类体操,请考虑使用Ruby或Perl。 我认为Python可以做类似的事情,但是我还没有做过,也不能确定。 在面向对象方面,PHP并不是一种灵活的语言,内部人员名单上的人对将OO提升到其他语言的更有趣的标准没有兴趣。


关于您的相关问题,听起来您确实需要实例级特征。 PHP 5.4也具有特征,但在类级别,而不在实例级别。 有关此内容的评论,请参见#4。

我认为您的方案可能需要工厂模式

也许您本来不应该实例化OrdinaryUser的〜

听起来装饰器模式可能会帮助您

<?php
/*
   an interface to ensure we only decorate Users
   and possibly some of the most common methods that all users have
   so that we don't always suffer the overhead of the magic __call method
*/
interface User
{
    public function getId();
    public function getName();
}

class OrdinaryUser implements User
{
    private $id,$name;

    public function __construct($id)
    {
        $this->id = $id;
        $this->name = fictionalDB::getUserNameById($id);
    }

    public function getName()
    {
        return $this->name;
    }

    public function getId()
    {
        return $this->id;
    }

}

/*
   There aren't any abstract methods in this class
   but it is declared abstract because there is no point in instantiating one
*/
abstract class UserDecorator implements User
{
    protected $user;

    public function __construct( User $user )
    {
        $this->user = $user;
    }

    public function getId()
    {
        return $this->user->getId();
    }

    public function getName()
    {
        return $this->user->getName();
    }

    /*
       Any methods that aren't implemented by this type of
       user are dealt with by the decorated user
    */
    public function __call( $method, $arguments )
    {
        return call_user_func_array( array( $this->user, $method ), $arguments );
    }
}

class AdminUser extends UserDecorator
{
    /*
       Add any methods that are particular to this type of user
    */
    public function addedMethod()
    {
        // do AdminUser type stuff
        return "doing added method stuff\n";
    }

}

class FooUser extends UserDecorator
{
    public function foo()
    {
        // do some foo
        return "doing fooness\n";
    }
}

// just for testing
class fictionalDB
{
    public static function getUserNameById( $id )
    {
        $db = array(
            1 => 'Peter',
            2 => 'Paul'
        );
        return $db[$id];
    }
}


$user = new OrdinaryUser( 1 );
echo $user->getName();    // Peter

// make Peter into an AdminUser
$user = new AdminUser( $user );

// and also add some fooness
$user = new FooUser( $user );
echo $user->addedMethod(); // doing added method stuff
echo $user->foo();         // doing fooness

您描述的内容是不可能的,您是否考虑过其他方法? 如果您的User类在数组中“包含”了各种特权实现,该怎么办?

将其视为“是A”和“具有A”问题; 当AdminUser是管理员时,它具有特权,因此priv应当作为成员变量存储。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM