こんばんは。オブジェクト指向大好きな凪瀬です。
オブジェクト指向では「同じものを継承しているなら、一緒くたに扱う」という抽象化を行います。
個別ケースで対応するよりも、抽象化して同じものとして扱えるなら
同じものとして扱う方が余計な頭を使わずに済みます。
一方、ジェネリクスでは「型は違っても同じロジックを行っているなら、一緒くたに扱う」
という抽象化を行います。
このようないろんな抽象化で、同時に考えることを少なくしていくことで、
昨今の巨大なシステムが作られています。
戻り値を返さないイテレータ
繰返しを扱う概念として「イテレータ」というのがあります。GoFデザインパターンのIteratorパターンが有名ですね。
Javaでは
java.lang.Iterableを
継承することで、for-each構文でループさせることができます。
C#では
System.Collections.Generic.IEnumerator
を継承することでforeachでループさせることができるようです。
いずれも、繰り返しの際に返す型をジェネリクスで指定できますね。
今ここに、内部ステータスの変更などのみを行う、繰り返し可能なイテレータを作ると仮定しましょう。
Javaでは
ジェネリクスでVoidを扱う
の稿で述べたとおり、voidのラッパークラスであるjava.lang.Void型を用いてVoidを返すイテレータを表現できます。
public class Hoge implements Iterable<Void>, Iterator<Void>{
/** @see Iterable#iterator() */
public Iterator<Void> iterator() {
return this;
}
/** 繰り返し処理でさらに要素がある場合に true を返す */
public boolean hasNext() {
return true;
}
/** 繰り返し処理で次の要素を返す */
public Void next() {
return null;
}
/** 要素を削除 (任意のオペレーション) */
public void remove() {
throw new UnsupportedOperationException();
}
}
戻り値はVoidですが、Iterableなのでfor-each構文で処理することができます。
Hoge hoge = new Hoge();
for (Void v : hoge) {
// ...
}
Iterableなので繰り返しとして使うことができます。
これは「同じものを継承しているなら、一緒くたに扱う」ことができるオブジェクト指向の原則ですね。
型を継承するということは、その型としての機能を全うするべきです。
全うできないなら継承をしてはいけません。
このあたりはオブジェクト指向の設計手法でよく語られるis-a関係とhas-a関係ですね。
ゲームのロジックの実装にコルーチンを使うなら
あたりの話題なのですが、ゲームのロジックにコルーチンを用いようという試みです。
C#でIEnumeratorを実装してyield returnを用いることで実装されます。
議論のテーマはこのyield returnの戻り型は何であるべきか?というところです。
Javaにはコルーチンの機能がないので、かつのりさんが以前作っていたスレッドを用いた
コルーチンもどき
で表現してみましょう。
ContinuationIterator<Integer> iterator = new ContinuationIterator<Integer>(){
protected void process(){
for(int i = 0; i < 10; i++){
this.yieldReturn(i);
}
}
};
for(int i: iterator){
System.out.println(i);
}
通常はこのように、返す型が決まっており、for-eachでループして利用します。
返すべきデータがないのであれば、ジェネリクスの型パラメータにVoidを用いて、
ContinuationIterator<Void> iterator = new ContinuationIterator<Void>(){
protected void process(){
for(int i = 0; i < 10; i++){
this.yieldReturn(null);
}
}
};
for(Void v: iterator){
System.out.println("hoge");
}
となりますね。
さて、C#ではjava.lang.Voidのようなものが無いようです(私はC#については素人なので情報あれば指摘願います)。
となると、IEnumerator での戻り型のジェネリクス型パラメータに困ってしまいます。
コルーチンのyieldはDoEventsです
で例に挙げられたのはこの戻り型のないイテレータの話でした。
コルーチンと運命の出会い
では、コルーチンの内部で外部側のメンバ変数を変更するという作りになっていました。
これは、戻り型がないイテレータとは別の話題と考えるべきでしょう。
さて、ここからが本題、といきたいところですが、話が長くなってきたのでまた次回。
投稿日時 : 2008年3月11日 21:33