過去の日記

2008-05-24 [長年日記]

運動会 [徒然]

子供の運動会。

しおんの王 8 読了 そして完結 [comic]

webで書影の笑顔を見て、きっと最終巻なんだな、と。その通り。

しおんの王(8) <完> (アフタヌーンKC)

  • 作者: 安藤 慈朗
  • 出版社/メーカー: 講談社
  • 発売: 2008-05-23
  • ASIN: 4063145050
  • メディア: コミック
  • amazon.co.jp詳細へ


「自分の両親が殺されたのは自分が将棋を指していたから」→「自分がいなければ──あるいは将棋を指していなければ両親は殺されなかったかもしれない」となったら嫌だなぁ、と思っていたところをうまく回避。
その辺の緩急が醍醐味だった。
と書いておこう。

なるほど Twitter でやると言ってしまったことはやることになるものだと思った日 [java]

Twitter で「じゃ、あとでやっとく」と書くのはつまり、Twitter の文字数制限の範囲で書ける話題じゃないか、文字数制限内でまとめるのに時間がないとかなんだけど、特に後者は時間的制約と文字数制約のせめぎ合いの中での自分の腕、能力の問題と捉えるべきでそれについては、

文を書くのに時間を長く使えば、無駄が無くなり要点が伝わる

を参照されたい。


さて本題。

分けすぎたクラスの整理とかtrimとか。 - 人工無脳が作りたい

の件。


まず話の枕として Main クラス。

package munoutter;


import twitter4j.Twitter;

ここ。
munoutter package が「人工無脳エンジンむのうにのフロントエンド」になるか「人工無脳システムむのうにの plugable な package」になるかでも話は変わってくるけど──判断するには時期尚早だとももちろん思うけど──package munoutter は TwiteerJ から切り離されていた方が都合がいい。
package munoutter を使ってなにかやろうとすると、一緒に twitter4J.twitter をimport しないといけないというのは何となく妙だ。
package munoutter の存在意義を考えるに、twitter4J の諸機能や、Twitter の API を知らなくても munoutter があれば人工無脳エンジンとの連結は楽ですよ、ということになると思う。
そうであれば、package munoutter を import して使う方は twitter4J を一緒に import したくないなぁ、と。

実利的には、ある時点で 、twitter4J を切り捨てて別実装に置き換えるとか、自分で実装してしまうとかいう時に有利に働くし、そういうシチュエーションを頭に置いて書いてみるといいと思う。

今は twitter4J に探りを入れている段階なのでそこまで考える必要はないけれど、何を隠蔽し何を見せるか? は(教科書的)オブジェクト指向プログラミングの要なので意識はしておいた方がいいかと。


で、本当の本題。Certificate クラス。
とりあえず引用。

package munoutter;

import twitter4j.Twitter;

/**
 *
 * @author ryo
 */

public class Certificate {

  String id, pass;
  Twitter twitter;
  private int retrycount;
  
  public Certificate(){
      retrycount = 3;
  }
  
  public void setId(String id) {
    this.id = id;
  }

  public void setPass(String pass) {
    this.pass = pass;
  }

  public Twitter getTwitter() {
    return twitter;
  }

  public boolean certification() throws CertificateException{
    boolean result;
    twitter = new Twitter(this.id, this.pass);
    result = twitter.verifyCredentials();
    if (--retrycount == 0 && result == false) {
            CertificateException ex = new CertificateException();
            throw ex;
    }
    return result;
  }

}

発端はこの2つ。

http://twitter.com/happy_ryo/statuses/818901420
http://twitter.com/quintia/statuses/818903447

みんなこの部分が気になったらしい。
で、

http://twitter.com/quintia/statuses/818904762

こうなりました、と。


1.メソッド名が名詞な点
ささいだけと気にする人は気にしそうだ。


2.id と pass が set されていない状態で呼ぶとどうなるんだという点
new Twitter(this.id, this.pass); が通るかわからないけど*1、verifyCredentials() は間違いなく失敗して 例外で処理される。
でも、例外というのは「プログラマが想定していない事態で起きる」か「プログラマが制御できない事態で起こす」べきものだと思う(後者の例はまさしく Twitter 側の不調とかそんなの)。
ここではプログラマが楽をしないで自分でチェックするべき。


