【问题标题】:How to extend a PHP class within the class如何在类中扩展 PHP 类
【发布时间】:2012-05-02 00:03:25
【问题描述】:

我对 OOP 比较陌生,并且有一个关于如何最好地处理使用一个类名的问题,然后根据传递的参数在内部向该类添加功能。

基本故事是我有一个“项目”类,它接受 2 个参数 $module 和 $id。一些“模块”(但不是全部)将具有“项目”类默认不包含的奇特功能。一些模块还希望覆盖项目类中的一些方法。我的目标是能够从项目类的 __construct 中调用模块的“项目”类扩展。不过,我想要一个“项目”类。

我的第一个想法是在构建项目类时创建模块的项目扩展类的实例,然后,每当我调用项目类中的方法时,我都会检查是否有该模块特定的实例和调用该实例上的方法。

例如:

class item {
    // Variable where I'll store the instance of the child class
    var $child;
    // Child class may call up item's constuct, I'll use this to avoid an infinite loop
    var $child_initalized;
    // Child called, guess we have to keep track of this so we don't get stuck in infite loops either.
    var $child_called = 0;
    // Example variables
    var $id, $name;

    function __construct($module,$id) {
        if(!$this->child_initialized) {
            $this->child_initialized = 1; // So we don't do this again and get stuck in an infite loop
            $child_class = __CLASS__.'_'.$module;
            if(class_exists($child_class)) {
                $this->child = new $child_class($module,$id);
                return;
            }
        }

        // do normal class construction stuff here, such as setting $this->id and $this->name
        $this->module = $module;
        $this->id = $id;
    }

    // So we can catch child methods not in parent item class
    function __call($name,$arguments) {
        if($this->child) {
            return $this->child->$name();
        }
    }

    function id() {
        $this->child_called = 0;
        if($this->child) {
            $this->child_called = 1;
            return $this->child->id();
        }
        return $this->id;
    }
}

class item_photos extends item {
    function __construct($module,$id) {
        $this->child_initialized = 1;
        parent::__construct($module,$id);
    }

    function id() {
        return "photo-".$this->id;
    }

    function photo_width() {
        return 250; // Just some sample value
    }
}

$item = new item('photos',123);
print $item->id()."<br />"; // photo-123
print $item->photo_width()."<br />"; // 250

无论如何,这只是一个简单的例子。但是,我的方法似乎……嗯,错了。有什么更好的方法来实现这一点,还是我应该重新考虑如何构建所有内容?您能提供的任何建议或想法都会很棒。

/***********编辑 ***********/

澄清一下,我的目标实际上是能够调用一个类(在我的示例中为“项目”),并且基于传递给它的一个或多个参数,能够动态加载更多基于这些参数(在我的示例中,它是 $module)。但是这个额外的功能并不总是存在(所以我不能总是调用 new item_photos 而不是 new item())。

这是一个更新的示例。在代码末尾调用 $item = new item('videos',456) 是某些模块没有额外功能的示例。

class item {
    // Variable where I'll store the instance of the child class
    public $child;
    // Child class may call up item's constuct, I'll use this to avoid an infinite loop. Also to avoid when calling child class in methods.
    public $child_initalized;
    // Example variables
    public $module, $id, $name;

    function __construct($module,$id) {
        if(!$this->child_initialized) {
            $child_class = __CLASS__.'_'.$module;
            if(class_exists($child_class)) {
                $this->child = new $child_class($module,$id);
                return;
            }
        }

        // do normal class construction stuff here, such as setting $this->id and $this->name
        $this->module = $module;
        $this->id = $id;
    }

    // So we can catch child methods not in parent item class
    function __call($name,$arguments) {
        if($this->child) {
            return $this->child->$name();
        }
    }

    function id() {
        $this->child_initalized = 0;
        if($this->child) {
            $this->child_initalized = 1;
            return $this->child->id();
        }
        return $this->id;
    }
}

class item_photos extends item {
    function __construct($module,$id) {
        $this->child_initialized = 1;
        parent::__construct($module,$id);
    }

