使用数据库驱动的应用程序示例将OOP理论与实践联系起来

its*_*inn 1 php oop doctrine-orm silex

我是OOP的新手,并且认为我会尝试在我正在尝试的小应用程序上试用Silex.我正在寻找一些关于我的设计是否符合良好的面向对象原则的建议.

我有一个User对象,基本上只是一堆属性,getter和setter.然后,我有一个UserService对象,其中包含用于验证用户,从数据库获取用户,设置或更新用户信息等的逻辑.我还有一个UserServiceProvder类,UserService用于向应用程序提供类的实例(似乎是在Silex中创建可重用的代码块的最佳方法.

我现在的问题是:我正在使用Silex附带的Doctrine DBAL,当我实例化UserService该类时,我很想传入对Doctrine对象的引用,然后硬编码调用该对象的方法.UserService类.

例如,返回一个User从数据库中的ID,我可能会创建一个名为方法getUserById($id),然后硬编码原则编制声明成的方法来从数据库中选择该用户,然后返回一个User对象.

我是否更好地创建一个完整的其他服务,它只是Doctrine DBAL的进一步抽象,并将其传递给UserService我实例化它?这样,我就可以将Doctrine准备好的语句硬编码到该类中,让我的UserService类更加封装和重用,以防我决定将来离开Doctrine.

我想我现在遇到的困难是意识到OOP是否存在过度杀戮这样的事情.在我看来,第二种方法更可重复使用,但这是必要的还是明智的?

hop*_*ppa 5

将数据库访问权限移动到单独的类将带来一些优势.首先,如果您将数据库访问与其余逻辑分开,则可以更轻松地替换数据库访问的实现.如果出于某种原因想要删除Doctrine DBAL,您会很高兴所有代码只是引用某个存储库的接口而不是直接查询数据库.

第二大优势是您可以在分离数据库访问逻辑时测试应用程序逻辑.如果为UserService中的用户注入一个Repository,你可以在测试中模拟它,并确保它们只有在实际应用程序逻辑出现问题时才会失败.

你可以做的一个小例子

该界面便于在整个代码库中引用.没有代码引用实现,只引用接口.这样,您可以轻松替换界面的实现,而无需触及它所使用的所有位置:

interface IUserRepository
{
  /**
   * @return User
   */
  public function getUserById($userId); 
}
Run Code Online (Sandbox Code Playgroud)

当然,您需要实现所述接口.这是您注入UserService的内容.这是你有一天可能会用接口的另一个实现取而代之的:

class DoctrineDBALUserRepository implements IUserRepository
{
  /**
   * @return User
   */
  public function getUserById($userId)
  {
    //implementation specific for Doctrine DBAL
  }
}
Run Code Online (Sandbox Code Playgroud)

UserService只知道接口并可以自由使用它.为了避免在代码中的很多地方注入UserRepository,您可以创建一个便利构建方法.请注意引用接口的构造函数和注入该接口实现的构建方法:

class UserService 
{
  private $UserRepository;

  public static build()
  {
    return new UserService(new DoctrineDBALUserRepository());
  }

  public function __construct(IUserRepository $UserRepository)
  {
    $this->UserRepository = $UserRepository;
  }

  public function getUserById($userId)
  {
    if ($User = $this->UserRepository->getUserById($userId) {
      return $User;
    }
    throw new RuntimeException('O noes, we messed up');
}
Run Code Online (Sandbox Code Playgroud)

有了这个,您可以编写业务逻辑的测试(例如,如果保存失败则抛出异常):

public function UserServiceTest extends PHPUnit_Framework_TestCase
{
  public function testGetUserById_whenRetrievingFails_shouldThrowAnException()
  {
    $RepositoryStub = $this->getMock('IUserRepository');
    $RepositoryStub->expects($this->any())->method('getUserById')->will($this->returnValue(false);

    $UserService = new UserService($RepositoryStub);
    $this->setExpectedException('RuntimeException');
    $UserService->getUserById(1);
  }
}
Run Code Online (Sandbox Code Playgroud)

如果你还没有进行单元测试,我可以想象你不熟悉最后一段代码.我希望你是,如果不是也敦促你也要阅读它:DI认为无论如何将答案的完整性包括在内是好的.