Php 如何实施工厂设计模式
我试图将工厂设计模式应用于应用程序中的实际问题。我使用的是数据对象(有点像ORM,但不完全是),其中一个对象表示数据库中的一行,其属性是列。话虽如此,我有3种不同类型的用户,它们都扩展了基类Php 如何实施工厂设计模式,php,oop,design-patterns,Php,Oop,Design Patterns,我试图将工厂设计模式应用于应用程序中的实际问题。我使用的是数据对象(有点像ORM,但不完全是),其中一个对象表示数据库中的一行,其属性是列。话虽如此,我有3种不同类型的用户,它们都扩展了基类User,并且都保存在users的同一数据库表中。我有一个额外的列type,用于确定用户的类型 因此,要获取id为1的用户,我将使用以下代码: $user = User::getByPK(1); 然而,不同类型的用户具有不同的属性和方法,我需要获得适当的实例,这正是我遇到的问题。我创建了一个工厂类,但我不太
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
在这种情况下,使用工厂模式的正确方法是什么?工厂模式的思想是从对象本身分离和封装创建对象实例所需的逻辑。这就分离了关注点 您当前在
User
类上有一个静态方法,负责从数据库中获取用户信息。这将用户
对象耦合到数据库。它也很硬
- 如果您的用户存储在不同类型的数据库中怎么办
- 如果您的用户存储在一个XML文件中呢
- 如果您的用户存储在密钥/值存储中,该怎么办
- 如果您想创建一个用户的模拟存储库来编写测试,该怎么办
IDataAccessLayer
作为首选DAL的占位符)
然后,您可以这样使用:
$factory = new UserFactory($dataAccessLayer);
$factory->createUser(1);
(请记住,我已经有几年没有编写PHP了,所以我的一些语法可能不正确,但想法是一样的)
现在,就我个人而言,你需要在这里变得更细粒度。我将按照存储库模式创建一个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);
}
及
getUser()
是Repository
类的方法的好名字。工厂
类的方法名称通常以创建
开头,以更好地表达工厂
的功能:它创建新对象。如果有任何问题令人困惑、没有意义或没有答案,请告诉我,我会修改!不,这太棒了!我只是想知道一件事——因为大多数时候只有一种类型的对象——例如Note
。我有一个允许用户向自己发布注释的部分,我决定不会有其他类型的注释,只有Note
。在这种情况下,我认为不需要工厂,我可以将数据从网关直接提供给存储库中的对象构造函数,并让它自己填充。你同意吗?对不起,但我不明白-笔记类如何依赖于图书馆?另外,在进行了一次简短的测试并了解了这个“机制”之后,为了获得一个数据对象,我需要在我的控制器中实例化3个类——存储库、网关和工厂,我需要向它们传递一个db连接,而不是我过去所做的Model::byPK
。这真的是专业人士编程的方式吗?似乎太复杂了。q_q注意
依赖于提供包含数据库记录集的域模型的库。在PHP中,这可能没什么大不了的。然而,在一般编程中,这是您应该努力避免的<代码>注意是独立的,因为您说您要在对象上放置一个creator函数/构造函数,该对象需要一个类的实例,该实例只有在加载特定库时才可用。至于你对使用复杂模型的专业程序员的抨击,只有你才能决定你的项目是否需要使用设计模式。这个问题是专门关于设计模式的,所以我不知道你为什么现在放弃使用它们。如果您认为您的项目不够复杂,不需要设计模式,那么就不要使用它们。不过我会告诉你的。实现设计模式和可靠的原则可以使代码维护变得简单得多。在你开始使用它们之前,你根本无法理解它们所解决的问题。
public interface IUserRepository {
function getUserById($userId);
}
public interface IUserFactory {
function createUser($userData);
}