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

traitを使って楽したい話

 traitを使って楽したい話

2014年8月1日に行われた、インフィニットループ社内勉強会資料。

Infiniteloop

July 14, 2023
Tweet

More Decks by Infiniteloop

Other Decks in Programming

Transcript

  1. シングルトンするマネージャクラス作ったよ! ↓ じゃあその調子でもう一個同じようなの作って ↓ オーケー!楽勝だぜ! /lib/managers/SomeManager.php <?php class SomeManager {

    static private $_instance = null; private function __construct() {} public function getSomething() { // ... } static public function getInstance() { if (!isset(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } }
  2. <?php class OtherManager { static private $_instance = null; private

    function __construct() {} public function getAnything() { // ... } static public function getInstance() { if (!isset(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } } /lib/managers/SomeManager.php /lib/managers/OtherManager.php コピペ コピペ コピペ コピペ コピペ コピペ コピペ コピペ <?php class SomeManager { static private $_instance = null; private function __construct() {} public function getSomething() { // ... } static public function getInstance() { if (!isset(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } }
  3. <?php class OtherManager { static private $_instance = null; private

    function __construct() {} public function getAnything() { // ... } static public function getInstance() { if (!isset(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } } /lib/managers/SomeManager.php /lib/managers/OtherManager.php コピペ コピペ コピペ コピペ コピペ コピペ コピペ コピペ <?php class SomeManager { static private $_instance = null; private function __construct() {} public function getSomething() { // ... } static public function getInstance() { if (!isset(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } }
  4. <?php class SomeManager { use Singletonnable; public function getSomething() {

    // ... } } <?php class OtherManager { use Singletonnable; public function getAnything() { // ... } } 実際シンプル!! /lib/managers/SomeManager.php /lib/managers/OtherManager.php
  5. <?php trait Singletonnable { static private $_instance = null; private

    function __construct() {} static public function getInstance() { if (!isset(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } } Singleton trait <?php class SomeManager { use Singletonnable; public function getSomething() { // ... } } <?php $mngr = SomeManager::getInstance(); $mngr->getSomething(); /lib/traits/Singletonnable.php /lib/managers/SomeManager.php /main.php 適用 利用
  6. Why we use trait? 1. “複数クラスで実装される同一機能”を単位化し、再利用可能にする <?php // シングルトンデザインパターンは共通の機能 SomeClass::getInstance();

    OtherClass::getInstance(); // 何かの抽選はガチャやドロップ品などのランダム要素で共通の機能 $got_item_id = $gacha->lot($lot_item_list); $got_item_id = $drop_reward->lot($lot_item_list); // DB取得や設定はDBのレコード一行を保持するクラスで共通の機能 // マジックメソッドでうまいことやったり $user_name = $user_table->getUserName(); $unit_attack = $unit_table->getAttack();
  7. Why we use insteadof delegate? trait で出来ることは、委譲や静的メソッドでも実装出来る <?php class Player

    { private $attacker; public function __construct() { $attacker = new Attacker(); } public function attackTo($to) { $attacker->attack($to); } } <?php class Player { public function attackTo($to) { Attacker::attack($this, $to); } }
  8. When we use trait? 共通に使える単機能をパーツ化して、クラスにガチャコンするイメージ a. デザインパターンの実装(singleton, composite, …) b.

    様々な場所で使われる処理(DBアクセス、バリデート、特定データ保持…) c. 上記機能を提供するようなフレームワーク・ライブラリ作成 (CakePHP3は trait が活用されているそうです)
  9. Implements Design Pattern パターン化された処理の実装 <?php trait Compositable { private $_childs

    = []; public function addChild($child) { $this->_childs[] = $child; } public function removeChild($child) { foreach ($i = 0; $i < count($this->_childs); $i++) { if ($this->_childs[$i] === $child) { $this->_childs = array_splice($this->_childs, $i, 1); break; } } } } <?php trait Observable { private $_listeners = []; public function addListener($listener) { $this->_listeners[] = $listener; } public function notify() { foreach ($this->_listeners as $listener) { $listener->notified(); } } }
  10. Implements Common Behavior 様々な場所で使われるライブラリ的な振る舞いの実装 <?php trait Queriable { private $_db_accessor;

    private function query($select, $from, $where, $option = '') { if (!isset($this->_db_accessor)) { $this->_db_accessor = DatabaseAccessor::getInstance(); } // クエリを投げる処理... } } <?php trait Loggable { private function logError($str, $throw_exception = false) { // ログを出力したり、例外を排出したり } }
  11. P.S. Solarized colorscheme 色々なエディタ・ターミナル用の設定が配布中 • vim • emacs • IntelliJ

    IDEA • NetBeans • Visual Studio • Xcode • iTeam2 • OS X Terminal.app • …
  12. trait replaces ‘abstract class’ Template Method デザインパターンは trait で十分 <?php

    trait GachaPlayer { private $lot_items = array(); private $elected = null; public function lot() { $this->setLotItems(); $this->lot(); return $this->elected; abstract private function setLotItems(); private function lot() { /* 抽選する処理 */ } } class BattleGachaPlayer { use GachaPlayer; private function setLotItems() { /* バトル用の抽選データ設定 */ } } class AdventureGachaPlayer { use GachaPlayer; private function setLotItems() { /* 冒険用の抽選データ設定 */ } }
  13. trait replaces ‘abstract class’? 但し、「機能を再利用するための継承」ではなく、「オブジェクトの派生と しての継承(本質的)」の場合は使わない方が良い (オブジェクト指向の考え方の問題) <?php abstract class

    CharacterJob { private $job_name; public function acquireSkill($skill_type) { /* ... */ } public function addExperience($amount) { /* ... */ } // ... } class WarriorJob extends CharacterJob { const SKILL_TYPE_SLASH = 1; // ... }
  14. trait > extends PHPの継承は「多重継承」が出来ない <?php class Singletonnable { /* ...

    */ } class WorldMapBase { /* ... */ } class DQ1WorldMap extends Singletonnable, WorldMapBase { // ... }
  15. trait > implements PHPのインターフェイスは「実装」が出来ない <?php interface Singletonnable { static public

    function getInstance(); } class SomeManager implements Singletonnable { static public function getInstance() { // 結局実装しないといけない } } class OtherManager implements Singletonnable { static public function getInstance() { // 結局実装しないといけない } }
  16. trait > static クラス間の依存性が高まり保守性に欠ける <?php final class SomeManager { static

    public getSomeDataList($data_type) { // ... } } class SomeProcessor { public function process() { // 引数が変わる?スコープ変わってアクセス出来なくなる? $data = SomeManager::getSomeDataList(DATA_TYPE_SOMETHING); } }
  17. extends > trait 定数を持てない インターフェイスを implement 出来ない タイプヒントに使えない トレイト自身をインスタンス化出来ない <?php

    trait SomeFunc implements SomeInterface { const SOME_DATA = 'somesome'; public function someInterfaceMethod() { } } function getSomeFunc(SomeFunc some) { some->someInterfaceMethod(); } SomeFunc::SOME_DATA;
  18. implements > trait 絶対にそこで実装しなければならないメソッドを定義出来ない ※ trait は “実装付きインターフェイス” とも呼べる http://stackoverflow.com/questions/9205083/php-traits-vs-interfaces

    <?php interface SomeInterface { public function SomeInterfaceMethod(); } trait SomeTrait { public function SomeTraitMethod() { /* ... */ } } class SomeClass implements SomeInterface { use SomeTrait; public function SomeInterfaceMethod() { /* must implement */ } public function SomeTraitMethod() { /* don’t have to implement */ } }