2011-10-06 55 views
4

我的代码位于:https://github.com/maniator/SmallFry我应该从代码中移除静态函数吗?

我应该让这个使App类没有使用静态的功能,但在同一时间能够设置和从任何地方的应用程序设置变量?

或者我应该保持它如何与App::getApp::set方法?

两者的优点和缺点是什么?
如果我要进行第一项任务,我将如何完成这项任务?

Related Question

示例代码:

//DEFAULT TEMPLATE 
App::set('APP_NAME', 'SmallVC'); 
//END DEFAULT TEMPLAT 
// 
//DEFAULT TEMPLATE 
App::set('DEFAULT_TEMPLATE', 'default'); 
//END DEFAULT TEMPLATE 

//DEFAULT TITLE 
App::set('DEFAULT_TITLE', 'Small-VC'); 
//END DEFAULT TITLE 

//LOGIN SEED 
App::set('LOGIN_SEED', "lijfg98u5;jfd7hyf"); 
//END LOGIN SEED 

App::set('DEFAULT_CONTROLLER', 'AppController'); 

 if(App::get('view')){ 
      $template_file = $cwd.'/../view/'.App::get('view').'/'.App::get('method').'.stp'; 
      if(is_file($template_file)){ 
       include $template_file; 
      } 
      else { 
       include $cwd.'/../view/missingview.stp'; //no such view error 
      } 
     } 
     else { 
      App::set('template', 'blank'); 
      include $cwd.'/../view/missingfunction.stp'; //no such function error 
     } 
+2

您应至少给出一个小代码示例,其中您将使用要删除的静态函数。如果您将'$ app'对象传递到任何地方,您都可以访问它,例如获取和设置变量。那么你不需要全局(静态)函数或变量。那是你在找什么? – hakre

+0

@hakre代码示例在相关的问题中,并在GIT – Neal

+0

作为hakre说 - 小 – Paul

回答

6

我想你有一种感觉是静电不好。我发布的内容可能看起来很疯狂,因为它是一个巨大的变化。至少希望它呈现出对世界的不同想法。

MiškoHevery写道static methods are a death to testability

我喜欢测试,所以出于这个原因,我不使用它们。那么,我们还能如何解决这个问题呢?我喜欢用我认为是一种依赖注入来解决它。马丁福勒有一个很好但很复杂的文章here

对于构造中的每个对象,我传递它们操作所需的对象。从你的代码,我会做的AppController成为:

class AppController 
{ 
    protected $setup; 

    public function __construct(array $setup = array()) 
    { 
    $setup += array('App' => NULL, 'Database' => NULL); 

    if (!$setup['App'] instanceof App) 
    { 
     if (NULL !== $setup['App']) 
     { 
      throw new InvalidArgumentException('Not an App.'); 
     } 
     $setup['App'] = new App(); 
    } 

    // Same for Database. 

    // Avoid doing any more in the constructor if possible. 

    $this->setup = $setup; 
    } 

    public function otherFunction() 
    { 
     echo $this->setup['App']->get('view'); 
    } 
} 

的依赖关系默认为最有可能的值(默认的构造中的if语句)。所以,通常你不需要通过设置。然而,当你正在测试或者想要不同的功能时,你可以通过mock或者不同的类(派生自正确的基类)。您也可以使用接口作为选项。

编辑更纯粹的依赖注入形式涉及进一步的变化。它要求你总是传递所需的对象,而不是在对象未传递时让类缺省为一个对象。我在+ 20K LOC的代码库中经历了类似的更改。实施它之后,我发现整个过程都有很多好处。对象封装大大改进。它让你觉得你有真实的对象,而不是依赖于别的东西的每一点代码。

当您不注入所有依赖关系时抛出异常会导致您快速修复问题。通过在一些引导代码中使用set_exception_handler设置良好的系统范围异常处理程序,您将很容易看到异常并可以快速修复每个异常。然后,代码变得与在构造函数中检查成为AppController的简单:

 if (!$setup['App'] instanceof App) 
    { 
     throw new InvalidArgumentException('Not an App.'); 
    } 

随着每一个类,那么你写的所有对象将在初始化构造。另外,对于每个对象构造,您都可以传递所需的依赖关系(或者让您提供的默认对象)实例化。 (你会注意到当你忘记这么做的时候,因为在你测试之前你必须重写你的代码来取出依赖关系。)

