如何实现工厂设计模式

时间:2015-08-21 21:18:24

标签: php oop design-patterns

我正在尝试将工厂设计模式应用于应用程序中的真实世界问题。我正在使用数据对象(有点像ORM但不完全),其中一个对象表示数据库中的一行,其属性是列。话虽如此 - 我有3种不同类型的用户,它们都扩展了基类User,并且都保存在users的同一数据库表中。我有一个额外的列type,用于确定用户的类型。

因此,要获取ID为1的用户,我将使用此代码:

$user = User::getByPK(1);

然而,不同类型的用户具有不同的属性和方法,我需要获取适当的实例,这是我被困的地方。我创建了一个工厂类,但我不太确定如何调用它。在确定用户的类型之前,我需要从数据库获取有关用户的信息,但是我需要实例化相应的类,这两个事物彼此依赖。

这是我做的 - 我得到了适当的实例,但用户的所有信息都保存在基类中。

class UserFactory {

    public function getUser($type) {

        switch($type) {
            case User::ORDINARY:
                return new OrdinaryUser();

            case User::MODERATOR:
                return new Moderator();

            case User::ADMINISTRATOR:
                return new Administrator();
        }
    }
}


$user = User::getByPK(1); // Has all the information

$factory = new UserFactory();

$object = $factory->getUser($user->type); // Is instance of Administrator

在这种情况下使用工厂模式的正确方法是什么?

1 个答案:

答案 0 :(得分:4)

工厂模式的想法是从对象本身解耦和封装创建对象实例所需的逻辑。这引起了人们的关注。

您目前在User类上有一个静态方法,负责从数据库中获取用户信息。这会将您的User对象耦合到您的数据库。它也很僵硬。

  • 如果您的用户存储在不同类型的数据库中会怎样?
  • 如果您的用户存储在XML文件中该怎么办?
  • 如果您的用户存储在键/值存储中会怎样?
  • 如果要创建用户编写测试的模拟存储库,该怎么办?

工厂模式允许您抽象出这些问题,并根据您的需要提供工厂的替代实现(假设您有每个实现的通用接口)。也许您创建用户的方式发生了变化,但您不希望在创建用户所需的任何地方进行更改 - 只需更改用户的创建方式。将此代码隔离到工厂允许这样做。

如果您抽象了DAL,工厂也可能会将一个实例带到您的DAL。 (我将使用IDataAccessLayer作为您首选DAL的占位符

class UserFactory {
    private IDataAccessLayer $dataAccessLayer;

    public function __constructor($dataAccessLayer) {
        $this->dataAccessLayer = $dataAccessLayer;
    }

    public function createUser($userId) {
        $userDataSet = $this->dataAccessLayer->someQuery($userId);

        switch ($userDataSet->type) {
            case UserType::Administrator:
                return createAdministrator($userDataSet);
            case UserType::Moderator:
                return createModerator($userDataSet);
            case UserType::Ordinary:
                return createOrdinaryUser($userDataSet);
        }
    }

    private function createAdministrator($userDataSet) { /* Create and return an administrator */ }
    private function createModerator($userDataSet) { /* Create and return a moderator */ }
    private function createOrdinaryUser($userDataSet) { /* Create and return an ordinary user */ }
}

然后你可以使用它:

$factory = new UserFactory($dataAccessLayer);
$factory->createUser(1);

(请耐心等待,我在几年内没有对PHP编码,所以我的一些语法可能会关闭,但想法是一样的)

现在,就个人而言,看起来你需要更加细化。我将在Repository模式之后创建一个UserRepository。这是因为我认为工厂不应该真正访问数据库。它应该只使用数据库中的数据来创建用户。存储库模式应负责从数据库获取数据,然后将该数据提供给工厂模式以获取对象。

class UserRepository {
    private IDataAccessLayer $dataAccessLayer;

    public function __constructor($dataAccessLayer) {
        $this->dataAccessLayer = $dataAccessLayer;
    }

    public function getUserById($id) {
        $userData = $this->dataAccessLayer->fetch(someQuery);

        $factory = new UserFactory();
        return $factory->createUser($userData);
    }
}

然后你会有一个更简单的工厂:

class UserFactory {
    public function createUser($userData) {
        switch ($userData->type) {
            case UserType::Administrator:
                return createAdministrator($userData);
            case UserType::Moderator:
                return createModerator($userData);
            case UserType::Ordinary:
                return createOrdinaryUser($userData);
        }
    }

    private function createAdministrator($userDataSet) { /* Create and return an administrator */ }
    private function createModerator($userDataSet) { /* Create and return a moderator */ }
    private function createOrdinaryUser($userDataSet) { /* Create and return an ordinary user */ }
}

从你的代码中,你会有类似的东西:

$userRepository = new UserRepository($dataAccessLayer); //This is probably done somewhere higher in your code.
$userRepository->getUserById(1);

您可能希望继续创建以下接口并实现这些接口。这将允许您强制执行合同,并使您自己能够在需要时更换实施。

public interface IUserRepository {
    function getUserById($userId);
}

public interface IUserFactory {
    function createUser($userData);
}