- - PR -
C# 計算フローを抽象化したい
1
投稿者 | 投稿内容 | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-08-01 16:07
例題のようなケースの場合、計算フローと型に依存した処理を分離する方法はありますか?例えばインターフェースあるいはジェネリクスを使うことで解決できますか?
例題 配列の平均値を求めるプログラムがありますが、この記述方法では対象とする配列の型が増える度に計算フローを重複して記述しなければならず、メンテナンス上好ましくありません。つまり、平均値を求める場合、@初期化A積算ループB平均化の3ステップのフローを要しますが、string型、int型、float型に対応する各Averageメソッドに@ABが重複して記述されています。@ABの計算フローを抽象化し、型に依存する部分と分離することはできるでしょうか? 一つのアイデアとして、object型で受けるAverageメソッドを1つ作成し、リフレクション(GetType())を使って1@ABの中で型ごとに分岐する方法があります。こうすれば@の処理、Aの処理、Bの処理が1カ所に集められ、若干見通しが改善されますが、@ABの3カ所に分岐命令が挿入されるため、根本的な解決ではありません。 @ABの計算フローと型に依存する処理とを完全に分離する記述方法を探しています。 public abstract class Test { // 配列要素の平均値を求める public static string Average(string[] a) { int num = a.Length; // Step1 初期化 string sum = ""; // Step2 積算 for (int i = 0; i < num; i++) { sum += String.Format("+{0}", a[i]); } // Step3 除算 return String.Format("({0})/{1}", sum, num); } public static int Average(int[] a) { // Step1 初期化 int num = a.Length; int sum = 0; // Step2 積算 for (int i = 0; i < num; i++) { sum += a[i]; } // Step3 除算 return sum / num; } public static float Average(float[] a) { // Step1 初期化 int num = a.Length; float sum = 0; // Step2 積算 for (int i = 0; i < num; i++) { sum += a[i]; } // Step3 除算 return sum / num; } } | ||||||||
|
投稿日時: 2007-08-01 16:17
すみません、サンプルプログラムの一部に誤りが見つかりましたので訂正します。
string型の積算ループ部分 誤 sum += String.Format("+{0}", a[i]); // 左端に余分な+がついてしまう 正 sum += (i == 0 ? "" : "+") + a[i]; | ||||||||
|
投稿日時: 2007-08-01 22:02
どこが型に依存する部分かというのが問題になりますね。 「複数の項目を全て足し合わせて、それを項目数で割る」 という処理が平均を求める処理ならば、「足す」と「割る」という処理がひろしさんの決める型独自の処理になると思います。 例えばstringであれば足すというのは文字列を連結し間に'+'を入れることですし、intの割るは数値を項目数で除算したのち小数点以下を切り捨てる処理ということになります。 intは切捨てが当り前ではありません。VBでは整数変数に小数を入れると偶数丸めになります。 ひろしさんは、intの時には切り捨てをするという特別な選択を(暗黙的にですが)しているわけです。 ですので足すと割る以外の部分は分離することで「重複して記述」せずに済みます。 色々試してみたところ、結局こういう形になりました。
対象の型ごとにクラスが分かれているのは試行錯誤の名残ですので特に意味はありません。メソッドは全てstaticですから、ひとつのクラスに全てまとめても同じです。 で、ひろしさんが抽出したかったのはAverageCalcのAverageメソッドなんですね。 アーティストとしてはこのようにソースで物事を表現するのは楽しいものですが、もし仕事でこのようなことをしたいならおとなしく「重複して記述」しておいた方がいいですよ。 一年後に「平均」という言葉の意味が変わってしまうなら話は別ですが。 [ メッセージ編集済み 編集者: 一郎 編集日時 2007-08-01 22:05 ] | ||||||||
|
投稿日時: 2007-08-01 23:52
ご回答ありがとうございます。
私が希望していた回答です。 私自身は、ジェネリクのインターフェース制約あるいは派生制約あたりを使って解決できないか考えてみましたが、うまくいきませんでした。(残念なことに、演算子を制約に含めることはできないのですね) | ||||||||
|
投稿日時: 2007-08-02 00:02
あっと、すいません。
AddDelegate<T>とDivDelegate<T>の定義を書き忘れていました。 public delegate T AddDelegate<T>(T item1, T item2); public delegate T DivDelegate<T>(T item, int count); って感じです。 |
1