@IT情報マネジメント会議室は、2009年4月15日に新システムに移行しました。
新たに書き込みを行う場合には、新しい会議室をご利用ください。
- PR -

単位の表現を変えるのはクラスかメソッドか

1
投稿者投稿内容
Tacchang
ベテラン
会議室デビュー日: 2004/09/05
投稿数: 55
お住まい・勤務地: 川崎市
投稿日時: 2005-11-13 10:02
みなさん,こんにちは
Tacchangです。

クラス設計について意見を聞かせてください.
早速ですが,角度クラスを例に考えます.

外部表現(クラス利用者に対する仕様という意味で使っています)において,単位や精度が違う値を扱わなければならない場合,その違いをクラスで表すのが妥当か,メソッドで表すのが妥当か,スマートな表現をするためにはどのようなクラス設計が良いと思われますか?

例えば,私がC++メソッドで外部表現するとこんな↓感じです.
class Angle {
public:
double getRadian() const; // ラジアン値を
double getDegree() const; // 度値を
long getDegree() const; // 丸めた整数値を
    :
private:
double radian_; // 内部表現はラジアン
};

例えば,クラスしたらこんな感じでしょうか・・・
class Angle {
public:
double getValue() const;
private:
double angle_;
};
class RadianToDegreeConverter {
public:
double getValue(double radian) const;
};

とりあえずメソッドで表してみた(良いと思った)のですが,しっくりこないのと,別のうまい方法,例えばクラスで設計する(変換クラス/コンバータ)など,もっとスマートなやり方があるのではないか,イディオム,パターンがあるのではないか,というモヤモヤがぬぐえず,ご相談させて頂きました.

「C++ Coding Standards」によると「何でも詰め込むのででなくコンパクトを好め」という趣旨のプラクティスがありました.おそらく他のOOP言語でも同じだと思います.今回の例を照らし合わせると,単位変換はそれだけで責務になるのか,それとも値の付属機能なのか,といったところで,変換クラスを外だしにしろ,ということになるのでしょうか.

場合によりけり(C++で今回のケースなら変換マクロで充分など)だと思うのですが,例えば上記のような単位変換の場合はどうなるのか,そのほかのケースで,クラス内に機能を含ませるか外出しにするかの分岐点は,などご意見伺わせてください.

よろしくお願いいたします.
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2005-11-13 12:09
Tacchangさん、こんにちは。
よくお邪魔させていただいています。
#わたしはTacchangさんのストーカーではないので警戒しないでくださいね(~_~;)
#どうも、「組み込み」とか「C++」とかの話になると、つい嬉しくて、顔を出してしまいます。

引用:

Tacchangさんの書き込み (2005-11-13 10:02) より:

クラス設計について意見を聞かせてください.
早速ですが,角度クラスを例に考えます.

外部表現(クラス利用者に対する仕様という意味で使っています)において,単位や精度が違う値を扱わなければならない場合,その違いをクラスで表すのが妥当か,メソッドで表すのが妥当か,スマートな表現をするためにはどのようなクラス設計が良いと思われますか?

例えば,私がC++メソッドで外部表現するとこんな↓感じです.
class Angle {
public:
double getRadian() const; // ラジアン値を
double getDegree() const; // 度値を
long getDegree() const; // 丸めた整数値を
    :
private:
double radian_; // 内部表現はラジアン
};



私ならば、多分コンバータクラスは作らないです…場合によりけりですけれども。
こんなコードになります↓

コード:
#define PI 3.14159265358979323846

class Angle { 
public: 

	Angle() { radian_ = 0.0; }
	Angle(double radian) { radian_ = radian; }

	Angle(const Angle& angle) { this->radian_ = angle.radian_; }
	Angle& operator=(const Angle& angle) { this->radian_ = angle.radian_; return*this; }

	double getRadian() const { return radian_; }
	double getDegree() const { return toDegree(radian_); }

	// 変換メソッド(クラスメソッド)
	static double toDegree(double radian) { return (180.0 * radian / PI); }
	static double toRadian(double degree) { return (degree * PI / 180.0); }

private: 
	double radian_; // 内部表現はラジアン 
}; 



私の場合、あまり最初からクラスの責務をどこまでにするかは明確には考えないです。
実装していくうちに、クラスが肥大化してきたら、リファクタリングするようにします。

引用:

「C++ Coding Standards」によると「何でも詰め込むのででなくコンパクトを好め」という趣旨のプラクティスがありました.おそらく他のOOP言語でも同じだと思います.今回の例を照らし合わせると,単位変換はそれだけで責務になるのか,それとも値の付属機能なのか,といったところで,変換クラスを外だしにしろ,ということになるのでしょうか.



この辺のことは、次の書籍などが参考になるのではないでしょうか。

アンチパターン―ソフトウェア危篤患者の救出
リファクタリング―プログラムの体質改善テクニック

