1

我正在使用一个简单的项目来帮助我理解和实现带有 DI 的 MVC,以便为单元测试做准备。该站点将根据请求的页面显示不同的数据表。困难在于数据可能来自不同的数据源(例如 mysql 或 oracle),因此我正在尝试实现 DI 以减少在两者之间切换所需的代码。我的问题是这些:

1) 在哪里实例化数据源(可以是 mysql、oracle 甚至是一个普通的旧数组)并将其传递给需要它的对象(DI 甚至是最好的模式)?

2) 在事物结构中的什么位置放置特定于其来源的数据查询?根据下面的示例,我有一个类 Foo 查询 MySQL 数据库中的数据,但如果我稍后移至 Oracle 数据库,则必须将其全部重写为不同的语法。我是否只创建两个版本 FooMysql 和 FooOracle 并告诉某处要使用的东西?

这是一些基本的,不完整的代码,展示了我所看到的概念。希望您了解我的目标,并可以为我指明正确的方向。我不是在寻找已经构建好的框架。

$c = new Controller('foo');
print_r($c->getContent());

class Controller
{
    private $view = null;

    public function __construct($viewName)
    {
        $this->view = new $viewName($dbc); // where to get and pass dbc based on view
    }
}

interface ViewInterface
{
    public function getContent();
}

class FooView
{
    private $foo = null;

    public function __construct(DatabaseConnection &$dbc)
    {
        $this->foo = new Foo($dbc);
    }

    public function getContent()
    {
        return $this->foo->getAll();
    }
}

class BarView
{
    private $bar = null;

    public function __construct(DatabaseConnection &$dbc)
    {
        $this->bar = new Bar($dbc);
    }

    public function getContent()
    {
        return $this->bar->getAll();
    }
}

interface StorageInterface
{
    public function getAll();
}

class Foo implements StorageInterface
{
    private $dbc = null;

    public function __construct(DatabaseConnection &$dbc)
    {
        $this->dbc = $dbc;
    }

    public function getAll()
    {
        // mysql code to get all foos
    }
}

class Bar implements StorageInterface
{
    private $dbc = null;

    public function __construct(DatabaseConnection &$dbc)
    {
        $this->dbc = $dbc;
    }

    public function getAll()
    {
        // oracle code to get all bars
    }
}
4

1 回答 1

1

1) 在哪里实例化数据源(可以是 mysql、oracle 甚至是普通的旧数组)并将其传递给需要它的对象(DI 甚至是最好的模式)?

使用 DI,您将拥有一个单独的 DI 容器,用于处理创建控制器所需的依赖项实例。DI 是一个很好的选择,因为它将消除对必须确定您连接到哪个数据库的流程/控制语句(if/else、switch/case)的需要,并使其易于进行单元测试。使用 DI,您可以告诉对象这是您要连接的对象。

2) 在事物结构中的什么位置放置特定于其来源的数据查询?根据下面的示例,我有一个类 Foo 查询 MySQL 数据库中的数据,但如果我稍后移至 Oracle 数据库,则必须将其全部重写为不同的语法。我是否只创建两个版本 FooMysql 和 FooOracle 并告诉某处要使用的东西?

不,您将为数据库类创建一个接口。这样,当您将依赖项传递给控制器​​时,它将不知道数据库是什么类型。

前任。

public interface IDatabase {
    function connect();
    function query();
}

public class MySQLDB implements IDatabase {
    function connect() {
        // specific connection properties for mysql
    }
    function query() {
    }

}

public class PostgresDB implements IDatabase {
    function connect() {
        // specific connection properties for postgres
    }
    function query() {
    }
}

public class FooController {
    private $dataBase = null;

    public FooController(IDatabase $dataBase) {
        // guard clause
        if ($database == null)
            throw new Exception("IDatabase");

        $this->dataBase = $dataBase;
    }

    public index() {
        $this->dataBase->query();
    }
}

public class BarController {
    private $dataBase = null;

    public FooController(IDatabase $dataBase) {
        // guard clause
        if ($database == null)
            throw new Exception("IDatabase");

        $this->dataBase = $dataBase;
    }

    public index() {
        $this->dataBase->query();
    }
}

// will be hooked into your http request lifecycle
// to figure out what controller and even method is being called
// this will then return your composed controller
// we are newing up dependencies here, but they are all isolated in one location
// this way the rate of change among classes are not affecting each other
public class DIContainer {
    public resovleController() {
        // find out the http request info
        if (FooController) {
            return new FooController(new MySQLDB());
        } elseif (BarController) {
            return new FooController(new PostgresDB());
        }
    }
}

您的示例中还有一些代码异味,请尝试摆脱类的更新。
此外,视图不应该负责处理要传递的依赖项。
您肯定需要了解如何创建 DI 容器,以及在命名空间中组织代码。
您还需要创建一个前端控制器,您的 DI 容器将能够加载到该控制器中,或者 DI 容器可以是您的前端控制器

希望这可以帮助。

于 2013-06-13T14:08:40.347 回答