10upのWordPressのベストプラクティスから「デザインパターン」の章を訳してみた

PHPerの方からすれば何を今さらの話かもしれませんが、ふだんWordPress経由でしかPHPを触らないワタシにはなるなるなるーな話でしたので訳してみた。

ちなみにここで言う「デザイン」とは

ソフトウェア開発におけるデザインパターン(型紙(かたがみ)または設計パターン、英: design pattern)とは、過去のソフトウェア設計者が発見し編み出した設計ノウハウを蓄積し、名前をつけ、再利用しやすいように特定の規約に従ってカタログ化したものである。

デザインパターン (ソフトウェア) – Wikipediaより、

のことなので、(いわゆるフツーの)デザイナーの方はゆめゆめ勘違い召されませぬようよう。

10up Engineering Best Practicesより、

デザインパターン

PHPのコードを扱うときに一般的なデザインパターンを利用することは、製品の保全性を保証するためのもっとも簡単な方法です。このセクションではプロジェクトへ新しい開発者が参加するときの障壁を低くする標準的な手法について説明します。

名前空間

すべての関数のコードは適切に名前空間化されるようにしましょう。自分たちのコードを論理的に整理したり衝突を避けたりするために、私たちはこれをグローバルな名前空間で行っています。一般的には、これはインクルードされたファイルの一番上でPHPの名前空間識別子を利用することを意味します:

<?php
namespace tenup\Utilities\API;

function do_something() {
  // ...
}

もしそのコードがWordPress.orgのテーマやプラグインリポジトリへ一般公開するものなら、WordPress自身のPHPバージョン最低動作要件を満たす必要があります。残念ながら、PHPの名前空間がサポートされているのは5.3位上なので、代わりに擬似名前空間としてスタティックな関数をラップするためのクラスを用いることができます:

<?php
/**
 * Namespaced class name example.
 */
class Tenup_Utilities_API {
    public static function do_something() {
        // ...
    }
}

この名前空間とスタティッククラスは構造が似ているため、どちらのスタイルのプロジェクトでも簡単に馴染むことができます(また、WordPressの最低バージョン要件が引き上げられても素早くPHP名前空間にアップグレードできます)。

グローバルな名前空間に宣言されているものは名前空間自身も含めてすべて、一意性を確保するように書かなくてはなりません。 tenup のような名前空間は(たいていは)一意でしょう。 theme は一意ではありません。確実に一意性を持たせるようにするには宣言に一意なプリフィックスを付けるのが簡単でしょう。

オブジェクトデザイン

最初に、もしある関数があるオブジェクトに固有のものではなければ、上で言及したように関数的な名前空間に含まれるようにすべきです。

オブジェクトはよく設計され、アトミックで、そのファイルの最初のdocblockで完全にドキュメント化されるようにします。オブジェクト内のすべてのメソッドとプロパティ完全にドキュメント化し、オブジェクト自身に関連付けます。

<?php
/**
 * Video.
 *
 * This is a video object that wraps both traditional WordPress posts
 * and various YouTube meta information APIs hidden beneath them.
 *
 * @package    ClientTheme
 * @subpackage Content
 */
class Prefix_Video {

    /**
     * WordPress post object used for data storage.
     *
     * @access protected
     * @var WP_Post
     */
    protected $_post;

    /**
     * Default video constructor.
     *
     * @access public
     *
     * @see get_post()
     * @throws Exception Throws an exception if the data passed is not a post or post ID.
     *
     * @param int|WP_Post $post Post ID or WP_Post object.
     */
    public function __construct( $post = null ) {
        if ( null === $post ) {
            throw new Exception( 'Invalid post supplied' );
        }

        $this->_post = get_post( $post );
    }
}

可視性

オブジェクト指向プログラミング(OOP)という意味では、パブリックなプロパティとメソッドは明確に public であるべきです。意図してプライベートにするものはすべて実際に protected として明示するようにしましょう。ちゃんとしたドキュメントがなかったり合意済みの理由がない private なフィールドやプロパティがないようにしましょう。

構造とパターン

  • シングルトンはおすすめしません。実際のところこのパターンを正当化する理由がありませんし、問題の修正よりもメンテナンス上の問題を多く引き起こします。
  • クラスの継承は DRY なコードを作成できて、そのアプリケーション全体を通して以前作成したコンポーネントを共有できる可能性があるときに使用します。
  • グローバル変数は避けるべきです。もしオブジェクトをテーマやプラグイン全体に渡す必要がある場合、そうしたオブジェクトはパラメータとして渡すかオブジェクトファクトリを通して参照されるようにします。
  • 隠蔽されている依存(API機能、スーパーグローバル変数など)は各関数/メソッドもしくはプロパティのdocbock内にドキュメント化するようにします。