看起来好像很多工作,但是这些类反映了真实世界更接近和测试变得轻而易举。您还可以在构造函数中轻松地看到代码中的依赖关系。

+0

我的意思是我猜而不是'$ this-> setup = $ setup'我甚至可以执行'$ this-> App = $ setup ['App']'和$ $ this-> Database = $ setup ['Database' ]' – Neal

+0

这看起来像是一个很好的答案。可能需要一些重要的重写。我会在哪里创建'App'对象?我开始在这里使用'App':https://github.com/maniator/SmallFry/blob/master/config/app_config.php – Neal

+1

数组作为依赖注入容器的形式。 – hakre

2

如果你不希望有static功能,但来自世界各地的global访问,而没有经过对象的地方,它是实际上需要那么你几乎只能使用一件事:

A global variable

所以你不是那么做的更好。但那是我能想到的唯一能满足你的要求的东西。

如果App对象是类似的应用程序配置第一个可能的步骤是将它传递给需要它的对象:

class Login { 
    public function __construct() { 
     $this->_login_seed = App::get('LOGIN_SEED'); 
     self::$_ms = Database::getConnection(); 
    } 

变为:

class Login { 
    public function __construct(App $app) { 
     $this->_login_seed = $app->get('LOGIN_SEED'); 
     self::$_ms = Database::getConnection(); 
    } 
+0

请参阅我的更新问题(对于混淆抱歉)。 看起来我希望**使用静态,但我已从各方被告知**不是** – Neal

+1

在很短的时间内(因为那种超出了这个问题的范围):你想要没有因为全局访问一切通常是一件坏事。你不能交换课程,因为你把所有的东西连接在一起。如果你想获得OOP的好处,你需要真实的物体。你不应该需要全局访问来自任何地方的所有东西,但是当你构造它们时,通常会再次将所有需要的对象传递给这些类。 – edorian

3

那么,如果是我,我的最终目标是将App依赖注入到需要它的任何类(或类树)中。这样在测试或重复使用代码时,您可以注入任何你想要的东西。

注意我说在那里重用。这是因为很难重用使用静态调用的代码。这是因为它与全局状态相关联,所以你不能真正“改变”子请求的状态(或者你想做的任何事情)。

现在,谈谈手头的问题。看起来你有一个遗留的代码库,这会使事情变得复杂。我想接近它的方法如下:

  1. 创建应用程序类的非静态版本(命名为东西现在不同),什么也不做,但代理其get/set调用到真正的应用程序类。因此,例如:

    class AppProxy { 
        public function set($value) { 
         return App::set($value); 
        } 
    } 
    

    现在,它所要做的只是代理。一旦我们完成所有代码与代理的交谈,而不是静态的应用程序,我们将使其实际发挥作用。但在此之前,这将使应用程序继续运行。这样,您就可以花时间实施这些步骤,而无需一蹴而就。

  2. 选择一个主类(一个为应用程序做了很多事情,或者很重要),您可以轻松地控制其实例化。最好只在一个地方实例化(在bootstrap中是最简单的)。通过构造函数更改该类以使用依赖注入来获取“appproxy”。

    a。测试这个!

  3. 根据您认为最重要和最容易的选择,选择另一个班级树进行工作。

    a。测试!!!

  4. 如果您有更多的App::来电,转至#3

  5. 改变现有App类是非静态的。

    a。测试!!!!!!!!!!

  6. 移除AppProxy并在依赖注入器中替换为App。如果你做得对,你应该只有一个地方来改变做这个开关。

  7. 拍拍自己的背部,去喝一杯,因为你完成了。

我把它分割出来的原因是,一旦一步完成(任何一步),你仍然可以运行工作软件。因此,这种转换可以从字面上花费数月(取决于您的代码库的大小),而无需中断业务照常...

现在,一旦你做,你得到一些显著的好处:

  1. 易于测试,因为您可以创建一个新的App对象来注入(或根据需要嘲笑它)。

  2. 副作用更容易看到,因为无论哪里都可以更改App对象。

  3. 它更容易组件化图书馆这种方式,因为其副作用是局部/

  4. 它很容易,如果它不是,如果是静态注入覆盖(多态性)的核心应用程序类。

我可以继续,但我认为找到资源为什么静态通常很糟糕是很容易的。所以这是我将用于从静态类迁移到实例的方法...

相关问题