构造父类时设置对象的子类(动态子类)

时间:2012-05-12 17:15:00

标签: php oop

编辑:为了消除一些困惑,我实际上并没有使用MaleFemale。一个更好的例子是父类Animal和子类CatDog
我对OOP不熟悉,但到目前为止我已经很好地掌握了它。但是,有一点我可以找到一种方法。
首先,我创建了一个基于用户创建一个新对象。用户可以是(例如)男性或女性,但我无法检查他们是否没有检查数据库。然后我需要用他们性别的子类创建一个新的用户对象 例如,这是我当前(杂乱的)代码:

$user = new Person();
$userType = $user->GetUserType($userid);
if ($userType == 'Male') {
    $user = new Male($userid);
} else {
    $user = new Female($userid);
}

如果我想制作一个新物品,我必须每次都这样做,这是我不想要做的事情。
这里有一些代码可以帮助我们更好地理解这一点:

class Person {
function GetUserType($userid) {
        $db = mysqlConnect();
        $stmt = $db->stmt_init();
        // Get the players information
        $stmt->prepare("SELECT user_type FROM user_information WHERE user_id = ?");
        $stmt->bind_param('i', $userid);
        $stmt->execute();
        $stmt->bind_result($userType);
        $stmt->fetch();
        $stmt->close();
        $db->close();
        return $userType;
    }
}
class Male extends Person {
    // Male based things here
}
class Female extends Person {
    // Female based things here
}

因此,在__construct课程的Person中,我希望检查用户的性别并设置相应的子课程。

2 个答案:

答案 0 :(得分:2)

  

因此,在Person类的__construct中,我希望检查用户性别并设置相应的子类。

就像其他地方已经说过的那样,ctors不会回来,所以你不能这样做。并且ctors不应该查询数据库,因为that would be doing work。如果您不能/不希望通过DataMapperRepository立即创建正确的子类型,则可以使用State Pattern

  

允许对象在其内部状态更改时更改其行为。该对象似乎会改变其类。

这是Behavioral Patterns中的Gang Of Four book之一。显然,关键字就是这里的行为。状态模式不是关于变化的子类型(Cat,Dog),而是关于它们具有的行为的差异。因此我对你的问题发表评论。

实施例

interface GenderBehavior
{
    public function someBehavior();
    // can add more methods here
}

class FemalePersonBehavior implements GenderBehavior
{
    public function someBehavior()
    {
        // female specific behavior
    }
}

class MalePersonBehavior implements GenderBehavior
{
    public function someBehavior()
    {
        // male specific behavior
    }
}

class UnknownGenderBehavior implements GenderBehavior
{
    public function someBehavior()
    {
        // either implement a neutral behavior or
        throw new LogicException('Unknown Gender set');
    }
}

要创建这些实例,请添加Factory

class GenderBehaviorFactory
{
    public static function create($sex = null)
    {
        switch ($sex) {
           case 'male':
               return new MalePersonBehavior;
           case 'female':
               return new FemalePersonBehavior;
           default:
               return UnknownGenderBehavior;
        }
    }
}

然后将GenderBehavior接口添加到Person类,并将对接口方法的所有访问权限传递给Behavior实例:

class Person implements GenderBehavior
{
    private $behavior;

    public function __construct()
    {
        $this->behavior = GenderBehaviorFactory::create();
    }

    public function changeGender($sex)
    {
        $this->behavior = GenderBehaviorFactory::create($sex);
    }

    public function someBehavior()
    {
        $this->behavior->someBehavior();
    }
}

现在,当您第一次创建Person时,它将使用UnknownGenderBehavior进行实例化,如果您尝试调用someBehavior,则会引发异常。当您使用男性或女性作为参数调用changeGender时,将创建相应的GenderBehavior并将其设置为Person。然后你可以拨打someBehavior

答案 1 :(得分:0)

您无法选择从构造函数返回的内容,但您可以按原样采用层次结构(Person属于未知性别,MaleFemale已知)并使用类似的内容这种模式:

public class Person {
    public function ensureHasGender() {
        if ($this instanceof Male || $this instanceof Female) {
            return $this;
        }

        // now construct a Male or Female object from $this and return it
        $userType = $user->GetUserType($this->userid);
        return new $userType($this->userid);
    }
}

因此,您现在可以根据需要多次调用$user->ensureHasGender(),并且只会在第一次构建特定于性别的实例。

但是,我认为这个策略值得怀疑。分开MaleFemale课程可以获得什么?如果他们的行为如此不同,那么这可能是合理的,但这听起来不太可能。也许在延迟加载和适当条件的类中内部处理问题会更好。