- PR -

データクラスの設計とダウンキャストの回避について

投稿者投稿内容
zrx62460
会議室デビュー日: 2007/09/17
投稿数: 5
投稿日時: 2007-09-19 23:15
れいさん、回答ありがとうございます。

引用:

「メンバがたくさんあるから基底クラスで定義できない」
というのは論理がよく理解できません。
派生内容によって違うので定義できないというのであればわかります。



おっしゃるとおりです。
失礼しました。

引用:

そして、メンバが一つも定義できないのであれば、
上で書いたようにSuperVOには意味がありません。
その為にobject.equalsがあります。



equalsには独自の実装をしたいので、サブクラスで
そのVO独自の比較をしています。
基底クラスにメンバは定義できないので、確かに
基底クラスとしての意味はないですね。
(インタフェースになりますか)

引用:

SuperVOにメンバがあるなら、この手のはよくやりますので、
慣れてる人ならいちいち質問するまでもないのではないかと私は思います。
(その認識が間違っている可能性も少なくないですが。)
SuperVOにメンバが無いなら、無駄です。
つまり、慣れていないと判断しましたが。



基底クラスに共通のメンバがあって…というのは確かにオブジェクト指向
として分かりやすいですよね。

今回の趣旨としては、データクラスを設計する場合、どうなるんだろう
と悩んでましたので、投稿させて頂きました。
データクラスとしては、やはり基底クラスにメンバを置く必要はないのかなと。
そもそも、基底クラスという表現が間違ってますね。
C++の場合は、純粋仮想関数のみクラスはインタフェースということでしょうか。

結果としては、ダウンキャストは避けることにしたました。
つまり、データモデル化はやめて、コレクションを直で使う形に
なりそうです。(←この部分はすでにあるので)
私個人としては、ダウンキャスト自体が悪いとは思っていませんが、
理解不足は否めないので、踏み切れないです。

ただ、データクラスのあるべき姿がいまいちよく分かっていないので、
まだまだ、いろいろ試してみようと思っています。
とりあえず、時間があるときにオープンソースのORマッピングの中身で
も除いて、何か進展があればという感じです。


zrx62460
会議室デビュー日: 2007/09/17
投稿数: 5
投稿日時: 2007-09-19 23:28
一郎さん、回答ありがとうございます。

引用:

「ClientからするとgetB01のようなアクセサを使いたい」のはなぜでしょうか。getB01で値を取得するのは、その値を使って「何か」をするためですよね。
「何か」をする、というメソッドを作ることはできませんか?(unibonさんの言うexecute)
そして、それをSuperVoのメンバにすることはできませんか?(純粋仮想関数でも構いません)



ソフトウェアレイヤーの位置づけ的に、「何か」をするというのはクライアントの自由にしたいと思っています。
つまり、データクラスは基盤として、いろいろなサブシステムに提供する予定です。

引用:

もしそれができない、つまりSubBVoとして独特の呼び出し(処理ではなく呼び出し)をする必要があり、holder->Search()で取得するインスタンスの型はSubBVoであるとClient側が知っている必要があるならzrx62460さんの提示されたソースでいいんじゃないでしょうか。つまりダウンキャストをするということです。



最近、書籍でダウンキャストは避けるべきというを見てから、
ちょっと踏み切れないでいます。Clientは必ず知っているので、
ダウンキャストしてもいいとは思うのですが。
ちなみに書籍は「EffectiveC++」。私には難しい部分もあり、
理解できるようになるまでは、とりあえず従っておくかみたいな…。

引用:

あるいはSuperVoのコレクションの他にSubBVoだけのコレクションを作るという手もありますね。
SubBVoが要求されている処理なんですから、わざわざSuperVoのコレクションから取って来る必要はないですよね。



HolderからSearchを実行する際、どうしても戻り値が共通の型である必要があります。
そもそもの構成が悪いのか…。あるべき姿は自分の中で確立したいので、
まだまだ調べようと思っています。


unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2007-09-20 08:44
引用:

れいさんの書き込み (2007-09-18 15:31) より:
引用:

unibonさんの書き込み (2007-09-17 22:23) より:
「設計が悪い」と思います。普通のアプリケーションだったら、自分が0から作るクラスで、ダウンキャストが必要になることはまずありません。



