トップ «前の日記(2003-06-10) 最新 次の日記(2003-06-12)» 編集

日々の破片

著作一覧

2003-06-11

_ 初期化をどこでするか?

1.言語仕様で決まっているんだから、余分な記述はゴミ。
class Bao {
  int bab; // 0だよ。
  /* 別に書くものもないからコンストラクタは無しだ
  public Bao() {
  }
  */
 ...
}
2.宣言個所での初期化が明確である。
class Bao {
  int bab = 0;
  public Bao() {
    // またはコンストラクタを省略する
  }
 ...
}
3.そのためのコンストラクタだろ。
class Bao {
  int bab;
  public Bao() {
    bab = 0;
  }
 ...
}
 実際問題、どれでも良いんだが、1.が好みだ。3.は馬鹿げている。冗長だ。
 で、それはそれとして、次のようなファクトリパターンを実装したクラスがあるとする。
public class Garde {
  protected abstract class Gardon {
    ...
  }
  protected abstract Gardon getGardon();
  public dodo() {
    Gardon g = getGardon();
    g.touch(this);
  }
}
 で、いろいろあって、Gardonをキャッシュしたくなったとする。
public class Garde {
  public abstract class Gardon {
    ...
  }
  protected abstract Gardon getGardon();
  private Gardon cachedGardon;
  public dodo() {
    if (cachedGardon == null) {
      cachedGardon = getGardon();
    }
    cachedGardon.touch(this);
  }
}
 でも、元々の仕様ではdodoが呼ばれるたびにインスタンスを生成していたわけだから、Gardonの実装には例えば
Gardondon extends Garde.Gardon {
  private boolean booboo;
  public void touch(Garde g) {
    if (g.isPure()) {
      booboo = true;
    }
    go();
  }
  private go() {
    if (booboo) {
      ...
    } else {
      ...
    }
  }
}
なんていう一発勝負みたいな実装がありうる。この場合、最初にピュアなGardeのインスタンスでtouchが呼ばれ、次に不純なGardeのインスタンスでtouchが呼ばれると、当然、おかしくなる。つまり、キャッシュしたインスタンスの再利用時に、再初期化が必要となる。
 そこで、
public class Garde {
  public abstract class Gardon {
    public void renew() {
    }
    ...
  }
  protected abstract Gardon getGardon();
  private Gardon cachedGardon;
  public dodo() {
    if (cachedGardon == null) {
      cachedGardon = getGardon();
    } else {
      cachedGardon.renew();
    }
    cachedGardon.touch(this);
  }
}
とする。ここで、Gardon派生クラスが100個あって、すべてが再初期化が必要だとわかっているのなら、Gardon#renewはabstract宣言しておいて、コンパイルエラーを潰すことで確認していくのだが、100個あって、ほとんどすべてが再初期化が不要だとわかっているなら、既定の何もしないメソッドを定義するほうが良いだろう(少なくてもユニットテストプログラムがあればね)。が、実はどっちでも、この場合は本質的ではない。

_ で、この場合、最初の1〜3のどの記述方法が、このような状況でもっとも柔軟に変更に耐え得るかというのが問題だ。

 答えは3.でした。
class Bao {
  int bab;
  public Bao() {
    bab = 0;
  }
  public void renew() {
    bab = 0; // コンストラクタからコピペ
  }
 ...
 なお、
class Bao {
  int bab;
  public Bao() {
    renew();
  }
  public void renew() {
    bab = 0; // コンストラクタからコピペ
  }
 ...
は、ちょっとヤバメ。もっともfinalクラスなら構わないけど。  たとえば、派生された場合に
class Baox extends Bao {
  int babx;
  public Baox() {
    renew();
  }
  public void renew() {
    super.renew();
    babx = 0;  
  }
 ...
と記述されたとすると、コンストラクタでrenewが2回呼び出されることになる。この場合は、どうでも良いが、コピペではなく、1箇所にしたいような処理だと、2重に呼ばれるとまずいことが多いからだ。  その場合はフラグ管理しかないかな?
class Bao {
  boolean clean;
  int bab;
  public Bao() {
    clean = false;
    renew();
  }
  public void renew() {
    if (clean) {
      return;
    }
    bab = 0;
    clean = true;
  }
 ...
でも、これだと、そもそもの問題となったキャッシュされた場合の2度目の呼び出しが区別できないという罠。  かと言って
    if (clean) {
      clean = false;
      return;
    }
ってのは、後々禍根を残すだろう。  というわけで、コピペしとくか、renewに対応してcleanupという一時終了メソッドを追加するか、どっちかを選択ということになる。

_ メソッド名が悪いんだよ

 でも、
public class Garde {
  public abstract class Gardon {
    public void setup() {
    }
    ...
  }
  protected abstract Gardon getGardon();
  private Gardon cachedGardon;
  public dodo() {
    if (cachedGardon == null) {
      cachedGardon = getGardon();
    }
    cachedGardon.setup();
    cachedGardon.touch(this);
  }
}
なら、なんの問題もなく
class Bao {
  int bab;
  public Bao() {
  }
  public void setup() {
    bab = 0; // コンストラクタからカットペ
  }
 ...
で済むなぁ。

2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|

ジェズイットを見習え