Forwarding Collections

Forwarding Collectionsのクラスは、
java.io.inputStreamで使われているようなデコレーション(Decoration)パターンをコレクション系のクラスに使うときに便利なクラスです。
直接、Extendするより柔軟に使えますし。コレクション系のクラスを拡張するときは、まず使って行こうと思います
(ArrayListをextendsしたら、LinkListには使えなくなる)

クラス一覧などはFowarding関連のAPIをご覧ください
またコードを直接見たほうが理解しやすい場合もあります。
例えば、FowrdingObject.java (下はコメントなどを除去) だけのクラスです。

ただコレクションのクラスをoverwriteしていないので、デコレーションの例としてはいま1つわかりにくかったですね。Listとか見てみてください。

public abstract class ForwardingObject {
  protected ForwardingObject() {}

  protected abstract Object delegate();

  @Override public String toString() {
    return delegate().toString();
  }
}


Forwardingの使用例

ForwardingListを使った例

ここでは、FowwardingListを拡張して、配列の指定のindexのアイテムを上下に移動できるようにしてみました。

結局自分で、すべてのメソッドoverwriteする手間を考えたらGoogle Collecions使うのが便利ですね。

このMovableListは、ボタン押したら、並び方が変わる時に使っています。
public static void main(String argv[]){
List<String> array=Lists.newArrayList("1","2","3","4");
MovableList<String> mo=new MovableList<String>(array);
mo.moveUp(2);
System.out.println(Joiner.on(',').join(mo));
array=Lists.newLinkedList(array);
mo.moveDown(2);
System.out.println(Joiner.on(',').join(mo));
}

MovableList.java


/*
 * 
 * Copyright 2010 akjava.com 

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 * 
 */
package gaeada;

import java.util.List;

import com.google.common.base.Preconditions;
import com.google.common.collect.ForwardingList;

public class MovableList<E> extends ForwardingList<E>{

private List<E> delegate;
public MovableList(List<E> delegate){
this.delegate=delegate;
}
@Override
protected List<E> delegate() {
return delegate;
}
public boolean moveUp(int index){
Preconditions.checkArgument(index<size()&&index>=0);
if(index==0){
return false;
}
E element=remove(index);
add(index-1, element);
return true;
}
public boolean moveDown(int index){
Preconditions.checkArgument(index<size()&&index>=0);
if(index==size()-1){
return false;
}
E element=remove(index);
add(index+1, element);
return true;
}
public boolean moveTop(int index){
Preconditions.checkArgument(index<size()&&index>=0);
if(index==0){
return false;
}
E element=remove(index);
add(0, element);
return true;
}
public boolean moveBottom(int index){
Preconditions.checkArgument(index<size()&&index>=0);
if(index==size()-1){
return false;
}
E element=remove(index);
add(size()-1, element);
return true;
}

}


FowrardingMultisetを使った例


Google Collections Library内部での使用例

forwarding collectionsはGoogle CollectionsのBiMapやMultisetやMultimapの中で使われています。
UnmodifiableMultisetやUnmotifiableMultimapの元になっています。

 以下のように不要なメソッドを上書きすることで、修正不可を実現しています。
機能を加えるだけでなく機能を省くのにもForwardingは使えるのですね。

    @Override public boolean add(E element) {
      throw new UnsupportedOperationException();
    }


    @Override public boolean remove(Object element) {
      throw new UnsupportedOperationException();
    }


Forwardingを使った他の例 by Google Code Search

やはり、実例のコードをを見たほうがわかりやすいこともあります。
addした数を数えたり、遅延ロードしたりを実現しています。addと同時にリスナー追加などもしています。

ForwardingList
ForwardingMultiMap

デコレーション(Decoration)パターン

このForwarding Collectionsの理解には、Decorationの理解が不可欠です。使った例としてはInputStreamがJavaでは有名です。
デザインパターンの1つであるデコレーションパターンについては、以下リンクを見てみることをお勧めします。

Comments