よりスマートなJava開発

大規模なJavaアプリケーションの開発をスピードアップするための迅速で単純なスキームには、インターフェースの使用が含まれます。Javaインターフェースは、関連するオブジェクトに含まれる機能の青写真です。

次のプロジェクトにインターフェースを組み込むことにより、開発作業のライフサイクル全体でメリットに気付くでしょう。オブジェクトではなくインターフェースにコーディングする手法により、開発チームの効率が次のように向上します。

  • 開発チームが、サポートオブジェクトの早期定義を強制することなく、必要なオブジェクト間の相互作用を迅速に確立できるようにします
  • 統合がすでに考慮されているという知識を持って、開発者が開発タスクに集中できるようにします
  • 主要なコード変更なしでインターフェイスの新しい実装を既存のシステムに追加できるように柔軟性を提供します
  • すべてのオブジェクトが設計どおりに相互作用することを保証するために、開発チームのメンバーによって合意された契約を実施する

概要

オブジェクト指向の開発作業にはオブジェクトの相互作用が含まれるため、それらのオブジェクト間の強力なコントラクトを開発して実施することが不可欠です。インターフェイスにコーディングする手法では、通信の主要な方法として、オブジェクトではなくインターフェイスを使用します。

この記事では、簡単な例を通じて、インターフェースへのコーディングの概念をユーザーに紹介します。詳細な例を次に示し、複数の開発者を必要とする大規模なシステムでこのスキームの価値を実証するのに役立ちます。ただし、サンプルコードに入る前に、インターフェイスにコーディングすることの利点を見てみましょう。

なぜインターフェースにコーディングするのですか?

Javaインターフェースは開発契約です。これにより、特定のオブジェクトが特定のメソッドセットを満たすことが保証されます。インターフェースは、Java API全体で使用され、オブジェクトの相互作用に必要な機能を指定します。インターフェイスの使用例は、コールバックメカニズム(Event Listeners)、パターン(Observer)、および仕様(RunnableSerializable)です。

インターフェイスへのコーディングは、開発者がオブジェクトの特定のメソッドをシステム内の他のオブジェクトに公開できる手法です。これらのインターフェースの実装を受け取る開発者は、オブジェクト自体にコーディングする代わりに、インターフェースにコーディングすることができます。言い換えると、開発者は、オブジェクト自体と直接対話するのではなく、そのオブジェクトのインターフェイスの実装と対話するコードを作成します。

オブジェクトではなくインターフェースにコーディングするもう1つの理由は、システムのライフサイクルのさまざまなフェーズでより高い効率を提供することです。

  • 設計:オブジェクトのメソッドをすばやく指定して、影響を受けるすべての開発者に公開できます
  • 開発:Javaコンパイラは、インターフェイスのすべてのメソッドが正しい署名で実装され、インターフェイスへのすべての変更が他の開発者にすぐに表示されることを保証します
  • 統合:確立されたインターフェースにより、クラスまたはサブシステムをすばやく接続する機能があります
  • テスト:インターフェースは、考えられる論理エラーの範囲をメソッドの特定のサブセットに制限するため、バグの分離に役立ちます

必要なコードインフラストラクチャのため、この開発手法に関連するオーバーヘッドがあります。このインフラストラクチャには、オブジェクト間の相互作用のためのインターフェイスと、インターフェイスの実装を作成するための呼び出しコードの両方が含まれています。説明したようにインターフェイスを使用することの容易さと利点と比較すると、このオーバーヘッドは重要ではありません。

基本例

インターフェイスへのコーディングの概念をさらに説明するために、簡単な例を作成しました。この例は明らかに些細なことですが、上記の利点のいくつかを示しています。

Carインターフェイスを実装するクラスの簡単な例を考えてみましょうVehicle。インターフェイスにVehicleは、と呼ばれる単一のメソッドがありstart()ます。クラスCarは、start()メソッドを提供することによってインターフェースを実装します。Carクラスの他の機能は、わかりやすくするために省略されています。

