web-dev-qa-db-ja.com

インターフェースと抽象クラスの継承、拡張クラスでの実装

私が見たすべての例で、拡張クラスはその親のインターフェースを実装しています。参考までに、次の例:

interface MyInterface{
    public function foo();
    public function bar();
}

abstract class MyAbstract implements MyInterface{
    public function foo(){ /* stuff */ }
    public function bar(){ /* stuff */ }
}

// what i usually see
class MyClass extends MyAbstract implements MyInterface{}

// what i'm curious about
class MyOtherClass extends MyAbstract{}

親によって実装されている子のインターフェースの実装の失敗は、悪い習慣と考えられていますか?子供の実装を省略することに技術的な欠点はありますか?

34
Dan Lugg

私はあなたが正しい道にいると思います。すでにimplementsであるクラスを拡張する場合、インターフェースを実装していることを宣言する必要はありません。私にとっては、変更が必要な場合に維持するコードのほんの一部です。だから、はい、あなたは正しいです!

21
Emil Ivanov

親によって実装されている子にインターフェースを実装することの失敗は、悪い習慣か何かと考えられていますか?子供の実装を省略することには技術的な欠点がありますか?

この人が持っている以上に私はあなたの質問にうまく答えることができません:

本質的に、それらは非常によく似ている場合がありますが、抽象クラスとクラスインターフェイスは非常に異なる目的で機能します。

クラスのインターフェースは、そのクラスの「ユーザー」のためのツールとして意図されています。インターフェースはクラスの公開プレゼンテーションであり、それを使用することを検討しているすべての人に、どのメソッドと定数が利用可能で外部からアクセスできるかを宣伝する必要があります。そのため、その名前が示すように、ユーザーとそれを実装するクラスの間に常に存在します。

一方、抽象クラスは、それを拡張するクラスの「実装者」を支援することを目的としたツールです。これは、具象クラスがどのように見えるべきかについて制限とガイドラインを課すことができるインフラストラクチャです。クラス設計の観点からは、抽象クラスはインターフェースよりもアーキテクチャ上重要です。この場合、実装者は抽象クラスと具象クラスの間に位置し、後者を前者の上に構築します。

参考

したがって、誰がクラスを使用(インスタンス化)するのか、誰がクラスを作成するのかに基づいて、決定するのはあなた次第です。クラスの唯一のユーザーでありライターである場合、おそらく両方とも必要ないでしょう。ただし、クラスライターとクラスユーザーのコアビット設計図をすべての人に提供したい場合は、抽象化と実装の両方の使用を検討する必要があります。

18
Shef

多分少し遅れますが、上記のコメントはOPの質問の根底にある主な誤解を明確にしていないと思います。

したがって、根本的な質問は次のとおりです。

  • なぜ抽象クラスとインターフェイスの両方を同じ行で使用するのですか?
  • 抽象メソッドとインターフェースの両方がまったく同じメソッドを宣言する必要がありますか?

しかし、いくつかの明確化の前に、上記の2つのいずれかを使用する理由:

  • いずれかのプログラマーがコントラクト(要件、義務、制限)を定義するために1人のプログラマーが使用し、他のプログラマーは、そのプログラマーが開発した抽象クラス/インターフェースに基づいて具体的なクラス(および最終的にはソフトウェアアプリケーション全体)を作成するときに従わなければなりません。
  • 次に、抽象クラスを使用して、後で作成される具象クラスにメソッドとデータ構造の青写真を提供します。

    1. データ構造宣言(オプション)、
    2. メソッドの基本実装(およびそれらのシグニチャー、オプション)
    3. メソッド宣言のみ(インターフェースの使用法と同様、オプション)。
  • インターフェースを使用して、具体的なクラスにメソッドブループリントを提供します。

    1. メソッド(およびそのシグニチャー、オプション)宣言のみ。

以下は、抽象クラスと具象クラスの例です。

abstract class MyAbstractClass {

    public function foo() {
        // Base implementation of the method here.
    }

    public function bar() {
        // Base implementation of the method here.
    }

    // Effectively similar to baz() declaration within some interface:
    public abstract function baz($value);

}

class MyConcreteClass extends MyAbstractClass {

    // foo() and bar() are inherited here from MyAbstractClass.

    // baz() must be implemented or declared abstract again.
    public function baz($value) {
        // implementation.
    }

}

次に質問が来ます:

  1. ここにインターフェースが必要なのはなぜですか?
  2. 同じメソッド宣言を複製するためにインターフェイスが必要ですか?

回答:

  1. PHPは、各サブクラスに対して単一の継承のみを許可するため、class MyConcreteClass extends MyAbstractClass, MyAnotherClass {})、すでに使用されている抽象クラスを超えて具象クラスの機能を拡張する必要がある場合、1つ以上のインターフェースを介してこの追加機能を宣言する必要があります。

    このような:

    class MyConcreteClass 
        extends MyAbstractClass 
        implements MyInterface, MyAnotherInterface {
        // Methods and data implementations go here.
    }
    
  2. 回答1の結果として、インターフェイスしないほうがよい抽象クラスメソッドの宣言を複製します(これは基本的に役に立たない)。インターフェイスは、これらのクラスとインターフェイスの上に構築された各オブジェクトの確定コントラクトでこれらを使用するプログラマーに提供するために、具体的な(または別の抽象クラス、そうでない場合)機能の強化に役立つ可能性があるメソッドを宣言する必要があります。

最後に、抽象クラスまたは具象クラスにインターフェイスを使用するかどうかのOPの質問に答えます。

  • インターフェイスが新しいメソッドの宣言でクラスコントラクトを強化する限り、いずれかまたは両方(または必要に応じて)に使用します。
1
Valentine Shi

まあ、私も混乱しましたが、後者を使用するべきだと思います。そうです、抽象クラスにインターフェースを実装する場合、インターフェースを作成する必要はありません。インターフェース内のメソッドをすべてに書き込むことができます。抽象メソッドとして抽象化します。抽象クラスを拡張し、他の場所でクラスを使用するときは抽象クラスをparam型として使用する必要があるため、これは良いことではありません。抽象クラスは、インターフェースは使用する必要がありますが、param型として使用できます。

0
lvpiao

親によって実装されている子のインターフェースの実装の失敗は、悪い習慣と考えられていますか?

子は常にインターフェースを実装し、これを回避することはできません。

それが悪い習慣か何かであるかどうか私には手がかりがありません。言語機能だと思います。

子供の実装を省略することに技術的な欠点はありますか?

たとえば、インターフェイスを持つ抽象クラスのリフレクションをテストすることはできません。

ただし、抽象クラスはすでにインターフェースであるため、技術的にはインターフェース自体は実際には必要ありませんが、継承内で物事をスムーズに保つためにそうすることができます。

0
hakre