    function id() {
        return "photo-".$this->id;
    }

    function photo_width() {
        return 250; // Just some sample value
    }
}

$item = new item('photos',123);
print "photo id: ".$item->id()."<br />"; // photo-123
print "photo size: ".$item->photo_width()."<br />"; // 250

$item = new item('videos',456);
print "video id: ".$item->id()."<br />"; // 456
print "video size: ".$item->photo_width()."<br />"; // nothing (that method doesn't exist here)

/********** 编辑2 **********/

仍然有点挣扎。我研究了抽象工厂,它们似乎不起作用。如果我理解正确,我必须在抽象类中声明所有方法,这很棘手,因为模块会扩展项目类,所以我不会知道所有方法。另外,我似乎不得不调用“item_photos”或“item_videos”,而不仅仅是“item”,这是我试图避免的。更不用说在我的示例中 item_videos 甚至不存在。

鉴于我的目标是调用一个类,然后在该类 __construct 中,根据 $module 确定是否需要包含任何额外的功能,我想出了这个。

基本上什么是“item”类被移动到一个新的“item_core”。旧的项目类现在只确定使用哪个类,或者模块的项目类(如果存在)(例如:item_photos)或核心项目类(item_core)。在初始化其中一个类后,我存储实例并在调用方法时使用它(通过 __call,让我知道为什么使用 __call 不好)。

class item {
    // Instance of the core class or module class
    private $instance;

    function __construct($module,$id) {
        $class = __CLASS__;
        $class_core = $class.'_core';
        $class_module = $class.'_'.$module;

        // Module class
        if(class_exists($class_module)) $this->instance = new $class_module($module,$id);

        // Core class
        else $this->instance = new $class_core($module,$id);
    }
    // Catch all calls, redirect accordingly
    function __call($name,$arguments) {
        if($this->instance) {
            if(method_exists($this->instance,$name)) return $this->instance->$name();
        }
    }
}
class item_core {
    public $module, $id;

    function __construct($module,$id) {
        $this->module = $module;
        $this->id = $id;
    }

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

class item_photos extends item_core {
    function __construct($module,$id) {
        parent::__construct($module,$id);
    }

    function id() {
        return "photo-".$this->id;
    }