unibonさんはこう言っていますが、私は結構使います。
.Net1.1のころはジェネリックも無いので、コレクションではよく使いましたし。

キャスト可能か調べなきゃいけないので、もちろん好きではないですが、
必要とあれば躊躇なく使います。


これを読んで気づいたのですが、ダウンキャストは、使うときに、
(1) キャスト可能か調べる必要がある場合。
(2) キャスト可能か調べる必要がない場合。
の2つに大きく分けられると思います。(1) は、たとえば Java だったら instanceof で調べたり、あるいは、たとえば、変数に入っているクラスがどれであるかを示す目印を別途設けた int や enum の変数に格納しておいたりするやりかたで、変数にどんなクラスが入っているかをなんらかの方法で調べないと分からない(使えない)場合です。
Generics を使うことで避けられるのは (2) の場合であり、(2) は結局は実質的にはダウンキャストではないと思います。

引用:

zrx62460さんの書き込み (2007-09-18 02:31) より:
確かにどこまで、汎用的にするかは悩ましいところですね。
今回のデータクラスの場合、Javaで言うところ(?)のORマッピング
を意識しています。
実は、これらのデータクラスは外部リソースの情報を読込み
データモデル化し、アプリ側で必要な情報を取り出したい
というところから始まっています。
(具体的にはCSVファイル、後にDBとCSVの混在となる予定)


今回の場合は、(1)であり、本当のダウンキャストですよね。アプリケーションならば避けたいところですが、ミドルウェア的なものもので、それがメタデーターを扱うようなものならば、どこかで型を取り扱う必要があると思います。それならば、それを言語に組み込まれたキャストの機能でやりくりするのも良いのかな、と思います。

--
unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86}
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2007-09-20 12:51
引用:

zrx62460さんの書き込み (2007-09-19 23:15) より:
今回の趣旨としては、データクラスを設計する場合、どうなるんだろう
と悩んでましたので、投稿させて頂きました。



はい。私もデータクラスと認識した上で回答しました。
ただ、データクラスの定義を私は知りませんが。

引用:

データクラスとしては、やはり基底クラスにメンバを置く必要はないのかなと。



たとえデータクラスでも、集めるからには理由があるはずです。
その理由から導かれるメンバを基底クラスで定義するのは当然かと。
というか、共通メンバを抽出するからダウンキャストが避けられるのであって、
抽出しないのならObjectのままで集めて、
使うときにダウンキャストになってしまいます。
私には全然意味がわかりません。

引用:

そもそも、基底クラスという表現が間違ってますね。
C++の場合は、純粋仮想関数のみクラスはインタフェースということでしょうか。



言語によっていろいろあるので、継承元の一般的名称としては
基底クラスであってるかと。

引用:

ただ、データクラスのあるべき姿がいまいちよく分かっていないので、
まだまだ、いろいろ試してみようと思っています。



データクラスの定義自体私にはわかってませんが、
私は作ってみて後悔して学ぶタイプです。
過去の資産には無駄なクラスが山のようにあります。

引用:

unibonさんの書き込み (2007-09-20 08:44) より:
これを読んで気づいたのですが、ダウンキャストは、使うときに、
(1) キャスト可能か調べる必要がある場合。
(2) キャスト可能か調べる必要がない場合。
の2つに大きく分けられると思います。



その場合分けの意味は私にはよくわかりません。
また、Genericを使う場合は(2)が解決されるというわけでもないです。
型引数の派生クラスをコレクションに押し込んだ場合などでは、
キャスト可能か調べないといけない場合もあります。
キャスト可能であればxxxをする、見たいな処理もありますし。

引用:

ちなみに書籍は「EffectiveC++」。私には難しい部分もあり、



懐かしい名前です。
もう内容は覚えていません。
心を忘れてなければいいんですが。

「ダウンキャストは避けるべき」というのは、
Cなどから入った人が、ダウンキャストが多発するような
コーディングをすることがよくあるので、
それを避けるための指針であると思います。
ですので、ダウンキャスト多発は結果であって原因ではありません。
ダウンキャスト回避も、目的はダウンキャスト回避ではなく、いい設計です。

こういった非オブジェクト指向プログラマのための指針はたくさんあるので、
オブジェクト指向に自信があるなら、
そんな指針に囚われる必要はないと思います。

