2

我的控制类基本上选择要实例化的对象/类。因为这基本上就是它所做的,所以它自然有许多它调用的对象/类。

如果我使用依赖注入,我将注入所有这些对象。这似乎很糟糕,原因有两个。

  1. 我听说大约 3 个依赖对象/类对 KISS 来说是正常的(保持简单聪明)

  2. 只会使用其中一个对象/类。因此,从某种意义上说,其他人无缘无故地被实例化。

我如何解决这些设计考虑以满足 - 解耦代码,但简单且使用的代码?

4

2 回答 2

2

您所做的是实际上将某些参数映射到某些功能上,即所谓的scriptaction

所以最后你只需要一个约定如何将该参数或名称映射到某个函数上。由于函数可以在某个地方(在某个其他对象中,在全局空间中,匿名),您实际上并不需要将许多对象注入到您的控件类中,而是函数和映射。

如果您甚至要使用函数名称和参数(或“模块”和“操作”)添加更多差异化,那么您可以大大减少代码,并且实际上可以使请求注入依赖项:

脚本或动作

 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();
    }
}

这只是示例性的。仅出于演示目的,无法保证这适合您的需求设计。它表明您的实际控制器功能不够规范化/模块化。当您更好地分析存在的依赖关系并注入它们而不是硬编码它们时,您将自动找到设计系统的最佳方法

此示例还显示,您对请求和响应有大量隐藏的依赖项。您确实需要在某处画线并定义您经过的地方和方向。告别全局静态。总是注入。如果有帮助,您甚至可以从需要所有内容作为参数的函数开始。

于 2012-04-18T20:42:38.230 回答
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');
        }
    }

对象制造者

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;
    }
}
于 2012-04-18T17:54:48.593 回答