0

我对 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

不确定我喜欢混合函数/类,但它很简单。

想法?

4

3 回答 3

2

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

于 2012-05-02T00:11:17.410 回答
2

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

这是一篇使用 PHP 示例更详细地解释该模式的文章:http: //sourcemaking.com/design_patterns/abstract_factory/php/2

于 2012-05-02T03:47:56.147 回答
1

我想你都搞错了。

假设您要实现一个 class A。还有一个类B,它具有一些共同的属性,A但具有更多的功能。然后一堂课C,等等……

这是我的做法:

  • 找出你的类的最小共同属性
  • 将它们实现为 - 比方说 - 一个BasicItem类。(你甚至可能不使用这个类本身,而只是将它用于你的......“派生”类)
  • 将所有其他A, B, 任何类都建立在它之上(使用继承)。
于 2012-05-02T00:12:02.480 回答