我的控件类基本上选择要实例化的对象/类。因为这基本上就是它所做的事情,它自然会有许多它所调用的对象/类。
如果我使用依赖注入,我将注入所有这些对象。这看起来很糟糕有两个原因。
我听说大约3个依赖对象/类对KISS来说是正常的(保持简单Smarty)
只使用其中一个对象/类。所以在某种意义上,其他人无缘无故地被实例化。
如何解决这些设计注意事项以满足 - 解耦代码,但简单和使用的代码?
答案 0 :(得分:2)
您所做的是将某些参数实际映射到某些功能,即所谓的脚本或操作。
因此,最后您只需要一个约定如何将该参数或名称映射到某个函数。由于函数可以在某个地方(在某些其他对象中,在全局空间中,匿名),您实际上不需要将许多对象注入到控件类中,而是函数和映射。
如果你甚至比功能名称和参数(或“模块”和“动作”)更多地区分,那么,你可以大大减少你的代码,你可以实际让请求注入依赖:
脚本或操作
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类型。但是当你使用静态全局函数时,我会在类函数中简化它,因此实际上可以将依赖项传递给它。我将工厂放入全局函数,并从ini文件中加载ajax类型:
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);
}
}
和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();
}
}
这只是示范性的。无法保证这适合您的需求,仅用于演示目的。它显示的是您的实际控制器功能没有足够的标准化/模块化。当您更好地分析存在的依赖关系并注入它们而不是对它们进行硬编码时,您将自动找到设计 系统的最佳方法。
此示例显示的是,您有大量的请求和响应的隐藏依赖项。你真的需要在某处绘制线条并定义你通过的方向和方向。告别全局静态。总是注射。如果它有帮助,您甚至可以从需要所有内容的函数开始。
答案 1 :(得分:0)
解决:
通过将依赖注入放置在工厂模式(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;
}
}