PHPで使うGoFパターン ひとり Advent Calendar - アダプター

  • 13
    Like
  • 0
    Comment
More than 1 year has passed since last update.

アダプターってなに?

要するに無関係な二つの概念を結合させるためのパターンです。

  • 継承とインターフェースの実装を利用する
  • 継承と移譲を利用する

オブジェクト指向を普段利用している人にとっては、知らず知らずのうちに利用しているパターンではないでしょうか。今更、ふんぞり返ってデザインパターンと呼ぶほどのことではないじゃないか?なんて思ったりしているのではないでしょうか。しかし、このパターンを意識して考えてみると、これを適用することによって複雑度を減らすことが出来た箇所が多いことに気付くと思います。むしろ、継承という概念は、このパターンのために存在しているのではないだろうか? なんて思ったり。むしろ、そういう設計こそ良い設計でありデザインパターンと名前を付けて頭の念頭に置いておく意味のあるものではないかと思います。

アダプターの構造

  • クライアント - 利用者
  • Target - 適合する側
  • Adaptee - 適合させる側
  • Adapter - 実装部分

実際は何も難しい話ではないのですが、これ構造の項目名だけで見ても、よく分からないですし、
また、サンプルソースらしいサンプルソースだと見ても分かり辛いパターンです。
なので、今回はサンプルソースをそれなりに頑張ってみます。

例えば、既存システムに以下の機能が有るとします。

  • ECの決済機能
  • システムの利用に応じてスコアが貯まっていくゲーミフィケーション機能

これが営業側からの要請によりスコアに応じてEC機能で利用出来るポイントを実装することになりました。アダプタパターン(移譲)を使って実装してみようと思います。

<?php
/* Client */
class Register {
    public static function pay (Payment $payment) {
        $payment->pay (new Cart());
    }
}


interface Payment {
    public function pay ($cart);
}

/* Adaptee */
class PaymentImpl implements Payment {
    private $member = null;
    public function __construct($member) {
        $this->member = $member;
    }

    // 決済処理
    public function pay ( $cart ) {
        $subtotal = $cart->getSubTotal();
        print $this->member->getId() . 'さんに' . $subtotal . 'の決済したっす' . "\n";
    }
}



/* Target */
class Score {
    // 加算レコードの追加
    // 既存であったもの
    public function add ( $point ) {
    }


    // 加算レコードのみサマる
    // 既存であったもの
    public function getSum () {
    }

    // 減算レコードの追加
    // 今回追加したもの
    public function minus ( $point ) {
    }

    // 加算・減算をサマって現在の総ポイントを取得
    // 今回追加したの
    public function getSumWithMinu () {
    }

}

/* Adapter */
class PaymentWithScore extends Score implements Payment {
    private $member = null;
    private $scoreRate = 0.1;
    public function __construct ( $member ) {
        $this->member = $member;
    }

    public function pay ($cart) {
        // サンプルなので自動で全ポイントを使用する
        // パターンしか考慮しません
        $cart->addDiscount ($this->member->getScore());
        $subtotal = $cart->getSubTotal();
        print $this->member->getId() . 'さんに' . $subtotal . 'の決済したっす' . "\n";
    }
}

class Cart {
    private $discount = null;
    // 小計を取得
    public function getSubTotal () {
        return 5000 - $this->discount;
    }

    // 割引を設定
    public function addDiscount ( $point ) {
        $this->discount = $point;
    }

    // カートに商品を追加
    public function addProduct ( $prod, $amount ) {
    }
}

class Member {
    public function getId() {
        return 1234;
    }
    public function getScore() {
        return 500;
    }
}
$member = new Member();

Register::pay(new PaymentImpl($member));
// 関係の無いScoreクラスのインターフェースPaymentに適合してる
Register::pay(new PaymentWithScore($member));

アダプターの問題点

素晴しい設計手法ではりますが、多用すると構造を追いづらくなり易いパターンですので、ご利用は計画的に。このパターンは、むしろ既存システムを改修する時に、無関係な構造同士を結ぶ時にクイックハック的に使うものであり、初期からこれが登場するような設計はしてはいけません。また、これを適用する時には「アダプターパターンでっせ」と明示的にするように「HogeWithFuga」のようなクラス名を付けておくのが好みです