- PR -

プログラムの書き方でご指導お願いします。

投稿者投稿内容
がるがる
ぬし
会議室デビュー日: 2002/04/12
投稿数: 873
投稿日時: 2005-11-09 15:30
がるです。
……えっと。ちと引用が前後いたしますが。

引用:

これは、もっとも汚い実装のひとつではないでしょうか。認証の例に当てはめた場合、正しいユーザー名・パスワードを入力した場合にも is_true() が例外を投げずに false を返す可能性があるということですね。


引用:

え? 私にはとても信じられない実装なのですが・・・。例外を握りつぶすと
  cobj.is_true() == cobj.is_false()
これが成りなってしまう可能性があるということですか? 明らかに内部状態の不整合のアラワレだと思います。無理に例外を投げずに false にしてしまう、なんてことをするから、こういった論理破綻した等式が成り立ってしまうのでは?


is系が例外を返すかどうかについては微妙なのですが。
# 提供相手によって結構悩むポイントの一つです。

個人的には、is系の前に「処理メソッドが例外を投げている」ので
このタイミングでさらに投げるのは冗長だと思ってます。
ただ、is系で例外を投げるメリットがないわけではないので、
そのあたりは「状況次第」だと考えてます。
ちなみにおっしゃっている
「正しいユーザー名・パスワードを入力した場合」
のケースでは、問題があれば処理メソッド(今回でいうとcheckメソッド)
がすでに例外を投げているので、別に「無理に例外を投げない」
わけではないのですが。
そのあたりについてはどのように考えられてますでしょうか?

ついでに。
例外が発生したような状態で内部状態の整合をとっても
仕方がないと思うのですがどうでしょうか?
# というか、例外が発生したメソッドを保有する
# インスタンスの情報なんて基本的に全部信用してない
# のですが :-P

ちなみに私の「例外が発生したような状態」とは、
プログラムが以降継続不可能な状態でのエラーを
さします。
このあたり、未記入さんが以前に書かれている「正常系
フロー制御に例外を使うべきではない」そのまんまの
スタンスだと思うのですが、一応念のため。
# ちなみに上述の理由から「正常系の制御に使うな」という
# 教訓が導き出せる、ともいえます。

次に。
引用:

コード:
if (cobj.is_true()) {
 // 承認OK
} else
 if (cobj.is_wait()) {
  // ちょっとまて
 } else {
  // 承認NG
 }
}




括弧の対応が、私の記述したものと異なるのですが。
# というか上述のソースは{}の対応がおかしいです。

もう一度「よく」ソースをご覧ください。
私は
コード:
  // 判断
  if (cobj.is_true()) {
    // 承認OK
  } else
  if (cobj.is_wait()) {
    // ちょっとまて
  } else {
    // 承認NG
  }


と記述しております。ちと書き方を変えると
コード:
  // 判断
  if (cobj.is_true()) {
    // 承認OK
  } else if (cobj.is_wait()) {
    // ちょっとまて
  } else {
    // 承認NG
  }


となります。
引用:

あなたのいう第3の値が、第1、第2 の値と同等のものではなく、いずれかの子(詳細ステータス)ということであれば


上述のソースを見てなお「「ちょっとまて」とは「承認されてない」
に分類されてい」ると、或いは「第三の値は詳細ステータスで、
第一や第二のステータスと等価なものではない」と思われますか?

どんなもんでしょ?
未記入
ぬし
会議室デビュー日: 2004/09/17
投稿数: 667
投稿日時: 2005-11-09 15:59
引用:
問題があれば処理メソッド(今回でいうとcheckメソッド)がすでに例外を投げているので、別に「無理に例外を投げない」わけではないのですが。そのあたりについてはどのように考えられてますでしょうか?


考えたことないですね。is_true() は check() の呼び出し後のみ有意な値を返す、という制限事項が付くくらいなら、私は check() がその値を返すように実装してしまいますから。で、あなたの実装だと、is_true() の前に正しく check() が実行されなければならないわけですが、もし check() が正しく実行されていなかったら、どうするのかなと思ったのです。

引用:
例外が発生したような状態で内部状態の整合をとっても仕方がないと思うのですがどうでしょうか?


いや、私の意見とは異なります。

引用:
ちなみに私の「例外が発生したような状態」とは、プログラムが以降継続不可能な状態でのエラーをさします。このあたり、未記入さんが以前に書かれている「正常系
フロー制御に例外を使うべきではない」そのまんまのスタンスだと思うのですが、一応念のため。


