Home > Tags > UML
UML
Java: イベント駆動によるModelとViewの分離 - Observer パターン
- 2009-03-17 (火)
- Java
よくGUIやWebアプリの簡単なサンプルソースなどは、UIとアプリケーションのロジックが同じクラスまたはメソッドに書かれている場合が多いです。それはそのサンプルがある特定の機能や関数の紹介の為に簡潔に書いているのですが、仮にいざそのソースを元にアプリを作りこんで機能の追加を行っていくとUIとアプリのロジックは分離したほうが保守・拡張と共に行いやすいです。
下記のプログラムは1秒毎に数値をカウントし、それを2進数と10進数でGUI上のラベルに出力する機能をモデルとビューに分けています。すなわち、数値のカウントをするモデルと数値をUIに表示するビューに。(いくつかの言語ではGUIの部品としてタイマーがあるようですが。。)
実行結果
クラス図
インスタンスの生成はコンストラクタで行うのがいいのですが、はしょっています。さて、これまでに何らかのフレームワークを使っていた方には上クラス図はMVCの説明図として見慣れているかもしれません。今回ModelとViewを繋ぐControllerの役割はCountListenerが担っています(下記コードではCountChangeEvent経由でModelインスタンスを渡すのみですが)。
処理手順
ビュー側で自身のインスタンスをモデルに登録し(モデル.addCountListener(ビュー))、モデルのデータが更新された場合、モデル→ビューへイベントを送出。イベント内部にモデルのデータがあり、そのデータでビューがUIを更新します。イベントの発生順序等は下図のシーケンス図のようになります。
シーケンス図
無限ループに注意
ビューを更新するイベントリスナーのメソッド(ここではcountChanged())で再度モデルのプロパティ変更メソッド(ここではsetCount())を用いると、再度イベント通知処理(notifyToListeners())が発生することで、無限ループになるので注意が必要です。
ビューの追加手順
ビューを追加する際の手順は、ビューのインスタンスをモデルのリスナーに登録し(addCountListener())、ビュー自身がリスナーのメソッドを実装することで(countChanged())、モデルからのイベントを受け取ることが出来ます。
そういえば最近はIDEの方でバインド設定したり、ObservableList等があるのであまり意識することがなくなってきたなぁ。
ソースコード
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
class CountView extends JFrame implements CountListener{
private final JLabel binaryLabel = new JLabel("0");
private final JLabel decimalLabel = new JLabel("0");
private CountModel cModel = new CountModel(0);
public static void main(String[] args) {
new CountView();
}
public CountView() {
super("Counter");
Container c = getContentPane();
c.setLayout(new GridLayout(2, 2));
c.add(new JLabel(" 2進数:"));
c.add(binaryLabel);
c.add(new JLabel("10進数:"));
c.add(decimalLabel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cModel.addCountListener(this); // ModelにViewを登録
pack();
setVisible(true);
}
public void countChanged(CountChangeEvent e) {
if (e.getSource() == cModel) {
binaryLabel.setText(Integer.toString(cModel.getCount(), 2));
decimalLabel.setText(Integer.toString(cModel.getCount(), 10));
}
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
* カウントするModel
*/
public class CountModel {
private int count;
private final List<CountListener> listeners = new ArrayList<CountListener>();
private Timer t = new Timer("Count Timer", false);
CountModel(int i) {
count = i;
t.scheduleAtFixedRate(new CountTime(), 0, 1000);
}
// Viewを登録
public void addCountListener(CountListener listener) {
listeners.add(listener);
}
public int getCount() {
return count;
}
public void setCount(int i) {
count = i;
notifyToListeners();
}
// Viewへの通知
private void notifyToListeners() {
for (CountListener listener : listeners) {
listener.countChanged(new CountChangeEvent(this));
}
}
class CountTime extends TimerTask {
@Override
public void run() {
setCount(getCount() + 1);
}
}
}
/**
* View側で実装する(Model側から呼び出し)
*/
public interface CountListener {
public void countChanged(CountChangeEvent e);
}
/**
* 通知内容を表すイベント(Modelが生成しViewが受け取る)
*/
public class CountChangeEvent {
private final CountModel source;
public CountChangeEvent(CountModel count) {
this.source = count;
}
public CountModel getSource() {
return source;
}
}
- Comments: 0
- Trackbacks: 0
Java, デザインパターン: Simple Factory - インスタンスの生成方法を任せる
- 2009-03-15 (日)
- Java
以前デザインパターンの理解の確認がてら簡単なコードを書いていたので投稿してみます。リファクタリングの前後を省いているので初見ではパターンのメリットと適応基準が見えにくく、またサンプルのテーマ設定が良くなかったのでイマイチ説明し難い(反省)。今回のようなToyコードみたいなのはたまに書くのですが、Blog記事としてまとめるのが手間で大抵HDDの片隅に眠ってしまうのですが、今後はとりあえず上げていこうかと思います。説明などは隙をみて書き足していく感じでまず投稿、みたいな。ただ、そればっかりだと後で自分が読んだときに分からなくなるので、ほどほどにしよう。
クラス図
文字列の分割プログラム。
入力: クラス名+<区切り文字>+メソッド名の文字列
出力: その文字列の分割結果が格納されたインスタンス(Namerクラス)。
文字列の形式が"<クラス名>#<メソッド名>"か"<クラス名>.<メソッド名>"によって、すなわち区切り文字によって分割処理のメソッドの内容が変わるので、その違いを派生クラスでオーバーライドして定義したメソッドで吸収しようというもの。
これによる利点はMain側が分割対象の文字列の形式を意識しなくてよいこと。単にNameFactory#getNamer()に渡すだけ。文字列の形式が2つの内のどちらで、どのクラスに処理を任せるかの処理はgetNamer()が行う。これによって、仮に文字列の形式を増やす場合に修正するのはgetNamer()のifブロック、追加するのは新しい派生クラス。
ソースコード
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
NameFactory nameFactory = new NameFactory();
Namer namer;
namer = nameFactory.getNamer("String#indexOf()");
printName(namer);
namer = nameFactory.getNamer("ArrayList.get()");
printName(namer);
namer = nameFactory.getNamer("Thread#run()");
printName(namer);
}
private void printName(Namer namer) {
System.out.println("Class Name: " + namer.className);
System.out.println("Method Name: " + namer.methodName);
}
}
public class NameFactory {
// 区切り文字の種類によって、処理する派生クラスを決める
public Namer getNamer(String input) {
if (input.indexOf(".") > 0)
return new PeriodNamer(input);
else if (input.indexOf("#") > 0)
return new SharpNamer(input);
return null;
}
}
public class Namer {
protected String className;
protected String methodName;
public String getClassName() {
return className;
}
public String getMethodName() {
return methodName;
}
}
public class SharpNamer extends Namer {
// 「#」で名前を区切る
public SharpNamer(String str) {
int i = str.lastIndexOf("#");
if (i > 0) {
className = str.substring(0, i); // (beginIndex, endIndex)
methodName = str.substring(i + 1); // (beginIndex)
} else {
className = "";
methodName = str;
}
}
}
public class PeriodNamer extends Namer {
// 「.」で名前を区切る
public PeriodNamer(String str) {
int i = str.lastIndexOf(".");
if (i > 0) {
className = str.substring(0, i); // (beginIndex, endIndex)
methodName = str.substring(i + 1); // (beginIndex)
} else {
className = "";
methodName = str;
}
}
}
実行結果
Class Name: String Method Name: indexOf() Class Name: ArrayList Method Name: get() Class Name: Thread Method Name: run()
書いた後ふと考えたのですが、これ100形式だと100派生クラスで、if-elseブロックもえらいことになるなぁ。こういう時、現時点では思考停止してしまうので、今月の課題。
今後はなるべく制作中のアプリの中での問題かそれに関わるもの、絡められるものを取り上げていきたい。
- Comments: 0
- Trackbacks: 1
JavaのソースコードからUMLのクラス図を作成
- 2008-01-04 (金)
- 開発環境
統合開発環境のEclipseでJavaのオセロプログラム(講義の課題)を制作中に一度クラス図を作成しようと試みました。使用プラグインはAmaterasUMLでこちらのサイト(軽量なUMLプラグインAmaterasUML (1/4) - @IT)を参考にしながらインストールを進めました。
さて、数あるUMLデザイナの中でこのプラグインのアドバンテージの一つはJavaクラスの継承関係などを包含したクラス図をソースコードから生成できる点にあると私は考えます。
その作り方は、まず「ファイル」→「新規」→「その他」から「AmaterasUML」→「クラス図」と選択してクラス図ファイルを作成し、そのファイルをダブルクリックしクラス図エディタを起動します。その上にクラスファイルをドラックアンドドロップすれば、そのクラスのクラス図が作成されます。また継承関係などを表したい場合は、その関係のクラスを選択した上でドラッグ&ドロップすればOK。下図にその使用状況を示します。
ちなみに、これによって作成された図は画像形式でエクスポートできます。
人にプログラムの構造の説明する際に役立つので重宝しています。
- Comments: 0
- Trackbacks: 0
Home > Tags > UML





