インターフェース定数の長所と短所[終了]


105

PHPインターフェースを使用すると、インターフェースで定数を定義できます。

interface FooBar
{
    const FOO = 1;
    const BAR = 2;
}
echo FooBar::FOO; // 1

実装クラスでは、これらの定数が自動的に使用可能になります。例:

class MyFooBar implement FooBar
{
}
echo MyFooBar::FOO; // 1

これに対する私自身の見解は、グローバルはすべて悪であるということです。しかし、インターフェイス定数にも同じことが当てはまるのでしょうか。ことを考えるとインタフェースに対してコーディングすることは一般的に良い習慣とみなされ、インタフェース定数にクラスのコンテキスト外で使用する許容される定数のみを使用していますか?

私はあなたの個人的な意見やインターフェイス定数を使用するかどうかを知りたいと思っていますが、主にあなたの答えの客観的な理由を探しています。これを投票タイプの質問にしたくない。インターフェース定数の使用が保守性に与える影響に興味があります。カップリング。または単体テスト。それはSOLID PHP とどのように関係していますか?PHPのグッドプラクティスと見なされているコーディング原則に違反していますか?あなたはアイデアを得ます…

注: Javaについても同様の質問があり、それらが悪い習慣である理由をいくつか挙げていますが、JavaはPHPではないので、PHPタグ内でもう一度質問するのは妥当だと感じました。


1
うーん、これまでインターフェイスで定数を定義する必要に遭遇したことはありません。これは、クラスのことを知っている価値があるインターフェイスを実装するには、クラスは単にお互い拡張しながら、定数を変更することはできませんオーバーライド定数を。
Charles

1
定数は、単体テスト可能性を考慮しても予測可能な値を持っているので、悪くはないと思います。グローバル変数は、それが変数であり、すべてにスコープがあるので誰でも変更できるので悪です。
mdprotacio 2012

回答:


135

まあ、それは結局、十分の違いに帰着すると思います。

ほとんどの場合、他のパターン(戦略またはおそらくフライウェイト)を実装することで定数の使用を回避できますが、概念を表すために他の6種類のクラスを必要としないということもあります。結局のところ、他の定数が必要になる可能性はどのくらいあるのでしょうか。つまり、インターフェイスの定数によって提供されるENUMを拡張する必要がありますか。それを拡張する必要があることが予測できる場合は、より正式なパターンを使用してください。そうでない場合は、それで十分かもしれません(それで十分なため、記述およびテストするコードが少なくなります)。次に、十分な使用法と悪い使用法の例を示します。

悪い:

interface User {
    const TYPE_ADMINISTRATOR = 1;
    const TYPE_USER          = 2;
    const TYPE_GUEST         = 3;
}

十分良い:

interface HTTPRequest_1_1 {
    const TYPE_CONNECT = 'connect';
    const TYPE_DELETE  = 'delete';
    const TYPE_GET     = 'get';
    const TYPE_HEAD    = 'head';
    const TYPE_OPTIONS = 'options';
    const TYPE_POST    = 'post';
    const TYPE_PUT     = 'put';

    public function getType();
}

さて、これらの例を選んだ理由は簡単です。Userインタフェースは、ユーザタイプの列挙を定義しています。これは時間とともに拡大する可能性が非常に高く、別のパターンに適しています。ただし、HTTPRequest_1_1列挙型はRFC2616で定義されており、クラスの存続期間中は変更されないため、これは適切なユースケースです。

一般に、定数とクラス定数の問題は、グローバルな問題であるとは思いません。依存関係の問題だと思います。それは狭い違いですが、明確な違いです。強制されていないグローバル変数のようにグローバル問題を見て、そのため、ソフトなグローバル依存関係を作成します。ただし、ハードコードされたクラスは強制された依存関係を作成し、そのためハードグローバル依存関係を作成します。したがって、どちらも依存関係です。しかし、強制されていないので、グローバルははるかに悪いと思います...これが、同じバナーの下でクラス依存関係グローバル依存関係と一緒にまとめたくない理由です...

と書くMyClass::FOOと、の実装の詳細にハードコードされますMyClass。これによりハードカップリングが作成され、コードの柔軟性が低下するため、回避する必要があります。ただし、このタイプの結合を正確に許可するインターフェースが存在します。したがってMyInterface::FOO、具体的な結合は導入されません。そうは言っても、定数を追加するだけのインターフェースは紹介しません。

ですから、インターフェースを使用している、とあなたがしている場合は非常にあなた(またはそのことについては誰にも)追加の値を必要としないことを確認し、その後、私は本当にインターフェイス定数を持つ巨大な問題が表示されない...最高設計には、定数、条件、マジックナンバー、マジックストリング、またはハードコーディングされたものは含まれません。ただし、使用を検討する必要があるため、開発にはさらに時間がかかります。私の見解では、多くの場合、優れた堅牢な設計を構築するために追加の時間を費やすことが絶対に価値があると考えています。しかし、本当に十分に満足できる場合もあり(その違いを理解するには経験豊富な開発者が必要です)、そのような場合は問題ありません。

繰り返しますが、それは私の見解です...


4
この場合、ユーザーの別のパターンとして何を提案しますか?
ジェイコブ

@ジェイコブ:私はそれを抽象化します。必要に応じて、データベーステーブルからデータを取得するAccessクラスを作成する可能性があります。この方法では、新しいレベルを追加するのは、新しい行を挿入するのと同じくらい簡単です。別のオプションは、ENUMクラスセットを構築することです(各アクセス許可の役割に1つのクラスがあります)。次に、必要に応じてクラスを拡張して、適切な権限を提供できます。しかし、あまりにも動作します他の方法があります
ircmaxell

3
非常にしっかりした、明確な回答!+1
まともなダブラー2011年

1
パブリック定数を持つクラスはメソッドを持つべきではありません。データ構造またはオブジェクトのみである必要があります-両方ではありません。
OZ_ 2011

2
@FrederikKrautwald:ポリモーフィズムで条件文を回避できます(ほとんどの場合):この回答をチェックするだけでなく、このクリーンコードの話てください...
ircmaxell

10

定数、特別に列挙された定数は、インターフェースとは別の型(「クラス」)として処理する方が通常は良いと思います。

define(TYPE_CONNECT, 'connect');
define(TYPE_DELETE , 'delete');
define(TYPE_GET    , 'get');
define(TYPE_HEAD   , 'head');
define(TYPE_OPTIONS, 'options');
define(TYPE_POST   , 'post');
define(TYPE_PUT    , 'put');

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

または、クラスを名前空間として使用する場合:

class TypeHTTP_Enums
{
  const TYPE_CONNECT = 'connect';
  const TYPE_DELETE  = 'delete';
  const TYPE_GET     = 'get';
  const TYPE_HEAD    = 'head';
  const TYPE_OPTIONS = 'options';
  const TYPE_POST    = 'post';
  const TYPE_PUT     = 'put';
}

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

定数だけを使用しているのではなく、列挙値または列挙値の概念を使用しています。制限された値のセットは、特定の使用法( "ドメイン"?)を持つ特定のタイプと見なされます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.