クラスインターフェイスとコレクション

優れたクラスインターフェイスとは何かという質問は簡単ではありません。 インターフェイスにどのメソッドを含める必要があり、パラメータは何である必要がありますか?このインターフェイスをいくつかに分割する必要さえありますか? プロジェクトの開発中にインターフェースはどうなりますか、変更する必要がありますか? 確かに多くの人がそのような質問をしました。 コレクションへのアクセスを提供するインターフェイスについての考えを共有します。



他の機能の中でも特に、キーによって順序付けられた行のセットを取得できるいくつかのコレクションのインターフェイスがあるとします。 つまり、次のようなメソッドが必要です

List<String> getElements(String key);
      
      





ただし、これらのセットが巨大な場合や、すべての行を一度に取得するのが難しい場合があります(たとえば、一部の実装では、愚かなプロトコルを使用して低速のWebサービスからそれらを要求します)。 そして、それらを適用します。たとえば、ページナビゲーションで画面に表示したり、パーツを読み込んだりします。 ここで、一部の開発者は、次のようなインターフェースを拡張するアイデアを思いつきます。

 public interface MyCollection { List<String> getElements(String key); String getElement(String key, int index); List<String> getElementsRange(String key, int fromIndex, int toIndex); int getElementsCount(String key); }
      
      



このようなインターフェイスは、商用コードまたは無料のコードで時々見かけます。 たとえば、 OpenFire JabberサーバーのUserProviderインターフェース(getUsers、パラメーター付きのgetUsers、getUserCountがあります。2つのfindUsersメソッドを持つ同様の例)。 考えてみれば、追加のメソッドは必要ありません。 getElementsを使用して簡単に表現できます。



 String getElement(String key, int index) { return getElements(key).get(index); } List<String> getElementsRange(String key, int fromIndex, int toIndex) { return getElements(key).subList(fromIndex, toIndex); } int getElementsCount(String key) { return getElements(key).size(); }
      
      



さらに、実装は非常に単純であるため、構文糖のためにも新しいメソッドを導入することは疑わしいです。 ここで経験の浅い開発者は次のように述べています。「最初のメソッドはリスト全体を返します。リストが大きく、メモリに収まらない場合は? 一般に、必要な要素が1つだけの場合、それは、Listインターフェイスが具体的な実装(通常はArrayList)に強く関連付けられており、Listメソッドを自分のインターフェイスと同じくらい遅延させることができることを忘れているためです。



補助的なメソッドは、明らかな利点ではありませんが、干渉しないことに反対する場合があります。 ただし、経験から、干渉することがわかります。 追加の不要なメソッドを備えたこのようなインターフェイスがあるため、開発者は、遅延リストを作成することなく、getElementsの要素の完全なリストを含むArrayListを実際に返す資格があると考えます。 その結果、たとえば、getElements(key).size()は機能しなくなります。これは、膨大な数の要素がメモリに読み込まれ、終了するためです。 したがって、ユーザーはgetElementsCount(キー)の実装を使用する必要があります。



たとえば、要素の数、最初の数個、および省略記号(要素がさらにある場合)を含む文字列を表示するユーティリティメソッドがあるとします。 たとえば、次のように:

[100500] First, second, third...





実装は次のようになります。

 public static String getSummary(List<String> list) { StringBuilder sb = new StringBuilder(); int size = list.size(); sb.append('[').append(size).append("] "); int maxElements = Math.min(3, size); for(int i=0; i<maxElements; i++) sb.append(i==0?"":", ").append(list.get(i)); if(maxElements < size) sb.append("..."); return sb.toString(); }
      
      





そして、あなたは冷静にgetSummary(myCollection.getElements(key))を呼び出します。 しかし、ここでは、多くの遅延実装があり、生活が複雑であることを思い出します。 結果は、見苦しいパラメーターのセットを持つメソッドです。 最初のアプローチは、getSummary(MyCollectionコレクション、Stringキー)メソッドを作成することです。 再利用可能な美しい独立したメソッドから、コレクションインターフェイスに依存するメソッドを取得しますが、これは他のメソッドには適用できません。 2番目のアプローチは、getSummary(int count、List firstThreeElements)メソッドを作成し、getSummary(myCollection.getElementsCount(key)、myCollection.getElementsRange(key、0、3))を介して呼び出すことです。 メソッドはインターフェイスに関連付けられていませんが、メソッドインターフェイスはその実装に関連付けられているため、このようなソリューションはさらに悪化します。 ユーザーが最後の要素も表示したいことを想像してください。

[100500] First, second, third... last





メソッドの実装に1行追加する代わりに、そのインターフェイス、つまりメソッドが呼び出されるすべての場所を変更する必要があります。 これはひどいです。



良い解決策は、Listインターフェースを実装するアダプターを作成することです。 次に、呼び出しはgetSummary(新しいMyCollectionListAdapter(myCollection、キー))のようになります。 しかし、これはまだ不必要な複雑さです。最初からうまくやれるからです。 さらに、このようなアダプターは最初は制限されています。 たとえば、MyCollectionインターフェイスには目的のメソッドがないため、List.containsまたはList.indexOfを最適に実装することはできません。 たとえば、MyCollectionの特定の実装で、リストが実際にSQLデータベースからロードされる場合、containsは簡単に最適化されます。すべての要素を反復処理する必要はありません。



MyCollectionインターフェースがgetElementsのみを持っている場合、それが実装されると、プログラマーはすべてをうまく実行し、怠zyなリストを実装する以外の選択肢はありません(もちろん、これは小さなコレクションを知っているため無視できます)。



一般的に、私はあなたのために既に作成されているそれらの良いインターフェースを複製してはならないことに注意したいです。 この方法でインターフェイスを詰まらせると、最終的には害よりも害が大きくなります。 標準コレクションのインターフェースも実装することを恐れないでください。 Mapの場合でも、これはまったく難しくありません。Listの場合はさらに難しくなります。 Java.utilには、AbstractListなどのヘルパークラスがあります。



All Articles