шаблоны проектирования — AbstractFactory в PHP без перегрузки метода

Ситуация

В настоящее время у меня есть 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);
}

}

6

Решение

Вот моя реализация:

Я использую интерфейс для того, чтобы функция 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

Решение, которое я создал, не является идеальным и все еще нуждается в рефакторинге и улучшении.

Я надеюсь, что это решит вашу проблему.

Благодарю.

3

Другие решения

Я думаю, что вы упускаете из виду фабрику. Классы, которые вы генерируете из фабрики, не расширяют фабрику, фабрика производит и экземпляр объекта соответствующего класса или подкласса. Вы можете расширить фабрику, чтобы создать более конкретную фабрику, но это уже другая тема.

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

}

Последнее замечание: все это использование абстрактных классов — не то, что я предпочитаю для незначительного повторного использования кода. Вместо этого я рекомендовал бы по возможности использовать интерфейсы, но это более глубокая тема.

2

По вопросам рекламы [email protected]