我的控制类基本上选择要实例化的对象/类。因为这基本上就是它所做的,所以它自然有许多它调用的对象/类。
如果我使用依赖注入,我将注入所有这些对象。这似乎很糟糕,原因有两个。
我听说大约 3 个依赖对象/类对 KISS 来说是正常的(保持简单聪明)
只会使用其中一个对象/类。因此,从某种意义上说,其他人无缘无故地被实例化。
我如何解决这些设计考虑以满足 - 解耦代码,但简单且使用的代码?
我的控制类基本上选择要实例化的对象/类。因为这基本上就是它所做的,所以它自然有许多它调用的对象/类。
如果我使用依赖注入,我将注入所有这些对象。这似乎很糟糕,原因有两个。
我听说大约 3 个依赖对象/类对 KISS 来说是正常的(保持简单聪明)
只会使用其中一个对象/类。因此,从某种意义上说,其他人无缘无故地被实例化。
我如何解决这些设计考虑以满足 - 解耦代码,但简单且使用的代码?
您所做的是实际上将某些参数映射到某些功能上,即所谓的script或action。
所以最后你只需要一个约定如何将该参数或名称映射到某个函数上。由于函数可以在某个地方(在某个其他对象中,在全局空间中,匿名),您实际上并不需要将许多对象注入到您的控件类中,而是函数和映射。
如果您甚至要使用函数名称和参数(或“模块”和“操作”)添加更多差异化,那么您可以大大减少代码,并且实际上可以使请求注入依赖项:
脚本或动作
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();
}
}
这只是示例性的。仅出于演示目的,无法保证这适合您的需求设计。它表明您的实际控制器功能不够规范化/模块化。当您更好地分析存在的依赖关系并注入它们而不是硬编码它们时,您将自动找到设计系统的最佳方法。
此示例还显示,您对请求和响应有大量隐藏的依赖项。您确实需要在某处画线并定义您经过的地方和方向。告别全局静态。总是注入。如果有帮助,您甚至可以从需要所有内容作为参数的函数开始。
解决了:
通过将依赖注入置于工厂模式 (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');
}
}
对象制造者
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;
}
}