В настоящее время у меня есть 4 типа пользователей, и мы прогнозируем, по крайней мере, еще 3 в будущем. На данный момент они являются:
В ближайшее время мне придется разрешить обоим сотрудникам одновременно быть и клиентами. У меня также будет центр поддержки и репортер.
Создание. Создание. Создание. Я не беспокоюсь о контроле доступа, разрешении и т. Д. Код, который у меня сейчас есть, может творить чудеса в этой области. Моя проблема только в творчестве. И мне кажется, что Abstract Factory может быть для меня единственной, но правда в том, что все эти «абстрактные учебные пособия», обучающие шаблонам проектирования с использованием книг и автомобилей, просто не помогают мне привести его в мою ситуацию. Либо я ошибаюсь в шаблоне проектирования, либо не понимаю его.
В классе UserFactory мы можем видеть источник моей проблемы: abstract public function signUp();
, Это плохая практика и даже приводит к тому, что ошибка Strict Standard в 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();
}
}
Здесь подпись SignUp будет такой же, как и у администратора, что исключает возможность работы с func_num_args()
а также func_get_args()
, Подождите, но в Java вы не сможете использовать метод Overload для решения этой проблемы. Правда, но в Java я мог бы заменить int $shopId
с Shop shop
а также int $companyId
с Company company
,
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);
}
}
Здесь метод SignUp отличается от обоих случаев, замеченных ранее.
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);
}
}
Здесь метод SignUp еще более отличается от предыдущего.
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);
}
}
Вот моя реализация:
Я использую интерфейс для того, чтобы функция signUp принимала разные типы параметров для каждого типа пользователя;
Создан интерфейс:
namespace main;
interface UserInterface { }
Вы можете добавить метод, который должен быть реализован для каждого класса. На данный момент, просто используя это как объект подсказки типа для signUp ();
Использование подсказок по типу в signUp (User $ user) решит вашу проблему с различными типами подписей, передаваемых при регистрации. Это может быть администратор типа пользователя, менеджер, продавец и клиент. Каждая запись {User} расширяет и реализует абстрактную фабрику, но отличается по реализации.
Я предполагаю, что для каждого типа пользователя существует соответствующее / уникальное поведение. Я добавил дополнительные классы с именами: AbstractUser.php, UserAdmin.php, UserManager.php, UserSalesman.php и UserCustomer.php. Каждый класс будет содержать различные типы пользователей и атрибутов, но расширяет пользователя абстрактного класса, который является общим для каждого класса (адрес электронной почты, имя, пароль);
AbstractUser.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. Но на этот раз он включает в себя интерфейс, который мы создали UserInterface.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);
}
обратите внимание на метод signUp (); Я набираю подсказку с созданным интерфейсом, это означает, что он будет принимать только пользователя объекта с экземпляром User (реализует интерфейс пользователя).
Я предполагаю, что следующие наборы кодов не требуют пояснений:
UserAdmin:
<?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
}
}
UserManager:
<?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;
}
}
ManagerRecord:
<?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);
}
}
UserSalesman:
<?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;
}
}
SalesmanRecord:
<?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);
}
}
UserCustomer:
<?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;
}
}
CustomerRecord:
<?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);
}
}
Вот как я это использую;
с loader.php:
<?php
function __autoload($class)
{
$parts = explode('\\', $class);
require end($parts) . '.php';
}
php main.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('[email protected]', 'francis', 'test', 1);
$adminRecord = new AdminRecord($userAdmin);
$userManager = new UserManager('[email protected]', 'francis', 'test', 1);
$managerRecord = new ManagerRecord($userManager);
$salesMan = new UserSalesman('[email protected]', 'francis', 'test', 2, 1);
$salesmanRecord = new SalesmanRecord($salesMan);
//$email, $name, $password, $phone, $birthday, $gender
$customer = new UserCustomer('[email protected]', '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
}
Последнее замечание: все это использование абстрактных классов — не то, что я предпочитаю для незначительного повторного использования кода. Вместо этого я рекомендовал бы по возможности использовать интерфейсы, но это более глубокая тема.