いやいやいや。ちょ、ちょっと待ってくださいよ。例外とはプログラムが以降継続不可能な状態でのエラーをさす、なんて言ったことも考えたこともありません。それは、.NET 文化の人達の意見だったはずです。私は、そのような意見に対して、Java ではそのような目的に Error がある、Exception は復帰可能な異常系状態遷移に積極的に使う価値がある、と述べています。

引用:
次に。括弧の対応が、私の記述したものと異なるのですが。


すみません。勝手に脳内補完しただけなく改竄までしてしまいました。

引用:
もう一度「よく」ソースをご覧ください。


大変申し訳ありませんでした。よく読みなおしてみました。
・・・けれど、結果は変りません。

is_true() が false だから is_wait() を判定しているという事実は変わりませんね。まさか、is_true() は false を返すけど、is_false() も false を返すので、承認されているとも承認されていないとも言えない、とか言い出さないですよね?

is_true() が正常系で false を返している以上、上位分類では「承認されていない」のでしょう。それあとで、その詳細ステータスを確認しているだけ、という認識は今も変りありません。

それとも「ちょっとまて」の導入時に is_true() の仕様が変更されたのでしょうか?

仕様変更前
 is_true()
  true: 承認されている
  false: 承認されていない

仕様変更後
 is_true()
  true: 承認されている
  false: さらに is_wait() で状態を確認する必要がある

まさかね。is_true() が false を返したら「承認されていない」でいいんですよね?

引用:
上述のソースを見てなお「「ちょっとまて」とは「承認されてない」に分類されていると、或いは「第三の値は詳細ステータスで、第一や第二のステータスと等価なものではない」と思われますか?


はい。is_wait() がどのような値を返そうとも is_true() == false の状態「承認されていない」の詳細ステータスだと思います。それから不明な点がひとつあります。「ちょっとまて」の状態のときに is_false() は true を返すのですか? false を返すのですか? 教えてください。
tarnwo
ベテラン
会議室デビュー日: 2002/10/25
投稿数: 58
投稿日時: 2005-11-09 16:13
お初にお目にかかります。

引用:

がるがるさんの書き込み (2005-11-09 15:30) より:
コード:
  // 判断
  if (cobj.is_true()) {
    // 承認OK
  } else if (cobj.is_wait()) {
    // ちょっとまて
  } else {
    // 承認NG
  }





私もこのコーディングには違和感があります。

仮に100個ステータスがあるなら100個isXXXメソッドがあるのでしょうか。
if文でいちいち呼び出したら、当該ステータスに行き着くまで
何度もオブジェクトにアクセスしますよね。
check()がvoid型で変更できないなら、check()の結果をcobj.getResultCode()などで取得し
swicthで判定する等のほうが後々も便利でしょう。
小僧
大ベテラン
会議室デビュー日: 2005/06/24
投稿数: 122
投稿日時: 2005-11-09 16:36
返信ありがとうございます。

> じゃんぬねっとさん
> この時点で、前提条件がフェアじゃないので比較対象から外れませんか?
比較対象の前提条件、というのは「クラスが使える言語」と「クラスが使えない言語」という事ですよね?
( いや、 VB でもクラスは使えますけど、直感的に。。。 )

> では、逆に「C#, Java とかだと (クラス使っていないと)」の状態で実装してみてください。
同じように、戻り値が増えたら全部見直し ( VB とか C と同じ ) しか思いつかないのですが。。。

> がるがるさん
> かくして、私が知っている狭い範囲では
> いう風になるのではないかと思います。
私もそう思っていました。
やはりそうなるんですよねぇ。
と、いう事で出来るだけ変更が起こらないような設計を最初からしていこうと
思います。 ( これが難しいのだが。。。 )

お忙しい中の返信、ありがとうございました。
がるがる
ぬし
会議室デビュー日: 2002/04/12
投稿数: 873
投稿日時: 2005-11-09 16:40
どもです。がるです。

引用:

tarnwoさんの書き込み (2005-11-09 16:13) より:
仮に100個ステータスがあるなら100個isXXXメソッドがあるのでしょうか。


これについては「ケースbyケースだけど悩ましいところ」ですね。
おっしゃるとおり
引用:

check()がvoid型で変更できないなら、check()の結果をcobj.getResultCode()などで取得し
swicthで判定する等のほうが後々も便利でしょう。


という手段は非常によいと思います。
実際、ステータスの数によっては悩みます。

ただ、悩むというからには上述でも気になる点がありまして。
それは「ステータスの表記をどのようにしてその定数をどこに持つか」
っていう話になるです。

