允许类相互访问并自动实现这一点的最佳方式


Best way to allow classes to access eachother and have this happen automatically?

我将给出一个基本的例子,假设我有三个类——ConfigDatabaseUserUser需要DatabaseDatabase需要Config,但User可能也需要Config。所有的类都只需要一个实例,因为我只需要加载一次配置文件,只需要连接一次数据库,然后检查用户是否登录并只获取一次数据。现在,从每个类中判断需要哪些类并自动加载它们的最佳方法是什么?还是这种做法不好,我只是懒惰?我试着把所有的类都设为单独的类,并从每个类中这样访问它们:

User.php

$this->db = Module::get('Database');

但后来我不得不创建一个自定义的__construct()方法,每当我第一次创建一个类的新实例时都要运行它,这让我感到很不舒服。我做了一些研究,读到singletons并不是很好的实践,dependency-injection将是一个更好的解决方案,但我不知道如何使用dependency-injection实现这一点。所以我想知道,这是一个好主意吗?还是我应该重新思考我希望一切如何运作?

用户需要数据库,数据库需要配置,但用户可能也需要配置

听起来你需要注入依赖项。现有的软件包可以做到这一点,但如果你自己制作,你可以创建一个名为DIContainer的类,它的唯一目的是设置和获取对象实例,然后将这个类传递给其他可能需要它的类。

class DIContainer {
    protected $_objects = array();
    public function __set( $name='', $instance=null )
    {
        if( !empty( $name ) && is_object( $instance ) )
        {
            $this->_objects[ $name ] = $instance;
        }
    }
    public function __get( $name='' )
    {
        if( !empty( $name ) && array_key_exists( $name, $this->_objects ) )
        {
            return $this->_objects[ $name ];
        }
        throw new Exception( 'Object not found ('.$name.').' );
    }
}

然后只需实例化和设置您的公共类,并将它们添加到DIContainer:中

// setup config 
$config = new Config(); 
$config->loadFile( './conf.php' ); 
// setup db 
$database = new Database( $config ); 
$database->connect(); 
// setup session
$session = new Session( $config );
$session->start();  
// save dependencies 
$container = new DIContainer;
$container->config = $config;
$container->database = $database;
$container->session = $session;
// later when you start a new User class 
$user = new User( $container ); 

现在用户类可以得到它需要的东西,只需设置构造函数方法来处理DIContainer:

class User {
    protected $container = null;
    public function __construct( DIContainer $container )
    {
        $this->container = $container;
    }
    public function getRows()
    {
        $rows = $this->container->database->getRows( '...' );
    }
}

您正在寻找php中的自动加载。这个话题已经在SO上讨论过了,这里有一些很好的解释。

简而言之,在文件的开头(声明名称空间之后),您告诉php您的类依赖什么。

namespace Entity;
use ORM'Database;
use Config;
//...
$d = new Database();
Config::set('a', 'b');

在使用外部库和管理代码中的自动加载而不重新设计轮子时,您可能还想选择Composer for DI。

以下是Composer文档对autload:的评价

对于指定自动加载信息的库,Composer会生成vendor/autoload.php文件。您只需包含此文件将免费获得自动加载。

require __DIR__ . '/vendor/autoload.php';

这使得使用第三方代码变得非常容易。例如:如果您的项目依赖于Monolog,您可以从它,它们将被自动加载。

$log = new Monolog'Logger('name');
$log->pushHandler(new Monolog'Handler'StreamHandler('app.log', Monolog'Logger::WARNING));
$log->addWarning('Foo');

您甚至可以通过添加自动加载将自己的代码添加到自动加载器中字段到composer.json.

{
    "autoload": {
        "psr-4": {"Acme''": "src/"}
    }
}