08/03/25 23:21:24
>>262
あとから足りないものに
気付いてメソッドを追加する、というごくありふれたケース
といいますが、これはOCPが違反しませんか?
今、Head Firstのオブジェクト指向設計とかいう本を読んでいます。
そこにはSRPの例として、車のクラスを定義する場合に
そこにwashなどのメソッドを組み込んではいけないという事になっていますが、
例えば外部で
CarWasher#wash(AutoMobile)を定義した場合、
このCarWasherクラスはAutoMobileの例えばdirtというフィールドが存在している事を知っていないければなりません。
(例えばwashというメソッドがdirtを0にするものだとすると)
これは情報の隠蔽に失敗していませんか?
それに無闇にAutoMobile#setDirtを設定してこれを受け入れれば、
他でどんな悪用をされるか分かりません。
失敗した設計だと思います。
カプセル化についてどう考えていますか?
setterはなるべく実装しない方が良いように思うので、
データについてクラスを分離するのがいいと思うのですが、
この本には振る舞いについて分離せよと書いてあります。
264:デフォルトの名無しさん
08/03/26 01:09:50
>>263
拡張と一口に言っても、類似概念の追加と、メソッドの追加では意味合いが
違います。OCP を守るためにメソッドの追加ではなく継承で対応するの
では本末転倒でしょう。原則は絶対ではないのだから、柔軟に対応すれば
いいと思いますよ。
その本は読んでいないので状況が良くわかりませんが、車クラスが wash
を提供するべきではない理由が書いてあるのではありませんか? 車の主要な
責務は「人を乗せて移動する」であるとかなんとか。
まあ、いずれにしてもいい例ではない気がしますが、クラスでは概念を
完全に表現することができない以上、どこかに軸足を置く必要がある
わけです。それが「洗う」なのか「走る」なのかは目的によって変わって
くるでしょう。
カプセル化は言うまでもなくとても重要ですね。
大切なのは、自分が表現したいものをはっきりさせることです。そうすれば
自然と必要なデータや振る舞いが備わっていきますよ。
265:デフォルトの名無しさん
08/05/14 12:54:34
>>263
CarWasherクラスがAutoMobileクラスのdirtフィールドを知ってなければならない
のは当たり前。
そもそも、洗車機は車の存在を前提に作られるし、皿洗浄器は皿の存在を前提
に作れる。何を?どうする?の2つを知ってないと「する側」は作れない。
てか、洗車は例えとして分かり難いぞw
266:デフォルトの名無しさん
08/05/14 15:08:00
そもそも「汚れ具合」が定義されていなければ「洗う」ことも「汚れる」ことも定義できない.
267:デフォルトの名無しさん
08/05/22 06:02:42
OOPに最近参入した新参者です。
設計(特にクラスの設計)に関するオススメの書籍何かないでしょうか?
例えばショッピングサイト、レンタルビデオショップなどわかりやすそうなものから考えていこうとしたものの
OOPへの理解が浅いせいかどうにも戸惑ってしまっています
よろしくお願いします
268:デフォルトの名無しさん
08/05/22 12:13:26
>>267
デザインパターンとともに学ぶオブジェクト指向のこころ
269:デフォルトの名無しさん
08/05/25 20:58:00
MVC分割したときにUndoのロジックってModelの実装領域になると思うんですが、
大抵このUndoってコマンドパターンとかで実装されますよね?
このとき、Modelに対する変更命令が全てコマンドで実行されることをコードレベルで保障するには、
Modelに対する変更命令を受け取ってコマンドを発行するクラスを作って、
更にModel内部のデータ構造に対するアクセスを制限するための
読み取り専用ラッパークラスを作って外に公開する、という感じになるのでしょうか?
実際このようなことって業務レベルの開発では行っていたりしますか?
270:デフォルトの名無しさん
08/05/26 00:23:07
>>267
個人的に、リファクタリングの実践が一番身に付きやすいと思う。
フリーソフトとかのソース落としてきてやりまくるといい。
ということで
・リファクタリング
271:デフォルトの名無しさん
08/06/19 22:55:43
実験で使用するシリアルポートから遠隔操作できる
温度調節機能付きの水質モニターを管理するプログラムを作りたいと思っています
1分毎に水質データと水温を取得する
PCから温度の管理値を変更できる
という機能を実現したいのですがこの場合
ポートの開閉やデータの送受信を管理するCommクラス
水質データの受信要求や管理値の変更命令をCommオブジェクトに送る、水質モニターの機能を実現するMonitorクラス
Monitorオブジェクトから値を受け取り実際に表示するGUIクラス
GUIがMonitorの参照を保持して
MonitorがCommの参照を保持する
このような構造でよいのでしょうか?
272:271
08/06/20 00:31:37
それとも
Monitorクラスの定期測定と管理値の変更は別のクラスの振る舞いにしたほうが良いのでしょうか
273:デフォルトの名無しさん
08/06/20 20:41:49
>>271
いいと思います。あとは、水質モニターのマルチベンダー化や多重化等が
確実なら、事前に拡張性を考慮するのもあり。
274:デフォルトの名無しさん
08/06/20 22:07:46
>>273
ありがとうございます
あと、新型のモニターを使用する場合も考えると
拡張機能を実装しやすいようにデコレータにしておいた方がいいですかね
275:デフォルトの名無しさん
08/06/21 00:04:07
>>274
数ヶ月以内に新型が導入されるならね。さもなくば、シンプルに徹する。
276:デフォルトの名無しさん
08/06/21 00:47:55
肝心なこと聞き忘れてました
二つの値を一つの文字列に合成してシリアル通信するのですが
この命令の合成と返答の翻訳はMonitorとCommクラスどちらに実装するべきでしょうか
質問続きで申し訳ありません
277:デフォルトの名無しさん
08/06/21 06:02:48
>>276
"二つの値Constructor"に任せればいいんじゃない?
もしくはMul/Demultiplexerとか?
278:デフォルトの名無しさん
08/06/21 10:29:05
>>276
Comm クラスは汎用的なシリアル通信だけを行うユーティリティクラスで
いいんじゃないかな。制御プロトコルの知識は Monitor に持たせる。
279:デフォルトの名無しさん
08/06/21 12:11:06
>>276
おれだったら、合成と返答の翻訳を行うクラスを別途作るかな。
280:デフォルトの名無しさん
08/08/05 22:06:37
>>279
>おれだったら、合成と返答の翻訳を行うクラスを別途作るかな。
メッセージI/FとパーサーI/F又はどれか一つ用意して
メッセージの詳細はベンダー毎に実装するのが一般的かと思う。
281:デフォルトの名無しさん
08/08/08 03:31:53
>>271
ちょっとOO分析っぽいことやってみたかった.
# [実験]で[使用する][シリアルポート]から[遠隔操作できる]
# [温度][調節][機能]付きの[水質モニター]を[管理する]
# [1分毎]に[水質データ]と[水温]を[取得する]
# [PC]から[温度]の[管理値]を[変更できる]
必要な名詞(オブジェクト)
シリアルポート,温度,水質モニター,1分毎,水質データ,水温,温度,管理値
足りない名詞
タイマー
必要な動詞(メソッド)
操作,調節,管理,取得,変更
ここまでやったけど別に何を作ろうというわけではない
282:デフォルトの名無しさん
08/08/10 12:13:24
温度がオブジェクトかよー
1分毎もかよー
水温・温度もかよー。
283:デフォルトの名無しさん
08/08/10 13:31:40
当たり前すぎますよねー^^
284:デフォルトの名無しさん
08/08/20 20:05:43
クラスの設計に関して悩み中です。
例えば以下のような必要とされる要素が有ったとします。
(要素内容はでたらめです。)
・コード/名称/メッセージ/結果/色/高さ/幅
/追加日/更新日/削除日/…(全部で20要素ぐらい)
処理1 … コード/名称/メッセージ/結果
処理2 … コード/結果/色/高さ/幅
処理3 … 結果/色/更新日
処理4 … 削除日
各処理は、クラスに個別分類できる処理になり、各処理に少しずつ上記要素が
絡んでくる状態になります。
このような場合、どのようなクラス設計が適していますか?
現在は、コード/名称~などの20要素ぐらいをBaseクラスにして、
処理1~4までを継承させています。
ただ、こうすると必要の無い要素まで入ってしまい、もっとすっきり
させたいなと思っています。
285:デフォルトの名無しさん
08/08/21 02:24:17
>>284
> ただ、こうすると必要の無い要素まで入ってしまい
この時点で継承を選ぶのがおかしい。意味のある単位に切り分けよう。
問題の切り分けじゃなくて、登場人物の切り分けを意識した方がいい。
例えば色/高さ/幅ってGUI上の属性情報なんじゃないの?
処理1と処理4でそれらの情報を使用しないってのなら、
ぱっと聞いただけでも処理1~4は継承関係上の兄弟とは思えない。
> このような場合、どのようなクラス設計が適していますか?
適切な切り分けの単位は要件仕様やその他の背景によって異なるよ。
とっかかりがないなら、それらのデータモデルを構造体化して、
処理の引数に渡してしまえばいい。
286:デフォルトの名無しさん
08/08/21 15:48:42
>>285
どうもです。
いえ、GUIの属性とかではありません。
サーバーへコマンドを投げると上記の値が返ってくるイメージです。
処理1なら
1.「コマンド処理1」をサーバへ送信
2.「コード/名称/メッセージ/結果」がサーバより返ってくる。
3.処理1の処理を行う。
処理2なら … と同じ様な処理が複数あります。
287:デフォルトの名無しさん
08/08/21 23:32:47
サーバーにコマンドを投げるとかいつ説明したよ。
それで相手に適切なクラスの分け方を聞いたわけ?
一応必要なアドバイスは>>285に入ってるから、熟読して悩め。
>>286の情報だけで何を悩めばいいかを挙げるなら以下くらいかな
・全ての処理で共通する送受信データの基本情報(必須情報)って何なの
・送受信データ全体を木構造に表すとどうなるの(構造体のメンバに構造体を持つかなど)
・送受信データに対して、それを継承するって適切なの
(データモデルとビジネスロジックは普通分けるがね)
恐らくだが、以下みたいな感じに落ち着くんじゃないか
・処理1,2,3,4ってのは、データモデルを引数に受ける関数ポインタ(Javaでいうリスナ)になる
・コマンド処理1,2,3,4と関数ポインタ(Javaならコマンド名のgetterとリスナをセットにしたクラス)と
送信データを引数とし、受信データを戻り値とする通信クライアントクラスが必要(C++なら受信データも引数で受ける)
・送受信データ中、基本情報以外の情報(構造体メンバの構造体)で使用しないものはNULLを入れる
基本情報以外が後からいろいろ増えるなら拡張情報をMapで持つ手もある。シリアライズ(デシリアライズ)がいるが。
最近の流儀だとXMLを使うのも悪い手ではない。(個人的にはJavaやC#ならこれにするな)
あなたのプロジェクトの答えを書いたつもりはないので、参考になるなら参考にして、後は悩め。
288:デフォルトの名無しさん
08/08/21 23:34:15
訂正
×受信データを戻り値とする
○受信データを関数ポインタの引数にしてその関数を実行する
289:デフォルトの名無しさん
08/08/31 16:08:07
ちょっとここの主題とずれるかもしれませんが、
ブラウザ - Webサーバー - APサーバー - DB
という一般的な構成でのエラーチェックで質問です。
入力データのチェックをするときに、未入力や不正文字はMVCのCで
チェックして、DBに問い合わせないとわからないチェックはMでいいですよね。
注文入力をするときに、数量の未入力は前者、在庫チェックは後者です。
でですね、「数量の上限」や「不可能な注文の組み合わせ」みたいに
「ビジネスロジックだけどDBに問い合わせる必要はない」というチェックは
APになげると余計な通信が発生するのでWebサーバーでやろうと思ってます。
Webサーバー側のpackageには原則Actionクラスしかないのですが、
このpackage配下にチェッカークラスを置くのに違和感を感じます。
注文形態が複雑でActionがいっぱいあるので、注文BaseActionを
作ってTemplateMethodパターンでフローを決めてるのですが、
だらだらとバリデートを書くのもフローがわかりにくくなって嫌です。
注文クラスそのものに書くべき?
290:デフォルトの名無しさん
08/08/31 16:34:20
TemplateMethod 使ってるなら、BaseAction に空の Validate メソッド
用意してフローに組み込み、具体的なチェック内容は派生クラスで実装
すればいいんでないの?
なぜ「だらだら」になるかが知りたいところ。
291:デフォルトの名無しさん
08/08/31 16:49:46
そこまではやってるんだけど、さらにその中で「注文条件がこれだったら
これは不可で」「数量をparseして文字列だったらこのエラーメッセージで」
みたいな処理が10以上あって、ロジックは共通なので、その個別のValidateを
BaseActionに書いてたんです。個別Validateのどれを呼ぶかは
Actionによって異なります。で、このチェッカーって切り出すべきだと
思うんだけど、actionパッケージの下にチェッカークラス置くのって
変だよなーと思って相談したわけです。
292:デフォルトの名無しさん
08/08/31 16:50:33
だらだらなのはBaseActionに個別のValidate処理がいっぱい並んでたから。
わかりにくかったですね。すいません。
293:デフォルトの名無しさん
08/08/31 21:00:05
なんとなく状況は把握できた。俺なら BaseAction には単一の Validate
メソッドだけ用意し、チェックメソッドは別クラスにまとめる。たぶん、
このチェッカークラスは stateless になるんじゃないかな。
派生クラスではこのチェッカーを使って個々の Validate メソッドを実装
すれば良い。チェッカークラスの置き場所はまあ、プロジェクト的な決め事
でしょう。
294:デフォルトの名無しさん
08/09/01 00:26:21
そうなんだよね。
今は各Actionクラスの処理の切り出しのイメージだったから
個別ValidatorでsetFieldError()してるんだけど、チェック処理の
多さからいって全public staticなクラスに切り出すべきだと思ってる。
すでにリリースされてるプロジェクトの機能追加なのであまり
リファクタリングしたくないんだけど。
で、最初の質問に戻るんだってば。actionパッケージの下には
actionクラスしかなく、serviceパッケージの下にはリモート呼び出しを
前提としたクラスとそのドメインオブジェクトしかない。
今後のプロジェクトではserviceクラスのメソッドにアノテーションつけて
ローカル実行とリモート呼び出しを分けられるようにしようかと思っていた。
そうするとやっぱ今回もあるべき論的にはserviceパッケージの下に
ローカル実行用のサービスクラスをつくるのかな。でも単純な未入力チェック
みたいのはコントローラーでやるべきだと思うし、うーん。
295:デフォルトの名無しさん
08/09/01 00:42:31
やっぱり、どの個別Validatorを呼び出すかみたいなロジックが
Actionに入ってることがそもそもおかしい気がしてきた。
あ、いやでも注文形態によって入力パラメータの数が違うから
未入力チェックを行うならActionか。
もしかしてstrutsみたいに各フィールドのセッターでvalidationしよう
っていうのが間違っているのか?
Action -> 個別注文クラス生成 -> 個別注文クラス#Validate()呼び出し
→ OKならリモート注文ServiceのValidate()呼び出し
こうするとすごくスッキリする。略してスッキる。ActionはModelの
生成だけでロジックにはノータッチになるし。チェッカークラスが
複雑で外だしにするとしても、個別注文クラスと同じpackageに
いれればしっくりくる。略してしっくる。ドメインオブジェクトがアクセッサしか
持っていないようなドメインモデル貧血症なつくりにはしてないからね。
296:デフォルトの名無しさん
08/11/13 18:02:24
あげ
297:デフォルトの名無しさん
08/11/27 14:40:57
パブリックヘッダファイルとプライベートヘッダファイルの違いが分かりません、
パブリックヘッダファイルで提供する関数と内部で使う関数の分け方すらわかりません。
298:デフォルトの名無しさん
08/11/28 00:16:46
OOP以前の問題だな
299:デフォルトの名無しさん
08/11/28 00:39:31
パブリック複合
300:デフォルトの名無しさん
08/12/06 00:00:34
テンプレート・メソッド パターンの多階層継承はマジ勘弁。
追いづらい。
IService ← テンプレート・メソッド パターン
↑
AbstractLogic ← テンプレート・メソッド パターン
↑
BaseCollectLogic ← テンプレート・メソッド パターン
↑
FileBaseCollectLogic ← テンプレート・メソッド パターン
↑
DomainLogic
カスが
301:デフォルトの名無しさん
08/12/06 01:58:35
それはやりすぎというより、なんか設計がおかしい気がする。
302:デフォルトの名無しさん
08/12/06 06:03:41
エントリーポイントを増やすのが目的なんだろうけど、
うざったいってのはよく分かる。
IService ← テンプレート・メソッド パターン
↑
AbstractLogic ← テンプレート・メソッド パターン
↑
DomainLogic
としてBaseCollectとやらはコンポジションでもっとけと。
303:デフォルトの名無しさん
08/12/06 09:36:21
問題ない。次
304:デフォルトの名無しさん
08/12/06 19:23:37
IService はインターフェースで Run メソッドが定義されてる。
クライアントは、
DomainLogic dl = new DomainLogic();
dl.Run();
あれ?スーパークラス使わないの?
IService はどうした?
IService は?
カスが
305:デフォルトの名無しさん
08/12/06 19:26:08
>>304 は >>300 の続きね
306:デフォルトの名無しさん
08/12/06 19:33:02
で、お前ならどうしたいのよ
307:デフォルトの名無しさん
08/12/06 19:36:49
黙れカスが
308:デフォルトの名無しさん
08/12/06 19:48:28
で、お前ならどうしたいのよ