Мой класс управления в основном выбирает объект / класс для создания экземпляра. Поскольку это в основном то, что он делает, естественно, есть много объектов / классов, которые он вызывает.
Если я использую инъекцию зависимостей, я буду вводить все эти объекты. Это кажется плохим по двум причинам.
Я слышал, что около 3 зависимых объектов / классов являются нормальными для KISS (Keep it Simple Smarty)
Будет использоваться только один из объектов / классов. Поэтому в некотором смысле другие создаются без причины.
Как решить эти соображения дизайна для удовлетворения – развязанного кода, но простого и используемого кода?
Что вы делаете, так это то, что вы на самом деле сопоставляете некоторый параметр с некоторой функциональностью, так называемым скриптом или действием .
Поэтому в конце вам нужно только соглашение о том, как сопоставить этот параметр или имя с некоторой функцией. Поскольку функции могут быть где-то (в каком-либо другом объекте, в глобальном пространстве, анонимным), вам действительно не нужно вводить много объектов в ваш класс управления, а функции и отображение.
Если бы вы даже добавили еще больше различий с именем и параметрами функции (или «модулями» и «действиями»), то тогда вы могли бы значительно сократить свой код, и вы действительно можете заставить запрос вставить зависимость:
Сценарий или действие
Mapping: Actions: "*" "_.Exception.Invalid ajax_type" "signin_control" "A.SignIn.invoke" "signup_control" "A.SignUp.invoke" "tweet_control" "A.Tweet.add" "ControlBookmark_add" "A.Bookmark.add" "ControlBookmark_delete" "A.Bookmark.delete" "ControlTryIt" "B.ControlTryIt" "ControlSignOut" "C.SignOut" Implementation: $action = $map[isset($map[$ajax_type]) ? $ajax_type : '*']; Session::start(); call_user_func_array( 'call_user_func_array', explode('.', $action) + array(NULL, NULL, NULL) ); function _($a, $b) { throw new $a($b); } function A($a, $b) { $maker = new ObjectMaker(); $maker->$a()->$b(); } function B($a) { new $a(); } function C($a) { Session::finish(); B($a); }
Этот псевдокод показывает фактический бизнес вашего класса управления: вызовите некоторые функции на основе его ввода. Конкретные зависимости:
ObjectMaker
Session
$map
Поскольку сеанс статичен, вы должны заменить его тем, что действительно может быть введено.
Поскольку $map
является массивом, его можно вводить, но логика отображения может потребоваться стать чем-то более внутренним, поэтому, если $map
является ArrayAccess
, это может произойти уже.
$ajax_type
зависимости скрыты внутри фактического $ajax_type
, поэтому эти зависимости зависят от этого параметра через сопоставление, которое уже является зависимостью. Таким образом, последняя зависимость:
$ajax_type
Эта зависимость связана как с классом управления, так и с отображением. Таким образом, сам класс управления может быть также зависим от типа ajax. Но поскольку вы используете статическую глобальную функцию, я упрощу это внутри функции класса, так что в нее могут быть переданы зависимости. Я поместил фабрику в глобальную функцию, и типы ajax загружаются из ini-файла:
function ajax_control_factory($inifile) { $maker = new ObjectMaker(); $session = new SessionWrap(); $types = new AjaxTypesIni($inifile); return new AjaxControl($maker, $session, $types); } $control = ajax_control_factory($inifile); printf("Call an nonexistent ajax type: "); try { $control->invokeByType('blurb'); printf(" - I so failed!\n"); } catch (Exception $e) { printf("Exception caught! All good!\n"); } printf("Add me a bookmark: "); $control->invokeByType("ControlBookmark_add"); printf("Done! Fine! Superb this works!\n"); printf("Do the two control functions: "); $control->invokeByType("ControlTryIt"); $control->invokeByType("ControlSignOut"); printf("Done! Fine! Superb this works!\n");
Файл Ini:
* = _.Exception.Invalid ajax_type signin_control = A.SignIn.invoke signup_control = A.SignUp.invoke tweet_control = A.Tweet.add ControlBookmark_add = A.Bookmark.add ControlBookmark_delete = A.Bookmark.delete ControlTryIt = B.ControlTryIt ControlSignOut = C.SignOut
Чтобы иметь эту работу, для этого нужны некоторые заглушки для издевательств, что легко с вашим примером:
class Mock { public $stub; public function __call($name, $args) { return class_exists($this->stub) ? new $this->stub() : $this->stub; } } class ObjectMaker extends Mock { public $stub = 'Mock'; } class ControlTryIt {} class SignOut {} class SessionWrap { public function start() { // session::start(); } public function stop() { // session::finish(); } }
Этого достаточно, чтобы запустить код выше, который даст:
Call an nonexistent ajax type: Exception caught! All good! Add me a bookmark: Done! Fine! Superb this works! Do the two control functions: Done! Fine! Superb this works!
Типы ajax:
class AjaxTypes extends ArrayObject { private $default; private $types; public function __construct(array $types, $default) { parent::__construct($types); $this->default = $default; } public function offsetGet($index) { return parent::offsetExists($index) ? parent::offsetGet($index) : $this->default; } } class AjaxTypesIni extends AjaxTypes { public function __construct($inifile) { $map = parse_ini_file($inifile); if (!isset($map['*'])) throw new UnexpectedValueException('No * entry found.'); $default = $map['*']; unset($map['*']); parent::__construct($map, $default); } }
сclass AjaxTypes extends ArrayObject { private $default; private $types; public function __construct(array $types, $default) { parent::__construct($types); $this->default = $default; } public function offsetGet($index) { return parent::offsetExists($index) ? parent::offsetGet($index) : $this->default; } } class AjaxTypesIni extends AjaxTypes { public function __construct($inifile) { $map = parse_ini_file($inifile); if (!isset($map['*'])) throw new UnexpectedValueException('No * entry found.'); $default = $map['*']; unset($map['*']); parent::__construct($map, $default); } }
И контроллер ajax:
class AjaxControl { private $types; private $maker; private $session; public function __construct(ObjectMaker $maker, SessionWrap $session, AjaxTypes $types) { $this->types = $types; $this->maker = $maker; $this->session = $session; } public function invokeByType($type) { $session = $this->session; $maker = $this->maker; $invoke = function($action) use ($session, $maker) { $_ = function($a, $b) { throw new $a($b); }; $A = function($a, $b) use ($maker) { $maker->$a()->$b(); }; $B = function ($a) { new $a(); }; $C = function ($a) use ($B, $session) { $session->stop(); $B($a); }; $args = explode('.', $action) + array(NULL, NULL, NULL); $func = array_shift($args); call_user_func_array(${$func}, $args); }; $invoke($this->types[$type]); $this->session->start(); } }
Это просто образцово. Нет никакой гарантии, что это подходит как дизайн для ваших нужд, просто для демонстрации целей. Это показывает, что ваша фактическая функция контроллера не нормализована / модульная. Когда вы лучше анализируете зависимости, которые существуют, и вы вводите их вместо того, что вы их hardencode, вы автоматически найдете лучший способ разработки вашей системы.
Как показывает этот пример, у вас есть тонна скрытых зависимостей для запроса и ответа. Вам действительно нужно рисовать линии где-то и определять, что вы проходите и в каком направлении. Попрощайтесь с глобальным статическим состоянием. Всегда вводите. Вы даже можете начать с функции, которая нужна всем в качестве параметров, если это поможет.
Решено:
Поместив инъекцию зависимостей в фабричный шаблон (Object Maker), я могу вытащить все зависимости в одну зависимость – Object Maker – примечание ниже.
Управление PHP
class Control { public static function ajax($ajax_type) { Session::start(); switch($ajax_type) { case 'signin_control': // uses Message, Text, Database $Object = new ObjectMaker(); $ObjectSignIn=$Object->makeSignIn(); $ObjectSignIn->invoke(); break; case 'signup_control':// uses Message, Text, Database $Object = new ObjectMaker(); $ObjectSignUp=$Object->makeSignUp(); $ObjectSignUp->invoke(); break; case 'tweet_control':// uses Message, Text, Database $Object = new ObjectMaker(); $ObjectTweet=$Object->makeTweet(); $ObjectTweet->add(); break; case 'ControlBookmark_add': // uses Message, Text, Database $Object = new ObjectMaker(); $ObjectBookmark = $Object->makeBookmark(); $ObjectBookmark->add(); break; case 'ControlBookmark_delete':// uses Database $Object = new ObjectMaker(); $ObjectBookmark=$Object->makeBookmark(); $ObjectBookmark->delete(); break; case 'ControlTryIt': // Why Not Session new ControlTryIt(); break; case 'ControlSignOut': Session::finish(); new ControlSignOut(); break; default: throw new Exception('Invalid ajax_type'); } }
ObjecMaker
class ObjectMaker { public function makeSignUp() { $DatabaseObject = new Database(); $TextObject = new Text(); $MessageObject = new Message(); $SignUpObject = new ControlSignUp(); $SignUpObject->setObjects($DatabaseObject, $TextObject, $MessageObject); return $SignUpObject; } public function makeSignIn() { $DatabaseObject = new Database(); $TextObject = new Text(); $MessageObject = new Message(); $SignInObject = new ControlSignIn(); $SignInObject->setObjects($DatabaseObject, $TextObject, $MessageObject); return $SignInObject; } public function makeTweet( $DatabaseObject = NULL, $TextObject = NULL, $MessageObject = NULL ) { if( $DatabaseObject == 'small' ) { $DatabaseObject = new Database(); } else if( $DatabaseObject == NULL ) { $DatabaseObject = new Database(); $TextObject = new Text(); $MessageObject = new Message(); } $TweetObject = new ControlTweet(); $TweetObject->setObjects($DatabaseObject, $TextObject, $MessageObject); return $TweetObject; } public function makeBookmark( $DatabaseObject = NULL, $TextObject = NULL, $MessageObject = NULL ) { if( $DatabaseObject == 'small' ) { $DatabaseObject = new Database(); } else if( $DatabaseObject == NULL ) { $DatabaseObject = new Database(); $TextObject = new Text(); $MessageObject = new Message(); } $BookmarkObject = new ControlBookmark(); $BookmarkObject->setObjects($DatabaseObject,$TextObject,$MessageObject); return $BookmarkObject; } }