あなたがしていることは、実際にいくつかのパラメータをいくつかの機能、いわゆるスクリプトまたはアクションにマッピングすることです。
したがって、最終的には、そのパラメーターまたは名前を関数にマップする方法の規則だけが必要です。関数はどこかにある可能性があるため(他のオブジェクト、グローバルスペース、匿名)、実際には多くのオブジェクトをコントロールクラスに挿入する必要はありませんが、関数とマッピングを挿入する必要があります。
関数名とパラメーター(または「モジュール」と「アクション」)でさらに差別化を加える場合は、コードを大幅に削減し、実際にリクエストに依存関係を注入させることができます。
スクリプトまたはアクション
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);
}
この擬似コードは、コントロールクラスの実際のビジネスを示しています。入力に基づいていくつかの関数を呼び出します。具体的な依存関係は次のとおりです。
セッションは静的であるため、実際に注入できるものに置き換える必要があります。
配列と同様$map
に、注入することもできますが、マッピングのロジックをより内部的なものにする必要がある場合$map
があるため、がの場合ArrayAccess
、これはすでに発生している可能性があります。
非具体的な依存関係は実際の内部に隠されている$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);
}
}
そして、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();
}
}
これは単なる模範です。目的を示すためだけに、これがニーズの設計として適合するという保証はありません。それが示しているのは、実際のコントローラー関数が十分に正規化/モジュール化されていないことです。存在する依存関係をより適切に分析し、それらをハードコーディングする代わりにそれらを注入すると、システムを設計するための最良の方法が自動的に見つかります。
この例でも示されているのは、要求と応答に大量の非表示の依存関係があることです。本当にどこかに線を引き、何をどの方向に通過するかを定義する必要があります。グローバルな静的状態に別れを告げます。常に注入します。役立つ場合は、パラメーターとしてすべてを必要とする関数から始めることもできます。