Advertisement
Advertisement

More Related Content

Slideshows for you(20)

Similar to 導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について(20)

Advertisement

導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について

  1. 導入から10 年、PHP のtrait は滅びるべきなのか その適切な使いどころと弱点、将来について 五十嵐進士/ sji / sj-i / @sji_ch
  2. 自己紹介 @sji_ch SNS 上でのアイコンはGitHub が自動生成した奴
  3. 生まれも育ちも仙台
  4. PHP カンファレンス仙台とかやった
  5. ふつうのサラリーマン 株式会社インフィニットループ仙台支社所属 スマホゲーのサーバサイドプログラマ
  6. 二歳児の父 かわいい 絵本好き 最近わりと会話が成立する
  7. WEB+DB PRESS の現PHP 連載担当 昨年6 月からWEB+DB PRESS のPHP 連載 だいたいわりと真面目な話をしてる
  8. Agenda trait とは trait の性質 trait の使いどころ trait の将来
  9. その前に
  10. PHP 8.2 に日本人が乱数拡張入れてたよ!
  11. twitter のTL にRFC の話とphpcon の話と両方同時にでてきた
  12. そうだ締め切り駆動でPHP のRFC も投げよう??
  13. よしTrait で定数定義できるようにしたろ!
  14. 議論フェーズに目立った反対もない、これは勝つる、投票開始や
  15. ちょ待てよ
  16. ちょ
  17. 投票が始まってから吹き上がる反対
  18. なんやかんやあって結局PHP 8.2 への取り込みはされました!
  19. trait とは
  20. trait ってこういうやつ 2012 年リリースのPHP 5.4 で導入 クラスへ追加するメンバーを切り出して定義 プロパティ、メソッド、定数(8.2から)を定義可能 クラスやtrait、Enum からuse use 先へコピペされたよう振る舞う 直接インスタンスを作れない 複数trait を同時にuse できる 型宣言に使えない trait T { // トレイトの定義 public string $property = self::class; public function method(): void { echo $this->property, PHP_EOL; } } class C { use T; // トレイトの利用 }
  21. trait の来歴
  22. もともとPHP 専用とかではない 2002 年にNathanael Schärli らがECOOP2002 向けに出した論文で提案 いくつかの言語でアイディアが取り入れられた https://www.cs.cmu.edu/~aldrich/courses/819/Scha03aTraits.pdf
  23. 単一継承と多重継承 単一継承: 一つの親クラスしか持てない 多重継承: 複数の親クラスを持てる それぞれ別の面倒くささがある
  24. 多重継承の反省からtraitが生まれた
  25. ちなみに当初論文の案ではtraitは状態を持たなかった 多重継承は状態の衝突への対応が難しいので、あえて外されてた プロパティが定義できるのはPHP が独自に入れた変更 元論文ではtrait がabstract で実装クラス側へアクセッサ提供を要求する形を提案 それだとボイラープレートが増えるので辛いとはみんな思ってた アカデミアのほうでもStateful Traits のような拡張が後追いで提案された https://link.springer.com/chapter/10.1007/978-3-540-71836-9_4
  26. trait の性質
  27. ソフトウェアの部品が満たすべき性質: 品質について trait はクラスの部品 クラスはプログラムの部品 プログラムは部品を組み合わせて作る 良いプログラムは良い部品の良い組み合わせ方で作られる 良いといっても基準は色々 合目的性と言い換えるとスッキリ? 良さは品質とも呼ばれる
  28. 品質とは バートランド・メイヤーさんの『オブジェクト指向入門第2 版原則・コンセプト』(訳:酒匂寛さん)は品質とはなんぞ やで始まる 品質: 外的要因+ 内的要因 外的要因: 性能とか使いやすさとか、ユーザが認識する部分 内的要因: 外的要因の達成に必要な専門家だけが見る部分 達成すべきは目標は外的要因
  29. 品質の外的要因 正確さ 頑丈さ 拡張性 再利用性 互換性 効率性 可搬性 使いやすさ 機能性 適時性 そのほか(実証性、統合性、修復性、経済性)
  30. 品質要因のトレードオフ 機能を増やそうとしたらお金がかかるとか バランスをとる必要がある
  31. 特に気にする部分 信頼性 正確さ 頑丈さ モジュール性 拡張性 再利用性
  32. 正確さ 仕様で定義されているとおりに仕事を実行するソフトウェア製品の能力 ちゃんと動かないと全部が台無し 仕様を厳密に記述するというのがまず難しい 仕様への合致度を検証する方法も必要 小分けに関心を分離して「この範囲では正しい」という保証を重ねる テストとか型とかアサーションとか
  33. 頑丈さ 異常な条件に対して適切に対応するソフトウェアシステムの能力 仕様に書かれてないからって鼻から悪魔が出ていいわけがない エラー吐いてさっさと行儀よく終了してくれるほうが嬉しかったり
  34. 拡張性 仕様の変更に対するソフトウェア製品の適応のしやすさ 自然のなりゆきでは規模が大きくなるほど修正の影響を見積もりづらくなる より設計を単純に より構成する部品それぞれの独立性を高く
  35. 再利用性 多数多様なアプリケーションの構築に使うことのできる、ソフトウェア要素の能力 コードが減ると他の品質にコストを使えるようにもなる
  36. 5 つの基準、5 つの規則、5 つの原則 『オブジェクト指向入門』の重点はモジュール性(拡張性と再利用性) モジュール性についての5 * 3 の15 の確認事項
  37. 5 つの基準(criteria) 部品の設計手法に求められるもの 分解しやすさ 組み合わせやすさ 分かりやすさ 連続性 保護性
  38. 5 つの規則(rule) 5 つの基準から導き出されるもの 部品の良さを保証するために守らねばならないルール 問題領域の直接的な写像であること 少ないインタフェース 小さなインタフェース 明示的なインタフェース 情報隠蔽
  39. 5 つの原則(principle) 5 つの基準と5 つの規則から導き出されるもの ソフトウェア構築にあたって守るべき原則 言語としてのモジュール単位の原則 自己文書化の原則 統一形式の原則 開放/閉鎖の原則 単一責任選択の原則
  40. trait の性質をあてはめてみる
  41. 5 つの基準から見るtrait 分解しやすさ: ○ 組み合わせやすさ: ○ 分かりやすさ: ☓ traitは利用クラスの中でメンバの名前空間を共有 自身の中にスコープの閉じたメンバを持てない プロパティは名前衝突時に定義がマージ 自身の定義したプロパティを操作するだけで他部品の不変 条件が壊れる可能性がある 連続性: ○ 保護性: △ trait HeightModifiable { // 身長操作trait private int $value; public function modifyHeight(int $value): void { $this->value += $value } } trait WeightModifiable { // 体重操作trait private int $value; public function modifyWeight(int $value): void { $this->value += $value } } class C { // わかりづらいことが起きる! use HeightModifiable, WeightModifiable; }
  42. 5 つの規則から見るtrait 問題領域の直接的な写像: ○ 少ないインタフェース: △ 小さいインタフェース: ☓ 利用クラス内で全てのメンバへのアクセスが全開放 同居する他trait のメンバにも自由にアクセス 明示的なインタフェース: ☓ 利用クラス側のprivate メンバへのアクセスさえ断りなく可能 プロパティの名前衝突のマージに問題がある場合も気づけない 情報隠蔽: ☓ trait local なメンバを定義できない private / protected / public は利用クラス側の可視性としてコピペ展開される
  43. 5 つの原則とtrait
  44. 5 つの原則とtrait: 言語としてのモジュール単位の原則: ○ 部品は使用される言語の構文単位として表されなければならないという原則 trait はまさにそれそのもの
  45. 5 つの原則とtrait: 自己文書化の原則: ☓ 部品についてのすべての情報を部品の一部として持つようにするという原則 かなりクラスに近い表現力はある PHP 8.2 からは定数定義もできる 「特定のinterface を実装するために使われるもの」をコメントでしか書けない 信頼性高く書けるクラスより弱い
  46. 5 つの原則とtrait: 統一形式アクセスの原則: △ プロパティの値を読み書きする時もメソッドを呼ぶ時も同じ表記を使うという原則 できないが、クラスもできないのでクラスより悪くはない 将来クラスで可能になればtrait でも可能になる internals でProperty Accessors の議論を進めている人もいる
  47. 5 つの原則とtrait: 開放/閉鎖の原則: ☓ 既存の部品への修正をせず部品を拡張できるという原則 abstract メソッドでtrait 中心の部品構成でも一応可能 interface 側とtrait 側で同じシグネチャを記述するようなこ とが起きがち 特定のinterface を実装していることの要求をできるクラス とくらべるとやや弱い trait T1 { abstract public function f1(): void; public function f2(): void { f1(); } } trait T2 { use T1; public function f1(): void { echo 'T2';} } trait T3 { use T1; public function f1(): void { echo 'T3';} }
  48. 5 つの原則とtrait: 単一責任選択の原則: ☓ 選択肢を提供する際、システム内の1 つの部品だけがその選択肢のすべてを把握すべきという原則 言語的にサポートされているとは言い難い どのtrait を使うかが利用側の各クラスなどへ静的にuse で分散して埋め込まれる use 先を分岐するような仕組みがtrait にはない
  49. trait の性質まとめ クラス(の継承)より良いところ 部品を小分けにして組み合わせられる ただしDI / 合成が同等の能力 クラス(の継承)より悪いところ 意図しない部品間の干渉を防ぐ仕組みが弱い interface が付けられない
  50. trait の使いどころ
  51. その前に前提として DI (合成)で済むならそっちのほうがよい DTO っぽいのにちょっとデータ取得メソッド生やしたいとか にはDI より向く 他に頼れるやつがいない時に使う最終兵器 // これより class C1 { use FunctionalityTrait1; use FunctionalityTrait2; } // クラスのDIにするほうが楽 class C2 { public function __construct( private FunctionalityClass1 $functionality1, private FunctionalityClass2 $functionality2, ) { } }
  52. インタフェースのデフォルト実装 リッチインタフェースを利用者が扱いやすい形で採用可能 利用コードへ影響を与えずにインタフェースをあとから拡張 可能 PSR-3 のLoggerAwareInterface とLoggerAwareTrait、 LoggerInterface とLoggerTrait など interface HogeInterface { public function method1(): void; } trait HogeDefaultImplementation { public function method1(): void { echo 'hoge'; } } class Hoge implements HogeInterface { use HogeDefaultImplementation; }
  53. インタフェースとあわせての実質的な多重継承の実現 trait は型を伴わない実装 interface は実装を伴わない型 2 つあわせて(だいたい)多重継承 // 単一継承だとこういうのを小分けにできない // traitなら小分けに分割できる class P { public function common_function(): void {} // A、Bにのみ必要な機能がC、Dにも導入 public function ab_function(): void {} // C、Dにのみ必要な機能がA、Bにも導入 public function cd_function(): void {} } class A extends P {} class B extends P {} class C extends P {} class D extends P {}
  54. クラスの分割実装 機械生成コードに手書きのコードを足したい場合などに嬉し い 直接修正するとスキーマ変更時の再生成で上書きされる ORM とかIDL からのコード生成とかへデータアクセス用の 処理をちょっと足したいとか // 機械生成コードをクラス側に置く構成も可 trait MachineGeneratedCodes { // 中略(機械生成されたコード) } // ↓別ファイルで定義 // 手書きコードをトレイト側に置く構成も可 class HandWrittenCodes { use MachineGeneratedCodes; // 中略(手書きのコード) }
  55. 類似機能を持つ他言語からのコード移植 Java やKotlin のinterface のメソッドデフォルト実装 C++ やPython の多重継承 Scala やRust のtrait C# のpartial
  56. 偶然同じ機能を持つクラスの実装の共通化 複数のクラスへ同じ機能を与えたい が、同じ型を与えたいわけではない Laravel のMacroable など コードのユーザに複数クラスの統一インタフェースを提供したいだけの時
  57. trait の将来 たぶんなくなることはない アカンけど捨てるわけにもいかんよね、はtrait 定数のRFC で再確認できた メンバの可視性を縛る方法とかinterface と紐付ける機能とかほしい
  58. おしまい
  59. - 宣伝- WEB+DB PRESS vol.130 のPHP 連載でも trait の扱いについて書いてます! このトークと被る部分もありつつ、切り口は違い、文章としてまとめてあります 興味があればぜひ https://gihyo.jp/magazine/wdpress/archive/2022/vol130
Advertisement