interface Vehicle {//すべてのVehicle実装はstartメソッドを実装する必要がありますpublicvoid start(); }クラスCarはVehicleを実装します{// Vehicleを実装するために必要ですpublicvoid start(){...}}

Carオブジェクトの基礎を築いたら、と呼ばれる別のオブジェクトを作成できますValet。それはValet開始するの仕事Carやレストランのパトロンにそれを持って来ます。Valet次のようにオブジェクトは、インターフェイスなしに書くことができます。

class Valet {public Car getCar(Car c){...}} 

Valetオブジェクトは、呼び出されたメソッドがあるgetCar返すCarオブジェクトを。このコード例はシステムの機能要件を満たしていますが、Valetオブジェクトをのオブジェクトと永久にリンクしていますCar。この状況では、2つのオブジェクトは緊密に結合されていると言われますValetオブジェクトは、の知識が必要ですCarオブジェクトを、そのオブジェクトに含まれるすべてのパブリックメソッドと変数にアクセスすることができます。依存関係が増加し、柔軟性が低下するため、このようなコードの緊密な結合は避けるのが最善です。

Valetインターフェイスを使用してオブジェクトをコーディングするには、次の実装を使用できます。

class Valet {public Vehicle getVehicle(Vehicle c){...}} 

コードの変更はごくわずかですが(参照をからCarに変更Vehicle)、開発サイクルへの影響はかなりのものです。2番目の実装を使用Valetすると、はVehicleインターフェイスで定義されたメソッドと変数についてのみ知識があります。Vehicleインターフェイスの特定の実装に含まれるその他のパブリックメソッドとデータは、Vehicleオブジェクトのユーザーから隠されます。

この単純なコード変更により、他のオブジェクトからの情報と実装の適切な隠蔽が保証され、したがって、開発者が望ましくない方法を使用する可能性が排除されました。

インターフェイスオブジェクトの作成

この開発手法に関して議論する最後の問題は、インターフェイスオブジェクトの作成です。new演算子を使用してクラスの新しいインスタンスを作成することはできますが、インターフェイスのインスタンスを直接作成することはできません。インターフェイスの実装を作成するには、オブジェクトをインスタンス化し、目的のインターフェイスにキャストする必要があります。したがって、オブジェクトコードを所有する開発者は、オブジェクトのインスタンスの作成とキャストの実行の両方を担当できます。

この作成プロセスはFactory、外部オブジェクトがで静的createXYZ()メソッドを呼び出しFactory、インターフェイスを返すパターンを使用して実現できます。また、開発者が別のオブジェクトのメソッドを呼び出して、実際のクラスではなくインターフェイスを渡す場合にも実現できます。これはEnumerationVectorまたはの代わりにインターフェイスを渡すことに似ていますHashtable

詳細な例

大規模なプロジェクトでこのスキームを使用する方法を示すために、会議スケジューラの例を作成しました。このスケジューラには、リソース(会議室と会議出席者)、オカレンス(会議自体)、およびスケジューラ(リソースカレンダーを管理するもの)の3つの主要なコンポーネントがあります。

Let's assume these three components were to be developed by three different developers. The goal of each developer should be to establish the usage of his or her component and publish it to the other developers on the project.

Consider the example of a Person. A Person may implement numerous methods but will implement the Resource interface for this application. I have created the Resource interface with all the necessary accessor methods for all resources used in this example (shown below):

public interface Resource { public String getID(); public String getName(); public void addOccurrence( Occurrence o); } 

At this point, the developer of the Person functionality has published the interface by which all users can access the information stored in the Person object. Coding to the interface helps ensure that no developers are using the Person object in an incorrect manner. The developer of the Scheduler object can now use the methods contained in the Resource interface to access the information and functionality necessary to create and maintain the schedule of the Person object.

The Occurrence interface contains methods necessary for the scheduling of an Occurrence. This can be a conference, travel plan, or any other scheduling event. The Occurrence interface is shown below:

public interface Occurrence { public void setEndDatetime(Date d); public Date getEndDatetime(); public void setStartDatetime(Date d); public Date getStartDatetime(); public void setDescription(String description); public String getDescription(); public void addResource(Resource r); public Resource[] getResources(); public boolean occursOn( Date d); } 

The Scheduler code uses the Resource interface and the Occurrence interface to maintain the schedule of a resource. Notice that the Scheduler does not have any knowledge of the entity for which it is maintaining the schedule:

public class Scheduler implements Schedule{ Vector schedule = null; public Scheduler(){ schedule = new Vector(); } public void addOccurrence(Occurrence o){ schedule.addElement(o); } public void removeOccurrence(Occurrence o){ schedule.removeElement(o); } public Occurrence getOccurrence(Date d) { Enumeration scheduleElements = schedule.elements(); Occurrence o = null; while ( scheduleElements.hasMoreElements() ) { o = (Occurrence) scheduleElements.nextElement(); // For this simple example, the occurrence matches if // the datetime isthe meeting start time. This logic // can be made more complex as required. if ( o.getStartDatetime() == d) { break; } } return o; } } 

This example shows the power of interfaces in the development phases of a system. Each of the subsystems has knowledge only of the interface through which it must communicate -- no knowledge of the implementation is required. If each of the building blocks in the above example were to be further developed by teams of developers, their efforts would be simplified due to the enforcement of these interface contracts.

Final thoughts on interfaces

This article has demonstrated some of the benefits of coding to interfaces. This technique enables greater efficiency throughout each phase of the development lifecycle.

During the design phases of the project, interfaces allow the quick establishment of the desired interactions among objects. The implementation objects associated with a given interface can be defined after the methods and requirements for that interface are specified. The more quickly the interaction is established, the more quickly the design phase can progress into development.

Interfaces give developers the ability to expose and limit certain methods and information to the users of their objects without changing the permissions and internal structure of the object itself. The use of interfaces can help eliminate the pesky bugs that appear when code developed by multiple development teams is integrated.

Contract enforcement is provided by the interface. Because the interface is generally agreed upon during the design phase of the project, the developers have the ability to concentrate on their individual modules without having to worry about the modules of their colleagues. Integrating these subsystems is made more efficient by the fact that the contracts have already been enforced throughout the development phase.

For testing purposes, a simple driver object can be created to implement the agreed-upon interfaces. Using this object, developers can continue their work with the knowledge that they are using the proper methods to access the object. When the objects are deployed in a test environment, the driver classes are replaced by the true classes, allowing the object to be tested without code or property changes.

This scheme provides the capability for easy expansion of this system; in our example, we could expand the code to include more forms of resources, such as meeting rooms and audio/video equipment. Any additional implementation of the Resource interface will fit into the established mechanism without modifying the existing code. Large-scale projects using this scheme could be designed and implemented in such a way that additional functionality can be added without major modification to the infrastructure. As an example, the ConferenceRoom object was created. This object implements the Resource interface and can interact with the Schedule and Occurrence implementers without changing the infrastructure.

もう1つの利点は、コードを一元的に配置できることです。新しいメソッドをResourceインターフェイスに追加する場合、このインターフェイスのすべての実装は変更が必要であると識別されます。これにより、インターフェイスへの変更による影響の可能性を判断するために必要な調査が削減されます。

開発の利点に加えて、この記事で紹介する手法は、プロジェクト管理に、オブジェクト間またはシステム間の通信パターンが開発サイクル全体で確立および実施されていることを保証します。これにより、プロジェクトの統合およびテスト段階での障害のリスクが軽減されます。