Php 我应该从代码中删除静态函数吗?

Php 我应该从代码中删除静态函数吗?,php,class,static,Php,Class,Static,我的代码位于此处: 我是否应该使应用程序类不必使用静态函数,但同时能够从任何地方为应用程序设置和设置变量 或者我应该用App::get和App::set方法保持现在的状态吗 两者的优点和缺点是什么? 如果我要承担第一项任务,我将如何完成它 示例代码: //DEFAULT TEMPLATE App::set('APP_NAME', 'SmallVC'); //END DEFAULT TEMPLAT // //DEFAULT TEMPLATE App::set('DEFAULT_TEMPLATE'

我的代码位于此处:

我是否应该使
应用程序
类不必使用静态函数,但同时能够从任何地方为应用程序设置和设置变量

或者我应该用
App::get
App::set
方法保持现在的状态吗

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

示例代码:

//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');


如果您不想拥有
静态
功能,而是想从任何地方进行
全局
访问,而不想将对象传递到实际需要它的地方,那么您几乎只能使用一种方法:

A
全局变量

所以你做这件事并不是更好。但这是我能想到的唯一能满足你要求的东西

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

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

米什科·赫弗利写道

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

对于构建时的每个对象,我传递操作所需的对象。根据您的代码,我将使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.');
     }
然后,您编写的每个类的所有对象都将在初始化时构造。此外,对于对象的每次构造,您都会传递所需的依赖项(或者让您提供的默认依赖项)被实例化。(当您忘记执行此操作时,您会注意到,因为您必须重写代码以删除依赖项,然后才能对其进行测试。)


这似乎需要做很多工作,但这些类更贴近真实世界,测试变得轻而易举。您还可以在构造函数中轻松地看到代码中的依赖项

好吧,如果是我,我的最终目标是将
App
依赖项注入任何需要它的类(或类树)。这样,在测试或重用代码时,您就可以注入任何您想要的内容

注意我说的重用。这是因为很难重用包含静态调用的代码。这是因为它绑定到全局状态,所以您不能真正“更改”子请求(或任何您想做的事情)的状态

现在,我们来谈谈手头的问题。看起来您有一个遗留的代码库,这将使事情复杂化。我的做法如下:

  • 创建一个非静态版本的app类(暂时命名为另一个),它只会将get/set调用代理给真实的app类。例如:

    class AppProxy {
        public function set($value) {
            return App::set($value);
        }
    }
    
    现在,它所要做的就是代理。一旦我们完成了与代理(而不是静态应用)对话的所有代码,我们将使它真正起作用。但在此之前,这将保持应用程序运行。这样,您就可以花时间实施这些步骤,而无需在一次大扫除中完成所有操作

  • 选择一个可以轻松控制实例化的主类(一个对应用程序有很大作用或很重要的类)。最好是只在一个地方实例化(在引导中是最容易的)。将该类更改为通过构造函数使用依赖项注入来获取“appproxy”

    a。试试这个

  • 根据您认为最重要和最简单的内容,选择另一个要处理的类树

    a。测试

  • 如果您有更多呼叫
    应用程序::
    ,请转到#3

  • 将现有的
    App
    类更改为非静态

    a。测试

  • 卸下AppProxy并替换为附件中的App。如果你做得对,你应该只有一个地方可以改变,使这个开关

  • 拍拍自己的背,去喝一杯,因为你已经喝完了

  • 我把它分割成这样的原因是,一旦一个步骤完成(任何步骤),您仍然可以发布工作软件。因此,这种转换可能需要几个月的时间(取决于代码库的大小),而不会中断正常的业务

    现在,一旦完成,您将获得一些显著的好处:

  • 易于测试,因为您只需创建一个新的应用程序对象来注入(或根据需要模拟它)

  • 由于应用程序ob,副作用更容易看到
    class AppProxy {
        public function set($value) {
            return App::set($value);
        }
    }