この話を新人にしたら、とても難しいらしい。
で、たぶん、今後はこの話を封印してしまうと思うので、
忘れないように自分用にメモメモ。
■なぜフレームワークを使うのか?
※ここでいうフレームワークは、strutsとかspringとかのWeb用の
フレームワークをさしています。
たとえば、ブラウザを使って、Web経由で会員登録するプログラムを考えます。
1から作るとすると、
・ブラウザに表示する画面を作る
・ブラウザ<=>サーバー間送受信プログラムを作る
・サーバー側で会員データをDBに登録するプログラムを作る
と、3種類のプログラムを作らなければいけません。
ここで、ブラウザに表示する画面を作る人は、かっこいい画面を作りたいです。
お客さんの要望に応じて、修正いっぱい入るかも!
DBは、将来的に修正があるかもしれません。でも基本的にやることは変わらないかな・・
ネットワーク通信部分は、そうそう変わらないです。
このように、修正タイミングがちがいます。
もし、1本のプログラムで書くとすると、大変です。画像修正+DB変更のたびに、
修正しなきゃいけないです。
さらに、必要な知識も違います。
ネットワークの部分の知識は、画面作成者にはいらないかもしれません。
ということで、これらの部分を分けて、
・ブラウザに表示する画面を作る
ところは、修正が入っても他に影響させず
・ブラウザ<=>サーバー間送受信プログラムを作る
は、難しいので知識がある人が作るけど
修正はしないようにする
・サーバー側で会員データをDBに登録するプログラムを作る
は、DB修正があっても、できるだけ、他に影響させないようにする
というように、影響が他にでないように作りたい
これを実現するのがフレームワークで、とくに、
・ブラウザに表示する画面を作る
・サーバー側で会員データをDBに登録するプログラムを作る
部分は個人で作成するけど、
・ブラウザ<=>サーバー間送受信プログラムを作る
部分は、手を出させなくさせたい。
そこで、ここをフレームワークで作成(隠蔽)し、他の部分を
作ってもらうということになります。
フレームワークから作った部分を呼び出します。
これが、ハリウッドの法則(私を呼び出すな、必要なら私から呼び出す)
になってきます。
■どうしてフレームワークが作れるのか?
したがって、フレームワークのしくみは、こんな感じになります。
(1)画面の表示内容をHTML+JavaScriptで記述、
サーバー側にREST型(GETまたはPOST)でアクセスして
入力内容をサーバーと通信する
(2)サーバー側で受け取ったら、
受け取った内容をもとに、起動したいプログラムを起動する
その際に、受け取った内容を引数として渡す
プログラムから、表示したい内容を受け取る
(3)サーバーは、受け取った内容をもとに、表示する
内容を受け取ってから、表示するまでには、いろいろ方法がある
ここで、(1)、(3)は作れそうです。
文字列を作るだけだから。
問題は、(2)です。
引数で受け取れるのは、文字列。
起動するのは、クラスなど
PHPやRubyならわかります。evalを使えば、文字列をプログラムと
見なして実行できます。
Javaは?
■リフレクション
Class.forName(クラス名)を書くと、実行できます。
たとえば、以下のソースは、受け取った第一引数をクラスとして起動し、
第二引数以下を、その起動したクラスに渡します。
import java.util.*;
public class MyReflection {
public static void main(String args[])
{
ArrayList<String> in = new ArrayList<String>();
ArrayList<String> out = new ArrayList<String>();
try {
Class cl = Class.forName(args[0]);
MyReflectionInterface my = (MyReflectionInterface)cl.newInstance();
for(int i = 1 ; i < args.length ; i++)
{
in.add(args[i]);
}
my.execute(in, out);
} catch (Exception e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
for(int i = 0 ; i < out.size() ; i++)
{
System.out.println(out.get(i));
}
}
}
|
たとえば、このクラスをコンパイルして実行し、引数を Class1 a b c とかにすると、
以下のクラスが見つかれば、じっこうしてくれるわけです。
import java.util.ArrayList;
public class Class1 implements MyReflectionInterface{
@Override
public int execute(ArrayList<String> in, ArrayList<String> out) {
for(int i = 0 ; i < in.size() ; i ++)
{
out.add(in.get(i));
}
return 0;
}
}
|
同様に引数を Class2 a b c とかにすると、Class2が見つかれば、じっこうしてくれるわけです。
同様に引数を Class3 a b c とかにすると、Class3が見つかれば、じっこうしてくれるわけです。
これらは、すべて、MyReflectionInterface
import java.util.ArrayList;
public class Class1 implements MyReflectionInterface{
@Override
public int execute(ArrayList<String> in, ArrayList<String> out) {
for(int i = 0 ; i < in.size() ; i ++)
{
out.add(in.get(i));
}
return 0;
}
}
|
を実装しているので、MyReflectionでmy.execute(in, out);と呼び出せるわけです。
この仕組みを使うと、
・(1)で受け取った引数などから、あらかじめ記述されている設定ファイル
を見て、起動するクラスを決定する
・起動するクラスを文字列で受け取り、それを起動する
ということが実現できます。
Servletにおけるweb.xmlのurl-patternとservlet class,
struts-config.xmlにおけるaction-mappingなど、この関係に成っています
たとえば、servletの場合、web.xmlのurl-patternにもとづきservlet class
をうけとります。Tomcatのなかでは、上記のMyReflectionと同じ仕組み
があって、servlet classで指定されたクラスを実行するわけです。
このとき、MyReflectionInterfaceに相当するものが、HttpServletになるわけです
(インターフェースじゃなくって、継承だけど)
ということで、つぎは、実際に流れるネットワークデータになるんだけど、
それについては、長くなるので、またこんど