    function photo_width() {
        return 250; // Just some sample value
    }
}

$item = new item('photos',123);
print "photo id: ".$item->id()."<br />"; // photo-123
print "photo size: ".$item->photo_width()."<br />"; // 250

$item = new item('videos',456);
print "video id: ".$item->id()."<br />"; // 456
print "video size: ".$item->photo_width()."<br />"; // nothing (that method doesn't exist in the videos module)

有人发现这有什么问题或无论如何要改进它吗?对我来说还是有点不对劲。

/**** 编辑 3 *******强>/

好吧,在我看来,我想做的事情是能够返回与我调用的不同的实例。因此,调用 new item($module,$id);,但如果 $module == photos(在我的示例中)我想返回 new item_photos($module,$id); 的实例。到目前为止,我提出了两种可能的解决方案。

首先是我在编辑 2 中概述的内容(见上文)。

第二个选项是简单地调用一个函数(可能是'item')并在那里返回正确的对象实例(因为函数可以返回您想要的任何对象实例,而类构造函数只返回它自己的类的实例)。所以(基本示例):

function item($module,$id) {
    $class_module = 'item_'.$module;
    // Module class
    if(class_exists($class_module)) return new $class_module($module,$id);
    // Core class
    else return new item($module,$id);
}
class item{..}
class item_photos{..}

$item = item('photos',123);
print $item->id(); // photo-123

不确定我是否喜欢混合函数/类,但它的作用非常简单。

想法?

【问题讨论】:

  • 查阅 SOLID 原则和敏捷编程。
  • 快速浏览了一下,感谢您的建议。看来在我继续学习这两个方面对我来说是个好主意。

标签: php oop class extend internal


【解决方案1】:

看起来您尝试实现的内容可以通过工厂设计模式来实现。简而言之,当您想要传入一堆配置选项并获取代表您想要使用的对象的东西时,这种模式是很好的。与其尝试从内部更改您的 Item 类,不如创建一个返回新对象的 ItemFactory 类。例如,您向工厂索要一张照片或视频,它会返回一个 Photo 类或一个 Video 类(从通用 Item 类扩展而来),其中只有它需要的东西,没有多余的东西。

这里有一篇文章通过 PHP 示例更详细地解释了该模式:http://sourcemaking.com/design_patterns/abstract_factory/php/2

【讨论】:

  • 采用了一种工厂设计。它出现在工厂设计中,我必须创建工厂实例,然后调用工厂上的方法以返回正确项目实例(项目或项目照片等)的实例,这是我希望避免的额外步骤.请参阅我的“编辑 2”和“编辑 3”,了解我提出的 2 个不同版本,它们避免了 2 次调用,但并不是最迷人的。我将测试每个的速度并选择哪个更快。感谢安德鲁(和所有人)的帮助。
  • 很高兴我能帮上忙。 =] 您可能对使用静态方法返回实例感兴趣。像ItemFactory::getInstance('photo') 这样的东西意味着你不需要ItemFactory 的实例。但是很容易开始过度使用静态方法,所以要明智地使用它们。 =]
【解决方案2】:

您应该在外部创建子类并将其注入item 类,而不是在内部创建子类(依赖注入是您可能想要查找的东西)。您可能希望在控制器中使用工厂来执行此操作。 __call的使用也有点黑,破坏了界面。我不确定您为什么需要围绕 item_photos 进行一定程度的抽象……您可以只使用该对象本身。

【讨论】:

  • 谢谢,我来看看依赖注入。简单地调用 new item_photos 可能会更好,但并非所有模块都会有扩展“item”的类。此外,其他几位程序员将致力于此,坦率地说,我不确定他们是否理解他们有时必须使用 item() (如果该模块没有扩展类)并使用 item_photos (或其他任何模块)。
【解决方案3】:

我认为你完全错了。

假设您要实现一个类A。还有一个类B 与A 共享一些共同属性,但具有更多功能。然后一个班级C,等等……

我会这样做:

  • 找出类的最少通用属性
  • 将它们实现为 - 比如说 - BasicItem 类。 (你甚至可能不使用这个类本身,而只是将它用于你的......“派生”类)
  • 基于所有其他 A、B,以及在此之上的任何类(使用继承)。

【讨论】:

  • 我同意这个答案,您应该在包含所有常用方法的“母”类之上使用继承。
  • 继承的问题(如果我理解“继承”意味着使用“扩展”)是某些模块没有额外的功能,因此不会有扩展“项目”的类(请参阅对 $item = new item('videos',456); 我在代码末尾添加的调用)。当然,我可以要求我或其他程序员制作的任何模块都需要有一个扩展 item 的类,即使 if 什么都不做,但这似乎有点奇怪。
  • @Charlie 如果你需要创建一个 class 的 instance,除了你的“母亲”(又名@ 987654328@) 类,为什么不使用你的“母亲”类本身呢? (老实说,我认为您对类和继承感到困惑;通常,当您尝试实现似乎几乎不可能的奇怪复杂的东西时,这仅仅是因为您的看法是错误的) .
  • 感谢 Dr.Kameleon 的评论,我可能认为可能是错误的。然而,问题不在于我不需要额外的功能。有时我不需要额外的功能,有时我需要,这取决于传递给类的参数(具体的 $module 参数)。我的目标是能够使用一个类,并且如果有额外的功能可用(由 $module 参数确定),则能够向该类添加功能。
  • @Charlie 为什么使用 1 类(并使用 $module 参数进行修改)而不使用 2? (听起来是不是太荒谬了?)
猜你喜欢
  • 2015-11-03
  • 2012-06-27
  • 2012-07-31
  • 2013-02-06
  • 2017-01-09
  • 2016-05-09
  • 1970-01-01
相关资源
最近更新 更多