web-dev-qa-db-ja.com

暗黙的に別のクラスに変換することのみを目的とするクラスを作成するのは悪いことですか?

Circleオブジェクトを作成できるライブラリを使用していて、円の半径と中心を指定してそれを定義できる状況を想像してみてください。ただし、何らかの理由で、必要なflavourパラメータも必要です。ここで、自分のアプリで実際にCircleを使用する必要があるとしましょう。ただし、アプリの目的で、毎回フレーバーを_Flavours.Cardboard_に設定できます。

これを「解決」するために、別の名前空間に独自のCircleクラスを作成します。これは、radiusおよびcenterのみをパラメーターとして受け取りますが、外部ライブラリの暗黙的なコンバーターを持っていますCircleクラスは、Circle(this.radius, this.center, Flavours.Cardboard)オブジェクトを作成するだけです。したがって、他のタイプのCircleが必要なすべての場所で、自動変換を実行します。

そのようなクラスを作成するとどうなりますか?より良い解決策はありますか?私のアプリケーションが、この外部ライブラリーの上に構築されたAPIであり、他のプログラマーが使用することを意図していた場合、何か違いはありますか?

10
user3002473

alwaysは悪くありませんが、暗黙の変換が最良のオプションであることは非常にまれです。

問題があります。

  1. 暗黙的な変換はあまり読みやすくありません。
  2. 新しいオブジェクトを作成しているため、元のオブジェクトに加えられた変更は、変換先のオブジェクトでは認識されず、バグが発生します。
  3. オブジェクトを捨てる場合、それはクリーンアップするための余分なゴミです。
  4. 問題がある場合は、モノが作成されたときではなく、変換が発生したときに発生するため、バグの追跡が難しくなります。

一般的に、より良い解決策があります。

  1. 内部で他のクラス/フレームワークを使用する独自のシンラッパーを作成します。
  2. 必要なコンストラクター引数を取り、修正された引数を提供するヘルパーメソッドを作成し、不要な引数を指定せずに実際のオブジェクトを返します。
  3. 問題のあるクラスから継承し、独自のより優れたコンストラクタを提供します。
  4. 追加の引数を渡すことは実際にはそれほど大きな問題ではないことを理解してください。

個人的に、私は#2が最も簡単に実装でき、設計の負担が最も少ないと感じています。他の人は、状況や、これらのクラスで他に何をしようとしているのかを考えれば、大丈夫です。

暗黙の変換は最後の手段であり、私が作成しようとしているC++スタイルのファンクターがある場合にのみ、価値があるように思えます-暗黙的にデリゲート型に変換する戦略オブジェクト。

13
Telastyn

説明しているシナリオを考えると、これは部分的な関数の適用という観点から考えることができます。

コンストラクターは関数です(少なくとも理論的には、C#では、コンストラクターを呼び出す「ファクトリー関数」を作成できます)。

Func<double, Point, Flavour, Circle> MakeCircle = (r, p, f) => new Circle(r, p, f);

部分的な適用については、以下で十分です:

public static Func<T1, T2, R> Partial<T1, T2, T3, R>(this Func<T1, T2, T3, R> func, T3 t3)
   => (t1, t2) => func(t1, t2, t3);

これで、2つのパラメーターのみを必要とするコンストラクターを取得できます。

Func<double, Point, Circle> MakeCardboardCircle = Circle.Partial(Flavours.Cardboard)

これで、目的のパラメーターを持つファクトリー関数ができました。

Circle c = MakeCardboardCircle(1.0, new Point(0, 0)))

ところで、これは、より機能的な観点から見ると、上記のオプション2と明らかに同等です。

1
la-yumba