1

我是 OOP 的新手,我想尝试一下我正在尝试的一个小应用程序。我正在寻找一些关于我的设计是否符合良好的面向对象原则的建议。

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

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

例如,要User通过 id 从数据库中返回 a,我可能会创建一个名为的方法getUserById($id),然后将 Doctrine 准备好的语句硬编码到该方法中,以从数据库中选择该用户,然后返回一个User对象。

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

我想我遇到的困难是意识到 OOP 中是否存在矫枉过正的东西。在我看来,第二种方法更可重用,但这是必要的还是明智的?

4

1 回答 1

5

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

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

你可以做什么的一个小例子

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

interface IUserRepository
{
  /**
   * @return User
   */
  public function getUserById($userId); 
}

当然,您确实需要实现所述接口。这就是您注入到 UserService 中的内容。这就是你有一天可能会用另一种接口实现来代替的东西:

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

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

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

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);
  }
}

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

于 2012-08-27T14:57:06.917 回答