1

私はOOPを初めて使用し、Silexに試している小さなアプリを試してみようと思いました。私のデザインが優れたオブジェクト指向の原則に沿っているかどうかについて、いくつかのアドバイスを探しています。

User基本的に、プロパティ、ゲッター、セッターの集まりであるオブジェクトがあります。次に、UserServiceユーザーの認証、データベースからのユーザーの取得、ユーザー情報の設定または更新などのロジックを含むオブジェクトがあります。また、UserServiceProvderクラスのインスタンスをUserServiceアプリに提供するためのクラスもあります( Silexで再利用可能なコードのチャンクを作成するための最良の方法です)。

私が今持っている質問はこれです:私はSilexに同梱されているDoctrine DBALを使用しており、UserServiceクラスをインスタンス化するときに、Doctrineオブジェクトへの参照を渡し、そのオブジェクトへのハードコード呼び出しをUserServiceクラス。

たとえばUser、データベースからIDでを返すには、というメソッドを作成しgetUserById($id)、Doctrineのプリペアドステートメントをそのメソッドにハードコーディングして、データベースからそのユーザーを選択し、Userオブジェクトを返します。

Doctrine DBALをさらに抽象化した他のサービス全体を作成し、それUserServiceをインスタンス化するときに渡す方がよいでしょうか。そうすれば、DoctrineのプリペアドステートメントをそのクラスにハードコーディングしてUserService、将来Doctrineから離れることを決定した場合に備えて、クラスをよりカプセル化して再利用できるようにすることができます。

私が苦労しているのは、OOPにやり過ぎなどがあるかどうかを理解することだと思います。2番目の方法の方がはるかに再利用可能であるように私には思えますが、それは必要ですか、それとも賢明ですか?

4

1 に答える 1

5

データベース アクセスを別のクラスに移動すると、いくつかの利点が得られます。まず第一に、データベース アクセスを残りのロジックから分離しておくと、データベース アクセスの実装をより簡単に置き換えることができます。何らかの理由で Doctrine DBAL を削除したい場合は、すべてのコードがデータベースに直接クエリするのではなく、リポジトリへのインターフェイスを参照しているだけで十分です。

2 つ目の大きな利点は、データベース アクセス ロジックを分離してアプリケーション ロジックをテストできることです。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 に答える