読者です 読者をやめる 読者になる 読者になる

ボクココ

サービス開発を成功させるまでの歩み

デザインパターン(コマンドパターン) vs メタプログラミング

最近はAndroidを触るようになって、Java的な感覚もつかめてきたような気がする今日この頃。
ふと思ってデザパタとメタプロの比較を頭の中で思ったので書き起こそう。※ツッコミ大歓迎

シチュエーション

とある処理はユーザが自由に書くことができ、フレームワーク側でそれら一連の処理をまとめて実行させてあげるような枠組みを提供したい。

Java(コマンドパターン)による実装例

// ユーザが書く部分
class Test1 implements Test
public void action(){
//ユーザが自由に記述
}
}
class Test2 implements Test
public void action(){
//ユーザが自由に記述
}
}

class Main{
public void static main(String args[]){
TestManager mng = new TestManager();
mng.add(new Test1());
mng.add(new Test2());

mng.exec();
}
}

//フレームワーク提供側が書く部分
public interface Test{
public void action();
}
class TestManager{
private List tests;
public TestManager(){
tests = new ArrayList();
}
public void add(Test test){
tests.add(test);
}
public void exec(){
for(Test test: tests){
test.action();
}
}
}

解説
こういう時interfaceって超便利!って思う。
違う型をinterfaceを通じて同じようにメソッドの実行ができる。これはRubyだとダックタイピングで当たり前のことだが、Javaだとありがたみを感じますわ。
コマンドパターンの場合、処理を一個一個クラスのインスタンスに保存しておかないといけない点がポイントですな。いちいちクラス作らないといけないけど、それによって指定したコマンドを的確に実行してくれる。
しっかりもののイメージがとても合うコード。

Ruby(メタプログラミング)による実装例


// ユーザが書く部分
class Test
def test1
//何らかの処理
end

def test2
//何らかの処理
end
end
TestManager.new(Test.new).exec

//フレームワーク提供側が書く部分
class TestManager
def initialize test
@test = test
end
def exec
@test.public_methods.each do |method|
@test.send(method) if method =~ /^test/
end
end
end

解説
処理は一つのクラスTestにまとめることができています。
処理を配列で保持するコマンドパターンとメタプログラミングでは考え方が全く違います。
public_methodsメソッドは、そのインスタンスの中のパブリックメソッドを全て取得します。
その中で、"test"から始まるメソッドを動的に実行することで、一個一個の処理を順番に実行することを可能にしています。
メタプログラミングの特徴として、
 ・クラスやインスタンスの中身を覗くことができる
 ・メソッド呼び出しを実行時に指定できる
があります。

言いたいこと

コードを見る限り、ユーザの書く量の少ないメタプログラミングの方がいいと思いませんか?
でも、Javaをいじって気づいたのですが、メタプログラミングにも「デメリット」があると感じました。理由を2つ挙げます。
1、「規約」を作り、それを守らなければいけない
今回の場合、"test"で始まるメソッド名を実行するようにしています。そうしないと、他の実行してほしくないメソッドも勝手に実行されてしまうからです。メタプログラミングを利用する場合、このようにフレームワーク実装側が何らかの「規約」を作り、フレームワーク利用側はその「規約」を守らなければいけません。これは最初慣れるまでの学習コストがかかるのと同時に、この「規約」に外れるようなことをしたい場合(例えばtestで始まらないメソッドも実行に含めたい)には自分で一から作るくらいのことをしないといけなくなります。
2、Eclipseマンセーができない
実行時にメソッドを実行するということは、もしかしたらそのメソッドが存在しないかもしれない訳です。Javaコードを見てみると、そんなことがあった場合はEclipse様が事前に「そんなメソッドないよ」と知らせてくれます。Rubyの場合はそれができないので、実行時にエラーが発生してしまうことも少なくありません(だからテストコードを書くのは大事!)。そう考えると、Javaの方がしっかりとコードを書けるので健全なコードが書けます。


一長一短ですね。一応Javaにも「リフレクション」っていうメタプログラミングができる仕組みがありますが、使ってみると例外処理をたくさん書かないといけなくて、ちょいと面倒っす。
それでもメタプログラミングはクールでカッコイイと思いませんか?w