<< < 1 2 3 > >>

Wicket Code Reading - PageMapでVisitorパターン

最近は暇をみつけて、Wicketのソースを読んだりしてる。
最近追ってるのは org.apache.wicket.PageMapクラス。
ページ情報をバージョン管理しつつ格納するクラス。

今まで業務ではあまり見なかったVisitorパターンらしき物を発見したので、
Visitorパターンを勉強してみた。
Visitorパターンとは、操作とデータを分離するパターンで、名前だけは知ってたけど、
今までお目にかかることはなかった。

該当のコードをまとめると以下のようになる。
public abstract class PageMap {
    public void clear (){
	visitEntries(new IVisitor() {
 		public void entry(IPageMapEntry entry) {
		    removeEntry(entry);
		}
 	    });
    }

    public abstract void removeEntry(final IPageMapEntry entry);

    static interface IVisitor {
	void entry(IPageMapEntry visitor);
    }

    public void visitEntries(IVisitor visitor) {
	IPageMapEntry pageMapEntry = new Entry1();
	visitor.entry(pageMapEntry);
    } 
}

class PageMapImp1 extends PageMap {
    public void removeEntry(final IPageMapEntry entry) {
	System.out.println("VisitorSampleImp1#visit is called");
    }
}
 interface IPageMapEntry {}

 class Entry1 implements IPageMapEntry {
 }
通常のVisitorパターンと違う所はIVisiterの実装が無名クラスで定義されている。
あと、visitEntriesメソッドがVisitorパターンで言うAccepterクラスを代用している。

ここまでわかって、純粋なVisitorパターンではなさそうな気がしてきた。
ポイントはIVisitorのentryメソッド。
ここで引数がInterfaceになっている。自分が知っている限り、VisitorパターンではVisitor側のメソッドでは、
引数をInterfaceでなく、実装となるはず。
つまりデータに対する処理がInterfaceに依存してしまう事になる。

と言うことで、Interface名に惑わされたけど、これは純粋なVisitorパターンではなさそう。
Visitorパターンのいい勉強にはなったけど。

JavaのGenericsはこんなに便利だったのか!

今までListに対する型指定ぐらいしか使ってなかったGeneric。
Genericを使ったクラスを作ってるのは業務でも見る事がなかったけど、
先日から現場でいっしょになった方にいろいろ教えて貰った。
そもそもJava5を使う機会があんまりなかったのもあるけど。

Javaやってて違和感があるのが、Objectを引数にするときと、
キャストをするとき。せっかく指定した型が台無しになる。
型を指定する言語である以上、指定した型は大事にしたいというか。

型を大事にしつつ、処理の大まかな流れが流れが似通ったクラスをまとめる場合は、
Genericsが大変便利な事を思い知った。
例えば業務アプリではよくあるこんな流れ
1.DataBeanにDBから値を詰め込む
2.データチェック
3.画面に表示

こんな場合にInterfaceがなくてもGenericesは効果を発揮する。
下の例ではDBから値をとってくる所を、お手軽にメソッドで代用してみた。
public class Template {
    public static void main (String[] args) {
	DisplayFooBean dispFoo = new DisplayFooBean();
	dispFoo.dispData();
	DisplayHogeBean dispHoge = new DisplayHogeBean();
	dispHoge.dispData();
    }
}

class DisplayFooBean {
    public void dispData() {
	FooBean fooBean = getData();
	if (fooBean != null) {
	    System.out.println(fooBean.getFoo());
	}
    }

    private FooBean getData() {
	FooBean fooBean = new FooBean();
	fooBean.setFoo("foo");
	return fooBean;
    }
}

class DisplayHogeBean {
    public void dispData() {
	HogeBean hogeBean = getData();

	if (hogeBean != null) {
	    System.out.println(hogeBean.getHoge());
	}
    }

    private HogeBean getData() {
	HogeBean hogeBean = new HogeBean();
	hogeBean.setHoge("hoge");
	return hogeBean;
    }
}

class FooBean {
    private String foo;

    public final String getFoo() {
	return this.foo;
    }

    public final void setFoo(final String newFoo) {
	foo = newFoo;
    }
}

class HogeBean {
    private String hoge;

    public final String getHoge() {
	return hoge;
    }

    public final void setHoge(final String newHoge) {
	this.hoge = newHoge;
    }
}
Genericsを使うと、

public class Template {
    public static void main (String[] args) {
	DisplayFooBean dispFoo = new DisplayFooBean();
	dispFoo.dispData();
	DisplayHogeBean dispHoge = new DisplayHogeBean();
	dispHoge.dispData();
    }
}

abstract class AbstractDisplay <T> {
    public void dispData() {
	T t = getData();
	if (t != null) {
	    display(t);
	}
    }
    abstract void display(T t);
    abstract T getData();
}

