Upgrade to Pro — share decks privately, control downloads, hide ads and more …

traitは本当に悪者か?

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for akshimo akshimo
June 07, 2026
23

 traitは本当に悪者か?

Avatar for akshimo

akshimo

June 07, 2026

Transcript

  1. • 五十嵐 進士『WEB+DB PRESS Vol.130 PHPで複雑さに立ち向かう 第8回 トレイトでのコードの再利用とどう向き合うか』.技術評論社 • Jeremy

    Evans『研鑽Rubyプログラミング』.ラムダノート • meihei『改めて学ぶ Trait の使い方』 https://speakerdeck.com/meihei3/phpcon-odawara-2025 今回参考にさせていただいたもの
  2. PHP は、コードを再利用するための「トレイト」という仕組みを実装しています。 トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。 トレイトは、単一継承の制約を減らすために作られたもので、 いくつかのメソッド群を異なるク ラス階層にある独立したクラスで再利用できるようにします。 トレイトとクラスを組み合わせた 構文は複雑さを軽減させてくれ、 多重継承や

    Mixin に関連するありがちな問題を回避する こともできます。 トレイトはクラスと似ていますが、トレイトは単にいくつかの機能をまとめるためだけのもので す。 トレイト自身のインスタンスを作成することはできません。 昔ながらの継承に機能を加え て、振る舞いを水平方向で構成できるようになります。 つまり、継承しなくてもクラスのメン バーに追加できるようになります。 https://www.php.net/manual/ja/language.oop5.traits.php
  3. 単一継承 Class A method() Class C method() Class D method()

    Class B method() 水平方向の再利用
  4. trait Flyable { public function fly() { echo 'パタパタ'; }

    } class Hawk extends Bird { use Flyable; public function searchesForFood() { $this->fly(); … } } traitの例
  5. 暗黙的な依存 trait AddressFormattable { public function fullAddress(): string { //

    traitの利用側にこれらのプロパティがあることに依存している return $this->address1 . $this->address2 . $this->address3; } } class DeliveryAdress { use AddressFormattable; private string $address1; … }
  6. 不透明 class DeliveryAdress { use AddressFormattable; } $deliveryAddress = new

    DeliveryAdress(); // このメソッドはどこから来た? $deliveryAddress->fullAddress();
  7. 密結合 trait AddressFormattable { public function fullAddress(): string { //

    たとえprivateなプロパティだろうと、お互いに”壊す”ことが可能 $this->address1 = $this->address1 . $this->address2 . $this->address3; return $this->address1; } } class DeliveryAdress { use AddressFormattable; private string $address1; … }
  8. 名前衝突が (やや)わかりづらい trait PropertiesTrait { public $same = true; public

    $different1 = false; public bool $different2; public bool $different3; } class PropertiesExample { use PropertiesTrait; public $same = true; public $different1 = true; public string $different2; readonly protected bool $different3; }
  9. 名前衝突が (やや)わかりづらい trait PropertiesTrait { public $same = true; public

    $different1 = false; public bool $different2; public bool $different3; } class PropertiesExample { use PropertiesTrait; public $same = true; public $different1 = true; // Fatal error public string $different2; // Fatal error readonly protected bool $different3; // Fatal error }
  10. Concerns are also a helpful way of extracting a slice

    of model that doesn’t seem part of its essence (what is and isn’t in the essence of a model is a fuzzy line and a longer discussion) without going full-bore Single Responsibility Principle and running the risk of ballooning your object inventory. David Heinemeier Hansson『Put chubby models on a diet with concerns』 https://signalvnoise.com/posts/3372-put-chubby-models-on-a-diet-with-concerns
  11. trait Norifiable { public function notify(string $email): void { //

    通知処理 } } class OrderAction { use Norifiable; } できるなら DI・コンポジションの方が良い
  12. class Norifier { public function notify(string $email): void { //

    通知処理 } } class OrderAction { public function __construct (private Norifier $notifier) {} } できるなら DI・コンポジションの方が良い
  13. interface Norifiable { public function notify(string $email): void; } class

    Norifier implements Norifiable { // 通知処理の実装 } class OrderAction { public function __construct (private Norifiable $notifier) {} } できるなら DI・コンポジションの方が良い
  14. 値オブジェクトも良い class Address { public function __construct ( private string

    $address1, private string $address2, private string $address3 ) {} public function fullAddress(): string { return $this->address1 . $this->address2 . $this->address3; } }
  15. 暗黙的な依存 trait AddressFormattable { public function fullAddress(): void { //

    traitの利用側にこれらの変数があることに依存している return $this->address1 . $this->address2 . $this->address3; } } class DeliveryAdress { use AddressFormattable; … }
  16. trait AddressFormattable { public function fullAddress(): string { return $this->address1()

    . $this->address2() . $this->address3(); } abstract private function address1(): string; abstract private function address2(): string; abstract private function address3(): string; } 抽象メソッドを介する
  17. interface AddressFormatInterface { public function fullAddress(): string; } trait AddressFormatTrait

    { public function fullAddress(): string { return $this->address1() . $this->address2() . $this->address3(); } abstract private function address1(): string; abstract private function address2(): string; abstract private function address3(): string; } interfaceのdefault実装
  18. class Adress implements AddressFormatInterface { use AddressFormatTrait; public function fullAddress():

    string { // デフォルト以外の振る舞いをしたければオーバーライド } } interfaceのdefault実装