情况
我目前有 4 种类型的用户,我们预计未来至少还会有 3 种。目前它们是:
- 管理员
- (商店管理员组)
- 员工(店长)
- 员工(商店销售员)
- 客户
在不久的将来,我必须允许两名员工同时成为客户。我还会有支持中心和记者。
问题所在
创造。创造。创造。我不担心访问控制,权限等...我现在拥有的代码可以在这方面创造奇迹。我的问题只是关于创造。似乎抽象工厂可能是适合我的一个,但事实是,所有那些使用书籍和汽车教授设计模式的"抽象教程"并没有帮助我把它带到我的情况。要么我使用了错误的设计模式,要么我不理解它。
我的尝试
在UserFactory类中,我们可以看到问题的根源:abstract public function signUp();
。这是一种不好的做法,甚至会导致 PHP 5.4+ 上的严格标准错误不尊重方法签名。在 Java 中,我会有方法重载来解决这个问题。在 PHP 中,方法重载的工作方式不同,不允许我以这种方式工作。
<?php
abstract class UserFactory {
const ADMIN = 'AdminRecord';
const MANAGER = 'ManagerRecord';
const SALESMAN = 'SalesmanRecord';
const CUSTOMER = 'CustomerRecord';
public static function manufacture($type) {
return new $type;
}
protected $accountController;
protected $emailController;
protected $toolMailer;
function __construct() {
$this->accountController = new AccountController();
$this->emailController = new EmailController();
$this->toolMailer = new ToolMailer();
}
abstract public function signUp();
}
这是我的第一个用例:创建一个新管理员。
class AdminRecord extends UserFactory {
protected $accountCompanyController;
function __construct() {
parent::__construct();
$this->accountCompanyController = new AccountCompanyController();
}
public function signUp($name, $email, $password, $companyId, $access) {
$accountId = $this->accountController->add($name, $password);
$this->emailController->add($email, $accountId);
$this->accountCompanyController->add($accountId, $companyId, $access);
$this->toolMailer->adminWelcome($name, $email, $password);
}
}
在这里,我创建了一个新的抽象类,因为我的两个用例属于同一个实体(销售员和经理都是具有不同访问级别的员工)。
abstract class StaffRecord extends UserFactory {
protected $staffController;
function __construct() {
parent::__construct();
$this->staffController = new staffController();
}
}
在这里,注册的签名将与管理员相同,这排除了使用 func_num_args()
和 func_get_args()
.等等,但是在Java中,您将无法使用方法重载来解决此问题。没错,但是在 Java 中,我可以用 Shop shop
替换int $shopId
,用 Company company
替换int $companyId
。
class ManagerRecord extends StaffRecord {
public function signUp($name, $email, $password, $shopId, $access) {
$accountId = $this->accountController->add($name, $password);
$this->emailController->add($email, $accountId);
$this->staffController->add($accountId, $shopId, $access);
$this->toolMailer->managerWelcome($name, $email, $password);
}
}
这里的注册方法与之前看到的两种情况都不同。
class SalesmanRecord extends StaffRecord {
public function signUp($name, $email, $password, $cpf, $shopId, $access) {
$accountId = $this->accountController->addSeller($name, $password, $cpf);
$this->emailController->add($email, $accountId);
$this->staffController->add($accountId, $shopId, $access);
$this->toolMailer->salesmanWelcome($name, $email, $password);
}
}
在这里,注册方法比以前更加不同。
class CustomerRecord extends UserFactory {
protected $customerController;
function __construct() {
parent::__construct();
$this->customerController = customerController();
}
public function signUp($name, $email, $password, $cpf, $phone, $birthday, $gender) {
$accountId = $this->accountController->addCustomer($name, $password, $cpf, $phone, $birthday, $gender);
$this->emailController->add($email, $accountId);
$this->toolMailer->customerWelcome($name, $email, $password);
}
}
这是我的实现:
我利用接口使注册函数为每个用户类型接受不同类型的参数;
创建的接口:
namespace main;
interface UserInterface { }
您可以添加需要按类实现的方法。现在,仅将其用作 signUp() 的类型提示对象;
通过在注册时使用类型提示(用户$user),它将解决您在注册中传递的不同类型的签名的问题。它可以是用户类型管理员、经理、销售员和客户。每个 {User}Record 都扩展和实现抽象工厂,但在实现上有所不同。
我假设对于每种用户类型都有一个相应的/唯一的行为。我添加了名为以下额外的类:AbstractUser.php,UserAdmin.php,UserManager.php,UserSalesman.php和UserCustomer.php。每个类将包含不同类型的用户和属性,但扩展了一个抽象类用户,该用户对于每个类(电子邮件、名称、密码)都是通用的;
抽象用户.php - 我注意到一个用户的共同属性,所以我创建了一个抽象用户。 通用属性(电子邮件、名称、密码)
<?php
namespace main;
abstract class AbstractUser {
public $email;
public $name;
public $password;
public function __construct($email, $name, $password) {
$this->email = $email;
$this->name = $name;
$this->password = $password;
}
}
让我们重写您的 UserFactory.php。但这一次,它包括我们构建的用户界面.php作为用户;
namespace main;
use main'UserInterface as User;
abstract class UserFactory {
const ADMIN = 'AdminRecord';
const MANAGER = 'ManagerRecord';
const SALESMAN = 'SalesmanRecord';
const CUSTOMER = 'CustomerRecord';
public static function manufacture($type) {
return new $type;
}
protected $accountController;
protected $emailController;
protected $toolMailer;
function __construct() {
$this->accountController = new 'stdClass();
$this->emailController = new 'stdClass();
$this->toolMailer = new 'stdClass();
}
abstract public function signUp(User $user);
}
请注意方法注册();我用创建的接口键入提示它,这意味着它只会接受具有 User 实例的对象用户(实现用户界面)。
我假设下一组代码是不言自明的:
用户管理员:
<?php
namespace main;
use main'AbstractUser;
class UserAdmin extends AbstractUser implements UserInterface {
public $companyId;
public $access;
public function __construct($email, $name, $password, $companyId) {
parent::__construct($email, $name, $password);
$this->companyId = $companyId;
$this->access = UserFactory::ADMIN;
}
}
AdminRecord: signUp(User $user) 应该只接受 UserAdmin 的实例.php
<?php
namespace main;
use main'UserFactory;
use main'UserInterface as User;
class AdminRecord extends UserFactory {
protected $accountCompanyController;
function __construct() {
parent::__construct();
$this->accountCompanyController = new 'stdClass(); //new AccountCompanyController();
}
public function signUp(User $user) {
$accountId = $this->accountController->add($user->name, $user->password);
$this->emailController->add($user->email, $accountId);
$this->accountCompanyController->add($accountId, $user->companyId, $user->access);
$this->toolMailer->adminWelcome($user->name, $user->email, $user->password);
}
}
让我们重写你的摘要 StaffRecord.php:(我认为没有变化)
<?php
namespace main;
use main'UserFactory;
abstract class StaffRecord extends UserFactory {
protected $staffController;
function __construct() {
parent::__construct();
$this->staffController = new 'stdClass(); //staffController
}
}
用户管理员:
<?php
namespace main;
use main'AbstractUser;
class UserManager extends AbstractUser implements UserInterface {
public $shopId;
public $access;
public function __construct($email, $name, $password, $shopId) {
parent::__construct($email, $name, $password);
$this->shopId = $shopId;
$this->access = UserFactory::MANAGER;
}
}
经理记录:
<?php
namespace main;
use main'StaffRecord;
use main'UserInterface as User;
class ManagerRecord extends StaffRecord {
public function signUp(User $user) {
$accountId = $this->accountController->add($user->name, $user->password);
$this->emailController->add($user->email, $accountId);
$this->staffController->add($accountId, $user->shopId, $user->access);
$this->toolMailer->managerWelcome($user->name, $user->email, $user->password);
}
}
用户业务员:
<?php
namespace main;
use main'AbstractUser;
class UserSalesman extends AbstractUser implements UserInterface {
public $cpf;
public $access;
public $shopId;
public function __construct($email, $name, $password, $cpf, $shopId) {
parent::__construct($email, $name, $password);
$this->shopId = $shopId;
$this->cpf = $cpf;
$this->access = UserFactory::SALESMAN;
}
}
业务员记录:
<?php
namespace main;
use main'StaffRecord;
use main'UserInterface as User;
class SalesmanRecord extends StaffRecord {
public function signUp(User $user) {
$accountId = $this->accountController->addSeller($user->name, $user->password, $user->cpf);
$this->emailController->add($user->email, $accountId);
$this->staffController->add($accountId, $user->shopId, $user->access);
$this->toolMailer->salesmanWelcome($user->name, $user->email, $user->password);
}
}
用户客户:
<?php
namespace main;
use main'AbstractUser;
class UserCustomer extends AbstractUser implements UserInterface {
public $cpf;
public $phone;
public $birthday;
public $gender;
public function __construct($email, $name, $password, $phone, $birthday, $gender) {
parent::__construct($email, $name, $password);
$this->phone = $phone;
$this->birthday = $birthday;
$this->gender = $gender;
$this->access = UserFactory::CUSTOMER;
}
}
客户记录:
<?php
namespace main;
use main'UserInterface;
use main'UserInterface as User;
class CustomerRecord extends UserFactory {
protected $customerController;
function __construct() {
parent::__construct();
$this->customerController = new 'stdClass(); //customerController
}
public function signUp(User $user) {
$accountId = $this->accountController->addCustomer($user->name, $user->password, $user->cpf, $user->phone, $user->birthday, $user->gender);
$this->emailController->add($user->email, $accountId);
$this->toolMailer->customerWelcome($user->name, $user->email, $user->password);
}
}
这就是我使用它的方式;
带加载器.php:
<?php
function __autoload($class)
{
$parts = explode('''', $class);
require end($parts) . '.php';
}
PHP主.php
<?php
namespace main;
include_once "loader.php";
use main'AdminRecord;
use main'UserAdmin;
use main'UserFactory;
use main'ManagerRecord;
use main'UserSalesman;
use main'CustomerRecord;
$userAdmin = new UserAdmin('francis@email.com', 'francis', 'test', 1);
$adminRecord = new AdminRecord($userAdmin);
$userManager = new UserManager('francis@email.com', 'francis', 'test', 1);
$managerRecord = new ManagerRecord($userManager);
$salesMan = new UserSalesman('francis@email.com', 'francis', 'test', 2, 1);
$salesmanRecord = new SalesmanRecord($salesMan);
//$email, $name, $password, $phone, $birthday, $gender
$customer = new UserCustomer('francis@email.com', 'francis', 'test', '0988-2293', '01-01-1984', 'Male');
$customerRecord = new CustomerRecord($customer);
print_r($adminRecord);
print_r($userManager);
print_r($salesMan);
print_r($salesmanRecord);
print_r($customer);
print_r($customerRecord);
下载文件: https://www.dropbox.com/sh/ggnplthw9tk1ms6/AACXa6-HyNXfJ_fw2vsLKhkIa?dl=0
我创建的解决方案并不完美,仍然需要重构和改进。
我希望这能解决你的问题。
谢谢。
我认为你错过了工厂的重点。 从工厂生成的类不会扩展工厂,工厂会大量生产并对象实例化适当的类或子类。 您可以扩展工厂以创建更具体的工厂,但这是一个不同的主题。
AdminRecord
、ManagerRecord
等应该扩展一个公共的基础抽象UserRecord
类,而不是UserRecordFactory
。 工厂只需构建适当的用户记录。
绕过不同签名的最简单方法是传入包含所需属性的选项对象或数组。 下面我展示了数组,但你可能想要一个 UserSignupConfig
类,它可以扩展为特定用途,如 AdminSignupConfig
并传入而不是泛型数组。
abstract class UserRecord {
public static function manufacture($type) {
return new $type;
}
protected $accountController;
protected $emailController;
protected $toolMailer;
function __construct() {
$this->accountController = new AccountController();
$this->emailController = new EmailController();
$this->toolMailer = new ToolMailer();
}
abstract public function signUp(array $config = array());
//or via optional object of type UserSignupConfig
// "abstract public function signUp(UserSignupConfig $config = null);"
}
在最基本的示例中,UserRecordFactory
可以有一个生成器方法来构造UserRecords
(或任何子类)。
//Create the factory
$userRecordFactory = new UserRecordFactory();
//Grab me something that extends UserRecord
$adminRecord = $userRecordFactory->churnOutUserRecord("AdminRecord");
churnOutUserRecord
方法可以是一个简单的开关:
public function churnOutUserRecord($type){
$record = null;
switch($type){
case "AdminRecord": $record = new AdminRecord(); break;
case "ManagerRecord": $record = new ManagerRecord(); break;
///...
}
return $record;
// ...Or just "return new $type;"
// if you are 100% sure all $types are valid classes
}
最后一点:抽象类的所有这些使用并不是我更喜欢的次要代码重用方式。 相反,我建议尽可能使用接口,但这是一个更深层次的主题。