【Java】interface を使って簡単なコールバック機能を作る 
2013/12/05 Thu [edit]
最近はワンバイナリじゃないけど、なるべく再利用可能なコードを書くようにしている。実際、Applet から Android の移植したときに、同じ言語のせいもあって、ほとんどのロジック系コードはコピペで済むのだけど、グラフィック系コードは全て書き直しを余儀なくされることがわかった。でも逆に言うとロジック系とグラフィック系を分けておけば、半分は再利用できるということだね。そんなときによく使う手として、簡単なコールバック機能を作る方法。
例えばこんなクラスがあるとする。簡単に言えば MainClass のデータ(maindata) は常に SubClass のデータ(subdata)の2倍を保持するものとする。update(n) メソッドで SubClass の値と MainClass の値を両方更新している。まず SubClass から更新して、その値を取り出して2倍して MainClass に代入してるだけだ。ただのサンプルコードなので特に意味はない。コードの良し悪しは気にしないで頂きたい(笑)。
//メインのクラス
public class MainClass {
//プロパティなど
private SubClass subClass; //関連クラス操作用
private int maindata; //メインクラスのデータ
//コンストラクタ
public MainClass() {
subClass = new SubClass();
}
//データをセットする
public void setData(int data) {
maindata = data;
}
//データを取得する
public int getData() {
return maindata;
}
//データを更新する
public void update(int data) {
subClass.setData(data); //関連クラスにデータをセットする
setData(subClass.getData() * 2); //関連クラスからデータを取得し、2倍してメインクラスのデータにセットする
}
//メイン(ここから開始)
public static void main(String[] args) {
MainClass mainClass = new MainClass();
//関連クラスにデータをセット→関連クラスからデータを取得して2倍し自身(メインクラス)に記録、の手順となる
mainClass.update(4);
System.out.println("maindata = " + mainClass.getData());
System.out.println("subdata = " + mainClass.subClass.getData());
}
}
//関連クラス
public class SubClass {
//プロパティなど
private int subdata;
//コンストラクタ
public SubClass() {
//特になし
}
//データをセットする
public void setData(int data) {
subdata = data;
}
//データを取得する
public int getData() {
return subdata;
}
}
実行するともちろん、maindata = 8, subdata = 4 となる。update(n) の値を入れ替えれば値も変わる。2つのクラスのデータが同時に更新されている感じ。別にこれはこれで問題はない。しかしこれをわざと簡単なコールバック機能を作って実現する。
//メインクラスに関連クラスのコールバックインターフェイスを導入
public class MainClass implements SubClass.Callback {
//プロパティなど
private SubClass subClass;
private int maindata;
//コンストラクタ
public MainClass() {
subClass = new SubClass(this);
}
//データをセットする
public void setData(int data) {
maindata = data;
}
//データを取得する
public int getData() {
return maindata;
}
//データを更新する
public void update(int data) {
subClass.setData(data);
}
//関連クラスからのコールバックメソッド
@Override
public void dataChanged(int data) {
setData(data * 2);
}
//メイン(ここから開始)
public static void main(String[] args) {
MainClass mainClass = new MainClass();
//関連クラスにデータをセット→関連クラスからコールバックメソッドを実行→
//→コールバック先(メインクラス)で受け取ったデータを2倍して自身に記録、の手順となる
mainClass.update(4);
System.out.println("maindata = " + mainClass.getData());
System.out.println("subdata = " + mainClass.subClass.getData());
}
}
//関連クラス
public class SubClass {
//コールバック用のインターフェイスを定義
interface Callback {
public void dataChanged(int data);
}
//プロパティなど
private Callback callback; //コールバックするオブジェクトアクセス用
private int subdata;
//コンストラクタにコールバックインターフェイスを受け取るようにしたもの
public SubClass(Callback callback) {
this.callback = callback; //コールバックするオブジェクトを記録
}
//データをセットする
public void setData(int data) {
subdata = data;
if (callback != null) { //コールバック先が記録されていたなら(null 以外なら)、メソッド実行
callback.dataChanged(subdata);
}
}
//データを取得する
public int getData() {
return subdata;
}
}
結果は全く同じになる。まず、SubClass にコールバック用 interface Callback を定義し、メソッド dataChanged() を定義してる。それを MainClass で implements することによって、SubClass で値が変更されたとき、MainClass で @Override された dataChanged() を実行されるように変更しただけだ。SubClass では呼び出し元がわかるように、new SubClass(this) によってコンストラクタで callback に記録している。callback が null でないかを調べているが、このサンプルでは必須ではない。そんな感じの構造。
どうだろうか?この2つのコードは同じ結果を生むが、後例の方が幾分すっきりしてる?特に update() メソッドは簡単になってるね。その代わり dataChanged() で戻ってきて値を更新してる。どちらの例でも良いと思う。でも再利用を考えると後例の方がずっと使いやすい。というのは前例では update() を使わないと正しく両クラスのデータが更新されないが、後例では他のメソッドで SubClass の値が更新されても MainClass の値が正しく更新されるからだ。多くの機能を後から付け加えた場合、この差は顕著となる。
こんな感じでロジック系クラスが更新されたとき、コールバックでグラフィック系クラスも更新されるようにしておけば、意外とコードも簡潔になる。また同じコールバックを定義さえして置けば、別のグラフィック系クラスにすげ替えたときもそのままロジックが使えるという利点もある。各々のクラスが機能分離できて、再利用しやすいわけだ。
「~Listener」(リスナー)も基本的には同じ仕組み。「ボタンなどのリスナーを登録して使う」というのは「ボタン押下を検知した→登録したオブジェクトにあるコールバックメソッドを実行」しているだけだ。リアルタイムなコールバック(リスナー)を作りたかったら、Thread などで検知し、コールバックメソッドを実行すれば同じことができる。
(関連記事)
【Android】【Applet】【Java】interface と abstract の使い方・違いとは~簡単なフレームワークを作って、考えてみる
【Android】【Applet】【Java】interface を使って独自のラッパークラスを作る
【Android】【Applet】【Java】interface を使ってインスタンスを生成する
- 関連記事
トラックバック
トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/91-373831ad
この記事にトラックバックする(FC2ブログユーザー)
| h o m e |