我正在使用一个简单的项目来帮助我理解并使用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
}
}
答案 0 :(得分:1)
1)在哪里实例化数据源(可能是mysql,oracle甚至是一个普通的旧数组)并将其传递给需要它的对象(DI甚至是最好的模式)? 强>
使用DI,您将拥有一个单独的DI容器,用于处理控制器所需的依赖项实例。 DI是一个很好的选择,因为它将不再需要流程/控制语句(if / else,switch / case),这些语句必须弄清楚你连接的DB是什么,并使其易于单元测试。使用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容器可以作为前端控制器
希望这有帮助。