ただ、過去の記憶を穿り返すと、大抵の場合「沢山のステータスコード」
は大抵「一桁程度の大分類」に取り合えず切り分けられていたので、
取り合えずisメソッドで実装していましたが。

実際に100くらいのステータスが「完全に並列に」きたら…
悩むなぁ。状況にも拠るけど、やっぱりtarnwoさんがおっしゃる
とおり、cobj.getResultCode() からswitchの二段コンボのほうを
チョイスするような気がします。

ただ、isメソッド100個実装だと「定数の書き間違い」とかの
ミスをコンパイルレベルとかで見つけられるのがまぁメリットと
言えばメリットなんですよね。
悩ましい問題です(笑

で。
引用:

未記入さんの書き込み (2005-11-09 15:59) より:
引用:
問題があれば処理メソッド(今回でいうとcheckメソッド)がすでに例外を投げているので、別に「無理に例外を投げない」わけではないのですが。そのあたりについてはどのように考えられてますでしょうか?


考えたことないですね。is_true() は check() の呼び出し後のみ有意な値を返す、という制限事項が付くくらいなら、私は check() がその値を返すように実装してしまいますから。で、あなたの実装だと、is_true() の前に正しく check() が実行されなければならないわけですが、もし check() が正しく実行されていなかったら、どうするのかなと思ったのです。


「もし check() が正しく実行されていなかったら」を考慮するので
あればもちろんis系で例外を実装する意味合いは十分にあります。
そのあたりが前述で「is系が例外を返すかどうかについては
微妙なのですが」と言っていた理由です。

引用:

引用:
ちなみに私の「例外が発生したような状態」とは、プログラムが以降継続不可能な状態でのエラーをさします。このあたり、未記入さんが以前に書かれている「正常系
フロー制御に例外を使うべきではない」そのまんまのスタンスだと思うのですが、一応念のため。


いやいやいや。ちょ、ちょっと待ってくださいよ。例外とはプログラムが以降継続不可能な状態でのエラーをさす、なんて言ったことも考えたこともありません。それは、.NET 文化の人達の意見だったはずです。私は、そのような意見に対して、Java ではそのような目的に Error がある、Exception は復帰可能な異常系状態遷移に積極的に使う価値がある、と述べています。


ををこれは失礼を。…そういう意味だと、私のスタンスは.NET寄り、に
なるのでしょうか?
# そのあたり、純粋によくわからんのですが。

結論を出すべきネタではないと思うのですが、議論をするには
面白そうなネタですね。
…って、たしか以前にそーゆースレがあったような。

引用:

is_true() が false だから is_wait() を判定しているという事実は変わりませんね。まさか、is_true() は false を返すけど、is_false() も false を返すので、承認されているとも承認されていないとも言えない、とか言い出さないですよね?


え?
「等価な」三値なのですから、waitがtrueであればほかの二つは当然falseに
なるかと思うのですが。
というか、そういう前提で話をしていたつもりなのですが…なにか行き違い
ましたでしょうか?

引用:

is_true() が正常系で false を返している以上、上位分類では「承認されていない」のでしょう。それあとで、その詳細ステータスを確認しているだけ、という認識は今も変りありません。


失礼。もしかすると、例題のソースコードのメソッド名から
誤解を生んでいたのかもしれません。
では、以下のように書き換えてみます。

コード:
// 処理そのものがおかしくなったら例外を返す
cobj.check();

// ステータスの判定
if (cobj.is_hoge()) {
    // HOGE処理
  } else if (cobj.is_foo()) {
    // foo処理
  } else if (cobj.is_bar()) {
    // bar処理
  } else {
    // ありえないしここに入るのは
  }


これでどうでしょうか?
# なんとなく、当初の「承認」という単語とis_trueというメソッド名が
# 誤解を生んでいるように感じられたので。

ちなみに一応。
承認という単語からは大抵
・承認 or 非承認
という二つの状態のみを連想されることが多いかと思われますが、
実際の業務で
・承認 or 一時承認 or 非承認
・承認 or 臨時承認 or 非承認
など、奇妙なステータスはあちこちで見ております(苦笑
なので、このたびの会話では承認という単語に対して
「承認 or 非承認 の二値のみ」という概念はとっぱずして
会話を進めているつもりなのですが。

多分、承認とかその辺の単語に惑わされずに見ていただけると
また見えるものなどあるのではないかと。
tarnwo
ベテラン
会議室デビュー日: 2002/10/25
投稿数: 58
投稿日時: 2005-11-09 17:19
tarnwoです。

引用:

ただ、悩むというからには上述でも気になる点がありまして。
それは「ステータスの表記をどのようにしてその定数をどこに持つか」
っていう話になるです。



私は定義用のインタフェースを用意しています。
定義方法については、大項目、中項目、小項目、(+詳細項目)であったり
全て1定義1ステータスコードのように定義したり。
どちらも一長一短ですね。

引用:

ただ、過去の記憶を穿り返すと、大抵の場合「沢山のステータスコード」
は大抵「一桁程度の大分類」に取り合えず切り分けられていたので、
取り合えずisメソッドで実装していましたが。



異常系のステータスの場合等ログにメッセージを吐き出しますが、
プログラム上のステータスコードとログメッセージ用の
プロパティファイルのキーを統一しておけば、
管理も楽だと思います。
(項目ごとの足し算だと追いかけるのは大変ですが)

引用:

ただ、isメソッド100個実装だと「定数の書き間違い」とかの
ミスをコンパイルレベルとかで見つけられるのがまぁメリットと
言えばメリットなんですよね。
悩ましい問題です(笑



すいません、イマイチ意味が・・・。
「定数の書き間違い」(手打ち?)のエラーはコンパイルレベルで普通に見つかると思いますが・・・(^^;

未記入
ぬし
会議室デビュー日: 2004/09/17
投稿数: 667
投稿日時: 2005-11-09 17:26
引用:
「等価な」三値なのですから、waitがtrueであればほかの二つは当然falseに
なるかと思うのですが。


そうですか。is_wait() が true を返すとき、is_true(), is_false() のいずれも false を返しますか。・・・ひどい設計ですね。これは、つまり私が危惧していた通りで「ちょっとまった」導入時に is_true() と is_false() の仕様が変更されたということになりますね。

仕様変更後
 is_true()
  true: 承認されている
  false: 判定不能。さらに is_wait() で状態を確認する必要がある
 is_false()
  true: 承認されていない
  false: 判定不能。さらに is_wait() で状態を確認する必要ががる

ということになりますね。is_true() のインターフェイスを変更すると影響範囲が広くなるので、is_wait() を建て増ししたと言っていませんでしたか? is_wait() を建て増しした挙句、is_true() が false を返したときの信頼性まで奪ってしまう、というのは最悪の拡張方法だと思いますけど。

コード:
if(is_true() == false) {
 「承認されていません」メッセージを出す。
}


このようなコードは、すべて見直しが発生しますね。false が不定値をあらわすという仕様に変更されてしまったわけですから。結局、is_true()、is_false() を使用している個所すべての見直しが必要になるのなら is_true() の戻り値 boolean を int に変更してしまったほうが、まだマシだと思います。そうすれば警告を出してくれる開発環境も多いので、見逃す可能性を低くできます。

引用:
「承認 or 非承認 の二値のみ」という概念はとっぱずして会話を進めているつもりなのですが。


ですから「承認」「非承認」の二値表現から脱していないと申し上げているのです。「一時承認」「臨時承認」いずれも「承認」の詳細ではないですか? 同等な第3の値だとは到底思えません。

成否・有無などの二値表現が拡張される可能性について例示していただいたとは考えていませんが、500万歩譲って boolean を 三値拡張することになったとしましょう。もしそうなったのであれば、is_wait() などの建て増しをせずに is_true() 改め is_hoge() が int や列挙型を返すように設計しなおすべきです。

あなたの考える is_wait() 建て増しは害悪しかないように思えます。

また、成否・有無ではなく初期設計時に二値だが状態の増加が想定される項目であれば (Java の場合ですが) Enum 列挙型の使用をオススメします。

コード:
enum TARGET {
    MAN,
    WOMAN
}

TARGET t;
switch(t) {
case MAN: .... break;
case WOMAN: .... break;
}


ここで、列挙型に CHILD を追加すると「switch 構文で CHILD への分岐がないよ」と警告してくれるので、作業漏れを防止できます。
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2005-11-09 17:27
引用:

小僧さんの書き込み (2005-11-09 16:36) より:

比較対象の前提条件、というのは「クラスが使える言語」と「クラスが使えない言語」という事ですよね?
( いや、 VB でもクラスは使えますけど、直感的に。。。 )


あらら、私が質問内容を勘違いしていたみたいです、ごめんなさい。(*_ _)

引用:

同じように、戻り値が増えたら全部見直し ( VB とか C と同じ ) しか思いつかないのですが。。。


戻り値ということなのですね。
C#、VB、いずれにしても、列挙体を使いますね。

メンバが増えるのは確かですから、増えた分の実装は当然しなくてはいけません。
全部見直すというよりは追加実装になるということですね。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌

スキルアップ/キャリアアップ(JOB@IT)