3.リトライの役を担うのは Certificate クラスでいいんじゃないかという点
具体的に書くと、retrycount を certification() の中の変数にして、certification() がリトライを担ってもいいんじゃないか? ということ。

もうちょっというと、「retrycount がインスタンス生成時点でセットされて、certification() に失敗する度に減っていく」という動作は説明しにくいんじゃないか。
certification() を呼んだ時、成功すると Twitter のインスタンスを、失敗すると null を返します。3回失敗すると Exception を発生します。
という動作が分かりやすいか? というあたり。
(まだ続きあります)


4.それにもめげずに呼び出すと、もう Exception は発生しない件
Exception を throw した時点で retrycount が0になっているので、また呼び出すともう Exception は発生しない。
Java で MIN_INT_VALUE な変数に -- 演算した時どういう動作をするか忘れちゃったけど、それで MAX_INT_VALUE になったと仮定して、また0になるまではこの if 文の条件が TRUE になることがない。
これはプログラマのミス。

あくまでこの動作を貫くなら、Exception を throw した時に「retrycount をどんな値に設定するべきか?」という問題を考え、答えを出す責任がプログラマに発生する。

実際、3 に設定しなおすか、それとも「一度 Exception が発生したんだから、ネットワークや Twitter 側に問題があるんだろうから、リトライは控えるべき。なら 1 でいいや」という選択肢もある。
あるいは、「ユーザID、パスワードの間違いの方が圧倒的に多いだろう」と思うなら 3 でもいいわけだ。
(まだ続く)


5.そもそも retrycount をインスタンスが持っているのって……?
という点が再浮上する。
なぜかというと、「ユーザID、パスワードの間違いの方が圧倒的に多いだろう」という前提に立てば、setId( ) や setPass( ) でも retrycount を初期値に戻すのが正着ということになる。
そうなると結局、retrycount を certification() の中の変数にしてもさほど動作が変わらない、ということになってしまう。
だからたぶん、はてスタを付けた人は、

  public boolean certification() throws CertificateException{
    retrycount = 3;
    boolean result;
    twitter = new Twitter(this.id, this.pass);
    result = twitter.verifyCredentials();
    if (--retrycount == 0 && result == false) {
            throw new CertificateException();
    }
    return result;
  }

でも十分なんじゃ? と思ったのではないかと。


6.Certificate と 内部インスタンス twitter のライフサイクルが違うんじゃないか? という点
何気なく、getTwitter() が インスタンス変数の twitter を返しているけど、Twitter のインスタンスの生存期間というか有効期間というか、そういうものをチェックしないで返していいのか?
かといって毎回そういうチェックを入れるのもおかしい。

getTwitter() が無条件で返していいのは、Certificate の方が圧倒的に生存期間が短いだろうという仮定があるからだろう。
これは Certificate だけの問題では無くなったのでここまで。


twitter4j のドキュメントや「むのうに」と munoutter の全体像を見ないで、Certificate のソースだけを見て"ヤバイ"と感じるのはこのぐらいまでかなぁ、と。

この作りなら、「id と pass はコンストラクタかcertification メソッドの引数で渡して、certification() メソッドは成功したらインスタンスを返し、失敗したら例外を起こす」が一番シンプルそうだけど、それは「munoutter の全体像を見ないで」という"前提"から外れるのでおいておく。

Until Death do us Part 7 [comic]

絵がこなれてきて読みやすい。ストーリー展開はどこへ征くのか読めない。だから面白い。

死がふたりを分かつまで 7 (ヤングガンガンコミックス)

  • 作者: たかしげ 宙
  • 出版社/メーカー: スクウェア・エニックス
  • 発売: 2008-05-24
  • ASIN: 4757522800
  • メディア: コミック
  • amazon.co.jp詳細へ

*1 おっと、この this. は不要だぞ。