オブジェクト指向からプログラミングに入った世代の人などには、
間違った指針である場合もあると思いますし。

とりあえず、最初からいってるように、
基底クラスにメンバがないなら、意味がないです。
ダウンキャストが多発します。
ダウンキャストが多発するから悪いのではなく、
設計が悪いからダウンキャストが多発します。

基底クラスにメンバがあり、
大抵の処理はそのメンバを使えばいいようにできてるなら、
何も問題ないと私は思います。
ダウンキャストもたまにしか必要ありません。

それは別にO/Rマッピングなどとは関係なく、
普通の話だと思うんですが。
O/Rマッピング用のクラスなら、
基底クラスのメンバを実際に用いることは少なくなるかもしれませんが、
それでも用意すべきものはたくさんあると思います。

それを忘れてしまうと後で泣けますので、ご注意を。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2007-09-20 20:00
引用:

れいさんの書き込み (2007-09-20 12:51) より:
引用:

unibonさんの書き込み (2007-09-20 08:44) より:
これを読んで気づいたのですが、ダウンキャストは、使うときに、
(1) キャスト可能か調べる必要がある場合。
(2) キャスト可能か調べる必要がない場合。
の2つに大きく分けられると思います。



その場合分けの意味は私にはよくわかりません。
また、Genericを使う場合は(2)が解決されるというわけでもないです。
型引数の派生クラスをコレクションに押し込んだ場合などでは、
キャスト可能か調べないといけない場合もあります。
キャスト可能であればxxxをする、見たいな処理もありますし。


たとえば、Java の場合、Generics が使えるようになる前までは、
コード:
List list = new ArrayList(); // String 専用
list.add("a");
list.add("b");
String str = (String) list.get(0); // instanceof で調べなくても一律にダウンキャストできる


のようにダウンキャストしていました。
これは、タイピングが面倒だったり、ケアレスミスで String 以外のものを入れてしまったときのデバッグに骨が折れましたが、本質的にはダウンキャストではないと考えて使っていました。なぜならば、List/ArrayList を拡張して String を引数に持つコレクションクラスを作ればそれで済んだからです。わざわざそれをしなかったのは、ダウンキャストにまつわる問題が生じているわけではなく、単純なコーディングの問題でしかなかったからだと思います。これは結局、Generics が登場して解決しました。

一方、
コード:
List list = new ArrayList(); // なんでも入れる入れ物
list.add("a");
list.add(new Integer(123));
Object obj = list.get(0);
if (obj instanceof String) { // instanceof で調べないと怖くて使えない
    String str = (String) obj;
}


だと、List のような Collection に限った問題ではないはずです。

--
unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86}
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2007-09-22 00:07
引用:

unibonさんの書き込み (2007-09-20 20:00) より:
引用:

れいさんの書き込み (2007-09-20 12:51) より:
引用:

unibonさんの書き込み (2007-09-20 08:44) より:
これを読んで気づいたのですが、ダウンキャストは、使うときに、
(1) キャスト可能か調べる必要がある場合。
(2) キャスト可能か調べる必要がない場合。
の2つに大きく分けられると思います。



その場合分けの意味は私にはよくわかりません。
また、Genericを使う場合は(2)が解決されるというわけでもないです。
型引数の派生クラスをコレクションに押し込んだ場合などでは、
キャスト可能か調べないといけない場合もあります。
キャスト可能であればxxxをする、見たいな処理もありますし。


たとえば、Java の場合、Generics が使えるようになる前までは、
(略



理解できました。
理解力足りないもので…。

引用:

今回の場合は、(1)であり、本当のダウンキャストですよね。アプリケーションならば避けたいところですが、ミドルウェア的なものもので、それがメタデーターを扱うようなものならば、どこかで型を取り扱う必要があると思います。それならば、それを言語に組み込まれたキャストの機能でやりくりするのも良いのかな、と思います。



自分のコードをちょっと見てみましたが、
unibonさんのいうところの真のダウンキャストは結構ありました。
C#だとエラー処理のところでたくさん使ってました。

考えてみましたが、私の力+C#の機能ではダウンキャスト無しでは組めません。
やはり、綺麗に処理できるなら私は真のダウンキャストでもOK派ですね。

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