class DisplayFooBean extends AbstractDisplay <FooBean> {
     void display(FooBean fooBean) {
	System.out.println(fooBean.getFoo());
    }
    FooBean getData() {
	FooBean fooBean = new FooBean();
	fooBean.setFoo("foo");
	return fooBean;
    }
}

class DisplayHogeBean extends AbstractDisplay <HogeBean> {
    void display(HogeBean hogeBean) {
	System.out.println(hogeBean.getHoge());
    }

    HogeBean getData() {
	HogeBean hogeBean = new HogeBean();
	hogeBean.setHoge("hoge");
	return hogeBean;
    }
}

class FooBean {
    private String foo;

    public final String getFoo() {
	return this.foo;
    }

    public final void setFoo(final String newFoo) {
	foo = newFoo;
    }
}

class HogeBean {
    private String hoge;

    public final String getHoge() {
	return hoge;
    }

    public final void setHoge(final String newHoge) {
	this.hoge = newHoge;
    }
}
AbstractDisplayクラスでは継承先に依存するBeanの部分をパラメータにしておき、
処理の流れを記述する。
AbstractDisplayクラスを見れば、処理の流れは大体分かるし、
継承先のDisplayHogeクラスではextends AbstractDisplay <HogeBean>と、型を指定してるので、
キャストも発生しない。

こんな感じで
  • InterFaceがなくても共通化がスマートにできて、
  • キャストも発生しない

C++使いにとっては、まだまだ物足りないJavaのGenericらしいけど、
もうちょっとはやってもいいよなぁ。

Findbugs と checkstyle で Continuous Integration

今の現場にきて5ヶ月が経ち、7月末で終了が決まった。
短い間だったけど、今までで一番学ぶところが大きい現場だった気がする。

自分が技術的にまだまだってのは痛いほどわかってるけど、
世の中にはシステムなんて動けばいいとか、ひどいと動かなくても我関せずみたいな人が結構いるってことがよくわかった。
ほんでもって組織というのは、そういった低い品質をレビュー等でちゃんとフィルターかけて、
一企業として恥ずかしくない品質の納品物をお金と交換してると、我ながら青臭いけど信じてた。
で、実際はこの前のエントリーみたいのが納品されてくる。

じゃあ自分ならどうするって考えると、ツール等の仕組みに頼るってのが、答えの一つだと思う。
丁度testearly.comのAgile Developer’s Toolboxで、そういった仕組みをサポートするいろいろなEclipse Pluginの紹介をやってたので、
試してみた。まずはCheckStyle & FindBugs。
それにしてもにAgileって単語がどうも好きになれないのは何でだろ。

で、testearly.comではCheckStyleが主に紹介されてて、FindbugsとCheckstyleが比較対象みたいに書いてあるけど、
Chekcstyleはコーディング規約、Findbugsはバグつぶしという棲み分けで両方使えばいいと思う。
FindbugsはDBとのコネクションクローズ忘れを見つけてくれるけど、Checkstyleはそういったことはやってくれない。
ちなみに以前Checkstyleは現場で使った事があって、なぜかプロジェクトの後半で摘要しようって決まって、
チェックを通すのに四苦八苦した覚えがある。
当たり前だけど、初めから計画的に摘要すべきやね。

HTTP Unit + DBUnitでデグレ防止

こっちに引っ越してからはの仕事は、WEBアプリのテストと保守のお手伝い。
保守の段階でデグレしないようにJunit でUnitTestを書こうと思ったけど、
1つのmethodが長すぎる上に、独自フレームワークで影響する出力がよくわからず断念した。

で、せっかくアプリは出来上がってるので、
Webアプリとの通信をシミュレートしてくれるHTTPUnitを使うことにした。
+ DBUnitの組み合わせで概ねデグレ防止になるという判断。
ちなみにSeleniumも試したけどFrameがあると動かなかったり、
肝心のassert関連のドキュメントが見つからなかったのでやめた。

HTTPUnitも一部のJavaScriptが動かなかったりしたけど、
大体の画面は適用することができたし、デグレ防止にも大活躍。

ってことで、既存アプリでブラックボックスな部分が多い場合は、
HTTPUnit + DBUnitはかなり効果的だった。
動かなかったJavaScriptの部分は要調査やね。

それにしてもせっかく 画面は共通的な設計になっているのに、
コードはコピペってのはもったいない実装でした。



FileEncoding

Javaのファイルの読み込みで文字化け。
どこで文字化けしてるか切り分けが難しいところだけど,
どうもファイルの文字コードと、実際に読み込む文字コードが合ってないっぽい。
そーいえば日本語ファイルを扱うって今まで無かったな。

システムのデフォルト文字コードでファイルを読み込むので、
System.getProperty("file.encoding")でファイルの読み書きに使用するエンコード方式を調査。
InputStreamReaderを使って文字コードを明示的に指定して解決。

開発機とサーバ機でOSのデフォルトエンコードが違うと厄介。