简体   繁体   English

如何实现工厂设计模式

[英]How to implement factory design pattern

I am trying to apply the factory design pattern to a real world problem in an application. 我正在尝试将工厂设计模式应用于应用程序中的实际问题。 I am using data objects (kind of like ORM but not quite) where an object represents a row from the database and its properties are the columns. 我正在使用数据对象(有点像ORM,但不完全是),其中一个对象代表数据库中的一行,其属性为列。 Having said this - I have 3 different types of users which all extend the base class User and are all kept in the same database table of users . 话虽如此-我有3种不同类型的用户,它们都扩展了基类User ,并且都保存在同一users数据库表中。 I have an extra column type that I use to determine the type of user. 我还有一个用于确定用户type的额外列type

So to fetch a user with id 1 I would use this code: 因此,要获取ID为1的用户,我将使用以下代码:

$user = User::getByPK(1);

However the different types of users have different properties and methods and I need to get the appropriate instance, which is where I am stuck. 但是,不同类型的用户具有不同的属性和方法,我需要获取适当的实例,这就是我遇到的问题。 I created a factory class but I am not quite sure how to call it. 我创建了一个工厂类,但是我不确定如何调用它。 I need to have the information about the user from the database before I can determine what type of user it is, however I need to instantiate the appropriate class and these two things are kind of dependant of each other. 在确定用户类型之前,我需要从数据库中获取有关该用户的信息,但是我需要实例化适当的类,并且这两件事相互依赖。

Here is what I made - I get the appropriate instance but all of the information for the user is kept in the base class. 这就是我所做的-我得到了适当的实例,但有关用户的所有信息都保留在基类中。

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

What is the proper way to use the factory pattern in this case? 在这种情况下,使用工厂模式的正确方法是什么?

The idea of the factory pattern is to decouple and encapsulate the logic required to create an instance of an object, from the object itself. 工厂模式的思想是从对象本身分离和封装创建对象实例所需的逻辑。 This separates concerns. 这将关注点分开。

You currently have a static method on your User class that is responsible for fetching the user information from the database. 当前,您的User类上有一个静态方法,该方法负责从数据库中获取用户信息。 This couples your User object to your database. 这会将您的User对象耦合到您的数据库。 It's also very rigid. 它也很僵化。

  • What if your users are stored in a different type of database? 如果您的用户存储在其他类型的数据库中怎么办?
  • What if your users are stored in an XML file? 如果您的用户存储在XML文件中怎么办?
  • What if your users are stored in a key/value store? 如果您的用户存储在键/值存储中怎么办?
  • What if you want to create a mock repository of users for writing tests? 如果要创建用于编写测试的用户模拟存储库怎么办?

The factory pattern allows you to abstract these concerns away, and provide alternate implementations of a factory depending on your needs (assuming you had a common interface for each implementation). 工厂模式使您可以将这些问题抽象化,并根据需要提供工厂的替代实现(假设每个实现都有一个通用的接口)。 Maybe the way you create a user changes, but you don't want to also change everywhere you've needed to create a user - just how the user is created. 也许创建用户的方式会发生变化,但是您也不想在创建用户所需的任何地方也进​​行更改-而是如何创建用户。 Isolating this code into a factory allows for this. 将此代码隔离到工厂中即可。

A factory might take an instance to your DAL as well, if you've abstracted your DAL. 如果您已经抽象了DAL,则工厂也可以将其作为DAL的实例。 (I'll be using IDataAccessLayer as a placeholder for your preferred 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 */ }
}

You'd then use this like: 然后,您可以这样使用:

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

(Bear with me, I haven't coded PHP in some years, so some of my syntax may be off, but the idea is the same) (和我在一起,我已经有几年没有编码PHP了,所以我的某些语法可能已经关闭,但是想法是一样的)

Now, personally, it looks like you need to get even more granular here. 现在,就个人而言,您似乎需要在这里进行更详细的介绍。 I'd create a UserRepository following the Repository pattern. 我将按照存储库模式创建一个UserRepository。 This is because the factory shouldn't really be accessing the database in my opinion. 我认为这是因为工厂实际上不应该访问数据库。 It should just use the data from the database to create the user. 它应该只使用数据库中的数据来创建用户。 The repository pattern should be responsible for getting the data from the database, and then feeding that data to the factory pattern to get an object. 存储库模式应该负责从数据库中获取数据,然后将数据馈送到工厂模式以获取对象。

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);
    }
}

Then you'd have a simpler factory: 然后,您将拥有一个更简单的工厂:

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 */ }
}

And from your code, you'd have something like: 从您的代码中,您将得到类似:

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

You'll probably want to go ahead and create the following interfaces and implement these as well. 您可能需要继续创建以下接口并也实现它们。 This will allow you to enforce your contract, as well as set yourself up to be able to swap out implementations if you need. 这将使您能够执行合同,并设置自己以能够根据需要交换实现。

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

and

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

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

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