インタフェース向けプログラミング(Javaインタフェースか抽象クラスかを選択)
抽象クラスではなくインタフェースを使用しなければならない場所があるのに、インタフェースではなく抽象クラスを使用しなければならない場所があるのではないかという疑問がある人が多い.あるいは、Javaクラスの一般化を考えると、インタフェースと抽象クラスの間でためらったり、勝手に選択したりする人が多い.実際にはインタフェースと抽象クラスの選択は勝手ではありません.インタフェースと抽象クラスの選択原則を理解するには、オブジェクトの動作とオブジェクトの実装という2つの概念が重要です.1つのエンティティに複数の実装方法がある場合、エンティティの動作の記述方法を設計するときは、エンティティを使用するときに、エンティティの動作の実装方法を詳細に理解する必要がないという目標を達成する必要があります.すなわち,オブジェクトの行為とオブジェクトの実現を分離する.Javaのインタフェースと抽象クラスが具体的な実装を提供しない方法を定義できる以上、オブジェクトの動作とオブジェクトの実装を分離する際に、インタフェースを使用するべきか抽象クラスを使用するべきか.
抽象クラスによる動作モデルの構築
インタフェースと抽象クラスの選択では、動作モデルは抽象クラスではなく常にインタフェースによって定義されるべきであるという原則を守らなければならない.その原因を説明するために、抽象クラスを通じて行動モデルを構築し、どのような問題が発生するかを見てみましょう.販売部門に「エンジン」(Motor)エンティティを含むソフトウェアを設計するとします.エンジンオブジェクトにおいてエンジンの態様を詳細に記述することは明らかではなく、現在のソフトウェアにとって重要ないくつかの特徴のみを記述することができる.エンジンの特徴が重要なのは、ユーザー(販売部門)と交流してこそ確定します.販売部門の人は、エンジンごとに馬力と呼ばれるパラメータがあることを要求しています.彼らにとって、これは唯一の関心のあるパラメータです.この判断に基づいて、エンジンの挙動を以下の挙動と定義することができる.動作1:エンジンの馬力を検索すると、エンジンは馬力を表す整数を返します.エンジンがどのように馬力というパラメータを取得するかはまだ分からないが、エンジンは必ずこの挙動を支持することができ、これはすべてのエンジンの唯一の注目すべき挙動特徴である.この動作特徴はインタフェースでも抽象クラスでも定義できます.抽象クラス定義で発生する可能性のある問題を説明するために、以下に抽象クラスでエンジンの動作モデルを構築し、Javaメソッドで動作1を記述します.コードは以下の通りです.
Motor抽象クラスに基づいて、A型エンジン、B型エンジンなど、さまざまな具体的な実装を構築し、システムの他の部分を加えて、1.0版のソフトウェアを入手し、使用を提供します.しばらく経って、今2.0版のソフトウェアを設計します.2.0版のソフトウェア要件を評価する過程で、エンジンの一部がバッテリ駆動であり、バッテリには一定の充電時間が必要であることが分かった.販売部門の人は、コンピューターで充電時間を調べることを望んでいます.この要求に基づいて、図1に示すように、新しい動作を定義する.動作2:電気駆動エンジンの充電時間を問い合わせると、エンジンは充電時間を表す整数を返します.Javaメソッドでこの動作を記述します.コードは次のとおりです.
販売部門のソフトウェアでは、電気駆動エンジンもクラス形式で実現されているが、これらのクラスはMotorではなくBatteryPoweredMotorから派生している.これらの変更は2.0版のソフトウェアに追加された後、販売部門は満足しています.業務の発展に伴い、やがて光駆動のエンジンが登場した.販売部門は光駆動エンジンに一定の光エネルギーが必要で、光エネルギーはルーメンで測定することを要求している.この情報はお客様にとって重要です.雨や曇りの天気では、一部の光駆動エンジンが作動しない可能性があります.販売部門は、ソフトウェアに光駆動エンジンのサポートを追加することを要求しているので、新しい動作を定義します.動作3:光駆動エンジンが正常に動作するために必要な最小流明数を照会し、エンジンは整数を返す.抽象クラスを定義し、動作3をJavaメソッドに変換します.コードは次のとおりです.
図1に示すように、SolarPoweredMotorとBatteryPoweredMotorは、いずれもMotor抽象クラスから派生している.ソフトウェア全体では、90%以上のコードがすべてのエンジンに同じように扱われています.たまにエンジンが光駆動なのか電気駆動なのかをチェックし、instanceofを使用して実現します.コードは以下の通りです.
どちらのエンジンでも馬力というパラメータが重要なので、派生するすべての抽象クラス(SolarPoweredMotorとBatteryPoweredMotor)ではgetHorsepower()メソッドが有効です.現在、販売部門には新しいエンジンがあり、電気駆動と光駆動の二重駆動エンジンです.光駆動と電気駆動の挙動自体は変化しなかったが,新しいエンジンは両方の挙動を同時にサポートした.新しい光電駆動エンジンをどのように定義するかを考えると,インタフェースと抽象クラスの違いが表示され始めた.新しい目標は、新型エンジンを増やす前提でコードをできるだけ変更しないことです.光駆動エンジン、電気駆動エンジンに関するコードはすでに全面的にテストされているため、既知のBugは存在しない.光電駆動エンジンを増やすには、新しいSolarBatteryPowered抽象クラスを定義します.SolarBatteryPoweredをMotor抽象クラスから派生させると、SolarBatteryPoweredは光駆動エンジンと電気駆動エンジンのinstanceof操作をサポートしません.つまり、光電駆動のエンジンが光駆動なのか、電気駆動なのかを尋ねると、答えは「いいえ」です.SolarBatteryPoweredをSolarPoweredMotor(またはBatteryPoweredMotor)抽象クラスから派生させる場合、同様の問題も発生します.SolarBatteryPoweredでは、BatteryPoweredMotor(またはSolarPoweredMotor)に対するinstanceof操作はサポートされません.動作から見ると,光電駆動のエンジンは同時に2つの抽象クラスから派生しなければならないが,Java言語では多重継承は許されない.この問題が発生する根本的な原因は、抽象クラスを使用することは、特定の動作を定義するだけでなく、実装のモードを定義することを意味するためである.すなわち、エンジンが動作を有することを宣言するだけでなく、エンジンが動作をどのように獲得するかのモデルを定義すべきである.
インタフェースによる動作モデルの作成
インタフェースを用いて動作モデルを構築すれば,実装モードを暗黙的に規定することを避けることができる.たとえば、前のいくつかの動作をインタフェースで定義すると、次のようになります. 動作1:
動作2:
動作3:
現在、光電駆動エンジンは、
DualPoweredMotorは、図2に示すように、動作の実装モードではなく、動作定義のみを継承する.
インタフェースを使用しながら抽象クラスを使用することもできますが、この場合、抽象クラスの役割は動作を定義するのではなく、動作を実現することです.動作を実現するクラスがインタフェース定義に従う限り、親抽象クラスを変更しても、他のコードが対話する方法を変更する必要はありません.特に,共通の実装コードに対して抽象クラスにはその利点がある.抽象クラスは実現の階層関係を保証し,コードの重複を避けることができる.しかしながら,抽象クラスを用いた場合でも,インタフェースによる挙動モデルの定義の原則を無視してはならない.実践の観点から見ると、抽象クラスに依存して動作を定義すると、複雑な継承関係を招くことが多く、インタフェース定義動作によって動作と実現をより効果的に分離することができ、コードのメンテナンスと修正に便利になる.(要約:抽象クラスは動作を実現するために定義でき、インタフェースは動作を定義するために使用される)
Javaインタフェースの特性学習Javaでインタフェースを見ると、最初に考えられるのはC++の多重継承とJavaのもう一つのキーワードabstractかもしれません.別の角度から多重継承を実現することはインタフェースの機能の一つであり、インタフェースの存在はJava内のオブジェクトを複数のベースタイプにアップグレードすることができ、抽象クラスと同様に他の人がクラスのオブジェクトを作成することを防止することができる.インタフェースはオブジェクトの作成を許可しないからである.
interfaceキーワードは、完全に抽象的なクラスを生成し、具体的な実装を提供しないインタフェースを宣言するために使用されます.interfaceの特性は以下のように整理されています.
1. インタフェース内のメソッドには、パラメータリストと戻りタイプがありますが、メソッドボディはありません.2. インタフェースにはフィールドを含めることができますが、staticとfinalとして暗黙的に宣言されます.3. インタフェースのフィールドは、インタフェースの静的記憶領域にのみ格納され、インタフェースには属しません.4. インタフェース内のメソッドはpublicとして宣言するか、宣言しないかを宣言できますが、結果はpublicタイプで処理されます.
(メソッドデフォルトpublic)5. インタフェースを実装する場合は、定義されたメソッドをpublicタイプとして宣言する必要があります.そうしないと、デフォルトのアクセスタイプとなり、Javaコンパイラでは許可されません.6. インタフェース内のすべてのメソッドが実装されていない場合、作成されたのは依然としてインタフェースです.7. 新しいインタフェースを生成するためにインタフェースを拡張するには、キーワードextendsを使用し、implementsを使用するインタフェースを実装する必要があります.
interfaceはabstractと似ている場所がありますが、クラスを宣言するには主に次の2つの点を参照します.1.メソッド定義とメンバー変数を持たないベースクラスを作成する場合は、抽象クラスではなくインタフェースを選択する必要があります.2.クラスがベースクラスであるべきであることを知っている場合は、抽象クラスを選択するのは、メソッド定義とメンバー変数が必要な場合のみです.抽象クラスには、1つまたは複数の特定の実装方法が存在することを可能にするため、メソッドがすべて実装されていない限り、そのクラスは抽象クラスである.以上がインタフェースの基本的な特性と応用分野であるが,インタフェースは決してそれだけではなく,Java構文構造ではインタフェースをネストすることができ,あるクラスにネストすることもできるし,インタフェースにネストすることもできる.これは実際の開発では多く応用される可能性はないが,その特性の一つでもある.インタフェースを実装する場合、内部にネストされたインタフェースを実装する必要はなく、privateインタフェースはクラスを定義する以外に実装できないことに注意してください.
抽象クラスによる動作モデルの構築
インタフェースと抽象クラスの選択では、動作モデルは抽象クラスではなく常にインタフェースによって定義されるべきであるという原則を守らなければならない.その原因を説明するために、抽象クラスを通じて行動モデルを構築し、どのような問題が発生するかを見てみましょう.販売部門に「エンジン」(Motor)エンティティを含むソフトウェアを設計するとします.エンジンオブジェクトにおいてエンジンの態様を詳細に記述することは明らかではなく、現在のソフトウェアにとって重要ないくつかの特徴のみを記述することができる.エンジンの特徴が重要なのは、ユーザー(販売部門)と交流してこそ確定します.販売部門の人は、エンジンごとに馬力と呼ばれるパラメータがあることを要求しています.彼らにとって、これは唯一の関心のあるパラメータです.この判断に基づいて、エンジンの挙動を以下の挙動と定義することができる.動作1:エンジンの馬力を検索すると、エンジンは馬力を表す整数を返します.エンジンがどのように馬力というパラメータを取得するかはまだ分からないが、エンジンは必ずこの挙動を支持することができ、これはすべてのエンジンの唯一の注目すべき挙動特徴である.この動作特徴はインタフェースでも抽象クラスでも定義できます.抽象クラス定義で発生する可能性のある問題を説明するために、以下に抽象クラスでエンジンの動作モデルを構築し、Javaメソッドで動作1を記述します.コードは以下の通りです.
public abstract Motor{
abstract public int getHorsepower();
}
Motor抽象クラスに基づいて、A型エンジン、B型エンジンなど、さまざまな具体的な実装を構築し、システムの他の部分を加えて、1.0版のソフトウェアを入手し、使用を提供します.しばらく経って、今2.0版のソフトウェアを設計します.2.0版のソフトウェア要件を評価する過程で、エンジンの一部がバッテリ駆動であり、バッテリには一定の充電時間が必要であることが分かった.販売部門の人は、コンピューターで充電時間を調べることを望んでいます.この要求に基づいて、図1に示すように、新しい動作を定義する.動作2:電気駆動エンジンの充電時間を問い合わせると、エンジンは充電時間を表す整数を返します.Javaメソッドでこの動作を記述します.コードは次のとおりです.
public abstract BatteryPoweredMotor extends Motor{
abstract public int getTimeToRecharge();
}
販売部門のソフトウェアでは、電気駆動エンジンもクラス形式で実現されているが、これらのクラスはMotorではなくBatteryPoweredMotorから派生している.これらの変更は2.0版のソフトウェアに追加された後、販売部門は満足しています.業務の発展に伴い、やがて光駆動のエンジンが登場した.販売部門は光駆動エンジンに一定の光エネルギーが必要で、光エネルギーはルーメンで測定することを要求している.この情報はお客様にとって重要です.雨や曇りの天気では、一部の光駆動エンジンが作動しない可能性があります.販売部門は、ソフトウェアに光駆動エンジンのサポートを追加することを要求しているので、新しい動作を定義します.動作3:光駆動エンジンが正常に動作するために必要な最小流明数を照会し、エンジンは整数を返す.抽象クラスを定義し、動作3をJavaメソッドに変換します.コードは次のとおりです.
public abstract SolarPoweredMotor extends Motor{
abstract public int getLumensToOperate();
}
図1に示すように、SolarPoweredMotorとBatteryPoweredMotorは、いずれもMotor抽象クラスから派生している.ソフトウェア全体では、90%以上のコードがすべてのエンジンに同じように扱われています.たまにエンジンが光駆動なのか電気駆動なのかをチェックし、instanceofを使用して実現します.コードは以下の通りです.
if (instanceof SolarPoweredMotor){...}
if (instanceof BatteryPoweredMotor){...}
どちらのエンジンでも馬力というパラメータが重要なので、派生するすべての抽象クラス(SolarPoweredMotorとBatteryPoweredMotor)ではgetHorsepower()メソッドが有効です.現在、販売部門には新しいエンジンがあり、電気駆動と光駆動の二重駆動エンジンです.光駆動と電気駆動の挙動自体は変化しなかったが,新しいエンジンは両方の挙動を同時にサポートした.新しい光電駆動エンジンをどのように定義するかを考えると,インタフェースと抽象クラスの違いが表示され始めた.新しい目標は、新型エンジンを増やす前提でコードをできるだけ変更しないことです.光駆動エンジン、電気駆動エンジンに関するコードはすでに全面的にテストされているため、既知のBugは存在しない.光電駆動エンジンを増やすには、新しいSolarBatteryPowered抽象クラスを定義します.SolarBatteryPoweredをMotor抽象クラスから派生させると、SolarBatteryPoweredは光駆動エンジンと電気駆動エンジンのinstanceof操作をサポートしません.つまり、光電駆動のエンジンが光駆動なのか、電気駆動なのかを尋ねると、答えは「いいえ」です.SolarBatteryPoweredをSolarPoweredMotor(またはBatteryPoweredMotor)抽象クラスから派生させる場合、同様の問題も発生します.SolarBatteryPoweredでは、BatteryPoweredMotor(またはSolarPoweredMotor)に対するinstanceof操作はサポートされません.動作から見ると,光電駆動のエンジンは同時に2つの抽象クラスから派生しなければならないが,Java言語では多重継承は許されない.この問題が発生する根本的な原因は、抽象クラスを使用することは、特定の動作を定義するだけでなく、実装のモードを定義することを意味するためである.すなわち、エンジンが動作を有することを宣言するだけでなく、エンジンが動作をどのように獲得するかのモデルを定義すべきである.
インタフェースによる動作モデルの作成
インタフェースを用いて動作モデルを構築すれば,実装モードを暗黙的に規定することを避けることができる.たとえば、前のいくつかの動作をインタフェースで定義すると、次のようになります. 動作1:
public interface Motor(){
public int getHorsepower();
}
動作2:
public interface BatteryPoweredMotor extends Motor(){
public int getTimeToRecharge();
}
動作3:
public interface SolarPoweredMotor extends Motor{
abstract public int getLumensToOperate();
}
現在、光電駆動エンジンは、
public DualPoweredMotor implements SolarPoweredMotor, BatteryPoweredMotor{}
DualPoweredMotorは、図2に示すように、動作の実装モードではなく、動作定義のみを継承する.
インタフェースを使用しながら抽象クラスを使用することもできますが、この場合、抽象クラスの役割は動作を定義するのではなく、動作を実現することです.動作を実現するクラスがインタフェース定義に従う限り、親抽象クラスを変更しても、他のコードが対話する方法を変更する必要はありません.特に,共通の実装コードに対して抽象クラスにはその利点がある.抽象クラスは実現の階層関係を保証し,コードの重複を避けることができる.しかしながら,抽象クラスを用いた場合でも,インタフェースによる挙動モデルの定義の原則を無視してはならない.実践の観点から見ると、抽象クラスに依存して動作を定義すると、複雑な継承関係を招くことが多く、インタフェース定義動作によって動作と実現をより効果的に分離することができ、コードのメンテナンスと修正に便利になる.(要約:抽象クラスは動作を実現するために定義でき、インタフェースは動作を定義するために使用される)
Javaインタフェースの特性学習Javaでインタフェースを見ると、最初に考えられるのはC++の多重継承とJavaのもう一つのキーワードabstractかもしれません.別の角度から多重継承を実現することはインタフェースの機能の一つであり、インタフェースの存在はJava内のオブジェクトを複数のベースタイプにアップグレードすることができ、抽象クラスと同様に他の人がクラスのオブジェクトを作成することを防止することができる.インタフェースはオブジェクトの作成を許可しないからである.
interfaceキーワードは、完全に抽象的なクラスを生成し、具体的な実装を提供しないインタフェースを宣言するために使用されます.interfaceの特性は以下のように整理されています.
1. インタフェース内のメソッドには、パラメータリストと戻りタイプがありますが、メソッドボディはありません.2. インタフェースにはフィールドを含めることができますが、staticとfinalとして暗黙的に宣言されます.3. インタフェースのフィールドは、インタフェースの静的記憶領域にのみ格納され、インタフェースには属しません.4. インタフェース内のメソッドはpublicとして宣言するか、宣言しないかを宣言できますが、結果はpublicタイプで処理されます.
(メソッドデフォルトpublic)5. インタフェースを実装する場合は、定義されたメソッドをpublicタイプとして宣言する必要があります.そうしないと、デフォルトのアクセスタイプとなり、Javaコンパイラでは許可されません.6. インタフェース内のすべてのメソッドが実装されていない場合、作成されたのは依然としてインタフェースです.7. 新しいインタフェースを生成するためにインタフェースを拡張するには、キーワードextendsを使用し、implementsを使用するインタフェースを実装する必要があります.
interfaceはabstractと似ている場所がありますが、クラスを宣言するには主に次の2つの点を参照します.1.メソッド定義とメンバー変数を持たないベースクラスを作成する場合は、抽象クラスではなくインタフェースを選択する必要があります.2.クラスがベースクラスであるべきであることを知っている場合は、抽象クラスを選択するのは、メソッド定義とメンバー変数が必要な場合のみです.抽象クラスには、1つまたは複数の特定の実装方法が存在することを可能にするため、メソッドがすべて実装されていない限り、そのクラスは抽象クラスである.以上がインタフェースの基本的な特性と応用分野であるが,インタフェースは決してそれだけではなく,Java構文構造ではインタフェースをネストすることができ,あるクラスにネストすることもできるし,インタフェースにネストすることもできる.これは実際の開発では多く応用される可能性はないが,その特性の一つでもある.インタフェースを実装する場合、内部にネストされたインタフェースを実装する必要はなく、privateインタフェースはクラスを定義する以外に実装できないことに注意してください.