PHPでトレイトを使ってみた

C#, Javaなど多重継承ができない言語では、複数のクラスをまとめて新たなクラスを作るのに多くの委譲コードを書く必要があります。PHPも多重継承はできません。しかし、version 5.4 からトレイトが導入されましたのでこれを使うと委譲コードを減らすことができます。

エンティティクラスへの応用

項目グループ 帳票1 帳票2 帳票3
A
B
C
D
E

3種類の帳票に対応するエンティティを定義するのにトレイトを使いました。

それぞれの帳票は多くの項目印刷項目があり、全ての帳票に印刷する項目もあれば、2帳票でのみ印刷する項目や1帳票でのみ印刷する項目もあります。各帳票の印刷項目は意味的なまとまりがあるものにグループ化することができ、帳票はこれらのグループを組み合わせたものになります。レイアウト上では項目の一つ一つは細い罫線で区切られていて、
グループは太い罫線で区切られています。
帳票とグループの関係は右表のようになっているとします。

もし継承だけでエンティティクラスを作成するのであれば、項目グループAに含まれる印刷項目のみを基本クラスに置き、項目グループB~Eに含まれる印刷項目はそれぞれ帳票1~3のクラスに置くことが考えられます。

この方法は下記の問題があります。

  • 2帳票で印刷する項目グループB, Cの実装が重複する
  • 項目グループのまとまりを表せない

これを解決するために、各項目グループA~Eをクラスとして、これらのクラスを使って帳票1~3のクラス(以後具象エンティティ)を作成することが考えられます。各帳票クラスの項目グループAを基本クラスとした場合、具象エンティティには項目グループB~Eに定義した印刷項目にアクセスする委譲コードを書く必要があります。委譲コードがあまりにも多いので、各項目グループA~Eをクラスにすることを諦めて最初の方法に戻ってしまいそうです。

1
2
3
4
5
6
7
8
9
10
11
class Report1 {
use A, B;
}

class Report2 {
use A, C, D, E;
}

class Report3 {
use A, B, C;
}

各項目グループA~Eをクラスではなくトレイトとした場合、委譲コードは不要となり無理なく実装できます。

リポジトリへの応用

リポジトリでは以下のようなさまざまな機能が必要となる場合があります。

  • データベースにアクセスする機能
  • データベース以外のストレージからアクセスする機能
  • リポジトリ内にデータをキャッシュする機能
  • キーを指定してエンティティを取得する機能
  • 抽出条件を指定してロードする機能

各エンティティクラスに対応するリポジトリ(以後具象リポジトリ)ではこれらの機能のすべてが必要になるのではなく、いくつかの機能のみが必要となります。その組み合わせはさまざまです。上記に挙げた機能をトレイトにしておけば、具象リポジトリはトレイトの組み合わせによって実装することができます。

実感したメリット

直接的には委譲コードを減らすことができるメリットがあります。それだけではなく下記のようなメリットが大きいと感じました。

  • トレイトはクラスに比べて粒度を細かくしやすいのでそれぞれの機能が明快になる。
  • まるでLEGOブロックを組むようにトレイトを組み合わせてクラスを作ることができる。
参照