エンジニアが発信する【2005年12月】の記事です

エンジニアが作る最新ITブログ トップ>【2005年12月】

2005年12月29日

インターフェイスを直接newしてしまうサンプル

今年は先のエントリはおしまいかと思っていたら、コメントを頂いてしまったので、またもや「はてな」からのコピーです。

「直接インターフェイスをnewする」ネーミングが決められないので、java++(仮)とするが、サンプルを考えてみた。
・・・
まずGreetというメッセージをどこにでも書き込むライブラリを題材にする(ログ出力と同じようなもの)。
まずインターフェイスはGreet、メソッドはvoid send(String)とする。つまり、以下の定義でこれは以前と変わりない。

public interface Greet {
void send(String msg);
}

・・・
デフォルトの実装クラス(参照実装)としてGreetStdoutとする。名の通り、System.outにprintlnするだけの実装。これも以前と変わりはない。

package impl;
public class GreetStdout implements Greet {
void send(String msg) {
System.out.println(msg);
}
}

・・・
このインターフェイスを使ってみる。まずは実装依存したコーディングだと、次のように直接実装クラスをimportして、直接newすることになる。

import impl.GreetStdout;
public class Main {
public static void main(String[] args) {
Greet g = new GreetStdout();
g.send("こんちは");
}
}

・・・
もし、インターフェイスを直接newできるのなら、次のコーディングとなる。

public class Main2 {
public static void main(String[] args) {
Greet g = new Greet();
g.send("こんちは");
}
}