クラスに何でもかんでも責務を負わせすぎると
『アンチパターン』のパターン ”肥満児”
『リファクタリング』の不吉な匂い ”巨大なクラス”
になる恐れがありますが、

逆に、あまりにもコンパクトに固執しすぎると
『アンチパターン』のパターン ”お邪魔妖怪”
『リファクタリング』の不吉な匂い ”怠け者クラス”や”疑わしき一般化”
になる恐れがあります。

#『アンチパターン』も『リファクタリング』の場合も、上記2冊にそれぞれ対処方法が載っています。

クラスが大きくなりすぎたり、極端に小さなクラスが異常増殖しないように
『オブジェクトの粒度』を適度な大きさに保つのがよいというのが、今の私の考え方です。
ya
大ベテラン
会議室デビュー日: 2002/05/03
投稿数: 212
投稿日時: 2005-11-13 12:29
この例に絞って話をしますが…。
個人的にはvalueに対しunitは付随情報と思うので、もし、「Angleというクラスを作るなら」、というかAngleというものを「型」として扱うならばValueとUnitをメンバに持った値にしてしまいます。

コード:


enum AngleUnit {
Degree,
Radian,
};

template<typename T>
struct Angle {
Angle(const Angle<T>& copy);
Angle(T value, AngleUnit unit);

T get_value();
AngleUnit get_unit();

friend Angle<T> operator +(Angle<T>& a, Angle<T>& b);
friend Angle<T> operator -(Angle<T>& a, Angle<T>& b);
friend Angle<T> operator *(Angle<T>& value, T& a);
friend Angle<T> operator *(T& a, Angle<T>& value);
friend Angle<T> operator /(Angle<T>& value, T& a);

Angle<T> convert(AngleUnit unit);
};



こんな感じに。

※C++にした

[ メッセージ編集済み 編集者: ya 編集日時 2005-11-13 12:36 ]
Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2005-11-13 20:52
んーと、結論からぶっちゃけてしまいますと、臨機応変に両方ありだと思います。

例として上げられた角度クラスでは、基本となる情報が一次元のスカラ一つです。
このようなケースでは、私の場合ですが、角度クラス自体に、度表現、ラジアン表現、必要であればグラジアン表現(一周を 400 とする方法)を得るメソッドを作り込んでおくと思います。

一方で、基本情報が木構造やグラフ構造であり、取得したい表現が CSV として平坦化するか、それとも構造をそのまま反映した XML テキストとするか、取得する情報も構造全体か、フィルタを通した一部だけで良いかなど広範囲にわたったり、また、新たな表現方法に対応する必要が考えられる場合には、内部構造を表すクラスとは別に必要とする表現形式に変換するクラスを作成します。

この場合、Gang of Four の Visitor パターンで構造探査を共通化したり、そういった細かい違いを利用する側では意識しなくて済むように Facade パターンで隠蔽したり、木構造表現ノードクラスが既存ライブラリで手を入れられない場合は Decorator パターンで探査処理だけ外付けできるようにしたりという方法をとることがしばしばあります。

もっと簡単に言ってしまうと、基本的には仕様拡張も考えて外付け、でもそこまでする必要がほとんど感じられない場合は作り込みというスタンスですね。

もっとも、後で見直して、適切でない場合にはリファクタリングする点は Tdnr_Sym さんが書かれた通りです。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2005-11-14 00:12
こんばんは。

引用:

Gioさんの書き込み (2005-11-13 20:52) より:

この場合、Gang of Four の Visitor パターンで構造探査を共通化したり、そういった細かい違いを利用する側では意識しなくて済むように Facade パターンで隠蔽したり、木構造表現ノードクラスが既存ライブラリで手を入れられない場合は Decorator パターンで探査処理だけ外付けできるようにしたりという方法をとることがしばしばあります。



ご存知のことかもしれませんが…
一応、念のため、Gioさんの書き込みに関する書籍を紹介しておきます。

オブジェクト指向における再利用のためのデザインパターン

まあ、わざわざ書籍を買うまでもなく
「デザインパターン」で検索すれば、いろんなサイトで調べられるでしょうけれども。
Tacchang
ベテラン
会議室デビュー日: 2004/09/05
投稿数: 55
お住まい・勤務地: 川崎市
投稿日時: 2005-11-18 10:28
Tdnr_Symさん、yaさん、Gioさん、コメントありがとうございます。

特にyaさんのとらえ方「valueに対しunitは付随情報と思う」はピンときました。
確かに、unitはvalueの付随情報だと思います。unitはvalueの観点であって、一般的な人間の概念上ではvalueに付属するものと考えられるということですね。

ただし、コーディング世界では単位を外だし(クラス分け)にすることがあるんですね。
現時点で、コーディング世界でunitを独立したクラスとして扱った方が良いケースは思い浮かびませんが、単位変換が複数のクラスで共有できるケースでしょうか・・・

自分なりに整理できました。
みなさん、ありがとうございました。
1

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