・・・
今のコンパイラではMain2はコンパイルできない。いまあるGreet, Main2だけでは、インターフェイスGreetの実装クラスを解決できないからだ。そこでGreetにデフォルトの実装クラスを教えてやる。(#アノテーション自体に自信はないですので・・・)

@ReferenceImplemention impl.GreetStdout
public interface Greet {
void send(String msg);
}

・・・
新しいGreetインターフェイスで定義してあれば、Main2をコンパイルする際にnew Greet()の実装クラスが決定できなくても、とりあえずimpl.GreetStdoutで仮押さえができるので、後はランタイムさんに任せても問題はないはず。
・・・
初期のc++コンパイラはプリプロセッサでcのソースに変換していた(はず)。今回のjava++(仮)も同様にプリプロセッサでjavaのソースに変換できる(はず)。
上記の例ではGreetのファクトリークラスを自動生成してやればいい。またファクトリークラスのデフォルトを、Greetインターフェイスで指定されたアノテーションの情報を利用する。

//it's generated from Greet.java
package generated;
public class GreetFactory {
private static GreetFactory singleton = new GreetFactory();
public static GreetFactory getInstance() { return singleton; }
public Greet newInstance() { return new impl.GreetStdout(); }
}

//it's generated from Main2.java
import generated.GreetFactory;
public class Main2 {
public static void main(String[] args) {
Greet g = generated.GreetFactory.getInstance().newInstance();
g.send("こんちは");
}
}

このGreetFactoryではimpl.GreetStdout()をnewしているだけだが、これを設定ファイルを見て動的にインスタンス生成するようにする。当然のことながら、自前で作るのではなく、DIコンテナに委譲してやってよい。
・・・
実装クラスの割り当て(attach)の設定ファイルとしてlog4jが参考になる。つまり、パッケージ名.クラス名の全部または一部で実装クラスをオーバーライドする。次の例ではerror以外のパッケージで使用するとデフォルトのGreetStdout、errorの配下でerror.file以外ならGreetStderr、FatalErrorクラスならGreetEventWriterとなる。

error.*=impl.GreetStderr
error.file.*=impl.GreetFileWriter
error.file.FatalError=impl.GreetEventWriter

まぁ、メソッドまで特定することもできるかな。下記の設定があれば、FatalError.sendsoap()内でnew Greet()するとnew impl.GreetSendSoap()され、FatalError.sendmail()内でnew Greet()するとnew impl.GreetSendMail()されることになる。

error.file.FatalError#sendsoap=impl.GreetSendSoap
error.file.FatalError#sendmail=impl.GreetSendMail

・・・
個々のサンプルのソースをあえて出していないが、error.Main3でも、error.file.Main4でも、error.file.FatalErrorでも、すべてnew Greet();と記述することになる。つまりGreetインターフェイスのMockクラスがデフォルトとして設定されていれば、Greetインターフェイスを利用するメソッドはimportすればすぐに作れる。
これこそEoD(Ease of Development)だと思うんです。
・・・
・・・
先のGreetFactory部分が文字通り固定のファクトリだったので、propertiesファイルを見るファクトリにしてみた。ただしクラス名との完全一致なので、実際に使おうと思うと設定は面倒です。クラス名に一致する最大のキーを探すロジック(一致しなければ'*')が必要。

public class GreetFactory {
private static String PATH = "jp/hiuchida/generated/Greet.properties";
private static Properties props = new Properties();
static {
try {
InputStream is = new FileInputStream(PATH);
props.load(is);
is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

private static GreetFactory singleton = new GreetFactory();
public static GreetFactory getInstance() { return singleton; }

public Greet newInstance() {
return newInstance("*");
}
public Greet newInstance(Class c) {
return newInstance(c.getName());
}
public Greet newInstance(String s) {
try {
String name = props.getProperty(s);
return (Greet)Class.forName(name).newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}

[Greet.properties]
*=jp.hiuchida.impl.GreetStdout
jp.hiuchida.Main=jp.hiuchida.impl.GreetStderr

・・・
して、むかし作ったSpringの勉強サンプルを見て、GreetFactoryにSpringを乗せてみる。

public class GreetFactory4Spring {
private static String PATH = "jp/hiuchida/generated/Greet.xml";
private static XmlBeanFactory factory = new XmlBeanFactory( (Resource)new ClassPathResource(PATH));

private static GreetFactory4Spring singleton = new GreetFactory4Spring();
public static GreetFactory4Spring getInstance() { return singleton; }

public Greet newInstance() {
return newInstance("*");
}
public Greet newInstance(Class c) {
return newInstance(c.getName());
}
public Greet newInstance(String s) {
return (Greet)factory.getBean(s);
}
}

[Greet.xml]
"http://www.springframework.org/dtd/spring-beans.dtd">





・・・
で、プリプロセッサはnewInstance()じゃなくて、newInstance(Class)を生成してもらう。

Greet g;
//REPLACE by pre-processer
//g = new Greet();
g = GreetFactory4Spring.getInstance().newInstance(Main.class);
g.send("こんちは");

・・・
GreetFactory4Springと命名しているけど、実際にはGreetWrapper4Springだな。
単なるファクトリーでしかないが、Greet.xmlはSpringの世界なので別のインスタンスをinjectすることは可能。



2005年12月28日

インターフェイスを直接newできる言語仕様にしてしまう

今年最後のエントリは、今年最後?に思いついたこと。「はてな」に書いたメモのコピーです。
・・・
javaでインスタンスを生成する際に、実装クラスをnewするのではなく、インターフェイスをnewする。
・・・
DIコンテナをひとつの外部ライブラリとするのではなく、言語実装の一部とする。つまり、DIは空気のようなもの。
・・・
javaでインスタンスを生成するとき、実装クラスをnewするのが一般的。たとえばArrayListならば、

List items = new ArrayList();

みたいになる。これでは実装クラスに直接依存している。そこでDIコンテナの出番なのだが、DIコンテナの実装にも依存するし、そもそも面倒くさい。今日は12/28だが、今年中にバッチプログラムを5本作ってくれといわれたら、DIコンテナを利用して実装依存しないような開発をするだろうか・・・!? するわけないでしょ。
・・・
そこで、インターフェイスをnewしてやるんだ!!!

List items = new List();

当然、コンパイル時に実装クラスを解決できないので、既存のコンパイラではエラーとなる。でも実装クラスなんていうのは、ランタイム時に決定できればいいんじゃん。つまりランタイム(VM)にDIコンテナが空気のように内臓されているのなら、上記のnew List()は当たり前のコーディングのはず。そして誰でも、どんなに忙しくても、このスタイルのコーディングができるはず。
・・・
ランタイム時にnew List()の実装クラスを解決するわけだが、おそらく設定ファイルの類になる。すると規模が大きいと定義の数が増えて面倒だ。そこで、interface List内にアノテーション等でデフォルトの実装クラス(参照実装ともいえるかな)は何だという定義を埋め込んでおけばよい。すると、new List()のデフォルトはnew ArrayList()となり、もし違う実装クラスに変更したければ、設定ファイル等でオーバーライドすればよい。
・・・
似ているけどまったく異なるものに匿名クラスがある。

Runnable r = new Runnable() { public void run() { ... } };

こいつもインターフェイスをnewしているが、実装も記述しているのでコンパイル時に解決している。つまり上記のDIとは関係なく、たんにクラス名を考えなくていいだけです。
・・・
そもそもインターフェイスを直接newできる仕様にするなんて考えた人間はいるのだろうか。
思いついただけで調べていないので、知っていたら教えて。
もしないのなら、これこそすぐにJavaSEレベルで欲しい機能だと思うんだけどね。
・・・
どうぞよいお年を。



2005年12月01日

eclipse v3.0で長い間悩んでいたこと

eclipseをv3.0にVerUpしてから長い間悩んでいました。

日本語化パッチを入れると、Searchメニューだけ英語のまま残り、さらに悪いことに「ソース(S)」のショートカットが効かなくなる(Alt + S)。

ショートカットが効かないというのが結構面倒なところでして、なんとかならないものかと最近になって探したら、eclipse wikiで見つかった。⇒コメント/日本語化

最初はconfigurationを消してみたが直らず、『EclipseSDK3.0にLanguagePack3.0.xを展開すると、そのような現象が発生するようです。EclipseSDK3.0.1にLanguagePack3.0.xを展開すると完全に日本語化されます。ファイルを削除したり設定を行ったりする必要は全くありません。私自身もちょっとはまったのでご報告を。 -- 2004-10-07 (木) 01:57:50』のコメント通りだった。

あぁ、wikiの強さだな。もっと性善説で人間の集まりを見ないと・・・。



2005年12月01日

リンク for MYCOM 05年11月

コーディングスタンダードから設計欠陥までチェック - Checkstyle 4.0 公開

Java APIを使用するC++ラッパクラスを生成 - Java for C++ 0.1

Microsoftら、OfficeのファイルフォーマットをXMLで標準化へ

RMI/JAX-RPC/JNDIもプロパティで一括化 - Crispy 0.6.2公開

グループブログを実現、Javaベースのブログサーバ - Roller 2.0

Seasar、Webフロントサービスエンジン - Mayaa 1.0.0-beta1公開

Ruby on Rails統合開発環境 - RadRails 0.5公開

第28回アノテーション(3) - アノテーションを知る(2)

もうひとつの軽量IoCコンテナ - Yan Container 0.7.1公開

軽量高速、Javaバイトコードフレームワーク - ASM 2.2 公開

正式リリースまであとわずか - Groovy JSR 04 公開

スキーマからファイルを自動生成してAppFuseを支援 - AppFuse Generator

JUnitテストコードとテストデータを分離 - JTestCase 3.1.0 公開

軽量にして多機能 Java仮想マシン - JamVM 1.4.0 公開

Apache 新たに3つのプロジェクトをIncubatorへ提案、Geronimoの強化を狙う

アーキテクチャを再構築、高速に生まれかわる - Jetty 6 Comming soon

ひとつの情報源から複数用途のサイトを構築 - Cocoon 2.1.8 公開

JavaのオブジェクトをSQLで操作 - JoSQL 1.1

次期DB2"Viper"の姿が明らかに - XML/XQueryをネイティブサポート

ApacheとTomcatを接続する要、mod_jk 1.2.15 公開

オープンソースで手軽にSOA - エンタープライズサービスバスServiceMix 2.0

JavaOne Tokyo 2005 - 見えてきたDolphin、Mustangのさらに向こうDolphin世代のJavaはどうなる?

JavaOne Tokyo 2005 - 自動化、心掛けてますか? 貴重な時間は大切に

最新の技術を投入、Seasar 2.3 公開 - Persistence APIとJSFの実装も新たに

JavaとHTMLで全てを完了 - ウェブアプリケーションフレームワークWicket

「Seasar開発を強力支援」Seasar V2関連プロダクト - Kijimuna 1.0.0公開



エンジニアが作る最新ITブログ トップ>【2005年12月】

メンバー紹介

タグパネル

ランキング

エンジニアが作る最新ITブログ DODA