ときどき、オブジェクトを異なるデータセットで初期化するために、いくつかのコンストラクターを提供しなければならないという問題に直面します。これらのセットは、数学的に相互に変換できます。次に例を示します。
public Element(int x, int y, int width, int height)
{
setValues(width, height, x, y);
}
public Element(int RectTop, int RectLeft, int RectRight, int RectBottom)
{
setValues(RectRight - RectLeft, Math.Abs(RectBottom - RectTop), RectLeft, -RectTop);
}
このような望ましいオーバーロードでは、同じシグネチャのため実際にはありませんが、間違ったコンストラクタが呼び出される可能性があります。 Rect-Valuesを指定すると、最初のコンストラクターが呼び出されます。異なるタイプがあると仮定すると、これは悪い習慣であることを十分に理解した上でタイプを交換することによってそれを達成できます。しかし、4倍のint型を使用したこの簡単な例では、そうではありません。
オーバーロードが便利であるが、同じシグネチャを使用して、望ましい動作を実現するためのいくつかのエレガントな代替案を考えることができますか?
public Element(Point topLeft, Size size)
public Element(Point topLeft, Point bottomRight)
ファクトリメソッドを使用することもできます
public static Element FromLeftTopWidthSize(int left, int top, int width, int height)
public static Element FromLeftTopRightBottom(int left, int top, int right, int bottom)
または、流暢なインターフェースを使用できます
Element.Top(10).Left(28).Right(112).Bottom(101);
Element.Top(10).Left(28).Width(102).Height(73);
簡単にインターフェイスを使用して、上に左、左に右または幅、右に下、幅に高さを強制できます。 (下記参照)
または、すべての中で最も賢明なことを行い、異なる選択を許可しないこともできます。ただ1つを選択し、それに固執します。
Progressive Fluentインターフェイスの例:
public class ElementBuilder : ITopSyntax, ILeftSyntax, IRightSyntax, IWidthSyntax
{
private int _top;
private int _left;
private int _right;
public ElementBuilder(int top)
{
_top = top;
}
ILeftSyntax ITopSyntax.Left(int left)
{
_left = left;
return this;
}
IRightSyntax ILeftSyntax.Right(int right)
{
_right = right;
return this;
}
IWidthSyntax ILeftSyntax.Width(int width)
{
_right = width + _left;
return this;
}
Element IRightSyntax.Bottom(int bottom)
{
return new Element(_left, _top, _right, bottom);
}
Element IWidthSyntax.Height(int height)
{
return new Element(_left, _top, _right, _top + height);
}
}
internal interface ILeftSyntax
{
IRightSyntax Right(int right);
IWidthSyntax Width(int width);
}
internal interface IRightSyntax
{
Element Bottom(int bottom);
}
internal interface IWidthSyntax
{
Element Height(int height);
}
internal interface ITopSyntax
{
ILeftSyntax Left(int left);
}
これを行う1つの方法は、Rectangle
オブジェクトを受け入れるコンストラクターを記述することです。
public Element(Rectangle r) { ...
次に、このように呼び出すことができます。
var element = new Element(new Rectangle(x, y, width, height));
もちろん、最初のオーバーロードと同じパラメータをRectangleが要求するので、今は元の場所に戻ります。
数学のわずかな変化に対してコンストラクタオーバーロードを提供することに専念している場合は、ブールフラグを追加して署名を一意にすることができます。何かのようなもの:
public Element(int RectTop, int RectLeft, int RectRight, int RectBottom, bool isRect)
これはちょっとしたハックであり、bool
パラメータに何を渡してもかまいませんが、特定のオーバーロードが呼び出されることを保証します。
受け入れられた回答(pdr)が説明するように、探しているエンティティはすでにフレームワークに存在するため、2つの座標をPointクラスに、または2つの次元をSizeクラスに簡単に置き換えることができます。また、ファクトリアプローチも優れたソリューションですが、残念ながら継承は不可能または困難になります。
より適切なクラスが見つからず、プリミティブ型に固執する必要がある場合でも、プリミティブ型をラッパークラスにラップできます。
public class Car
{
//public Car(string make) {...}
//public Car(string model) {...}
public Car(Make make) {...}
public Car(Model model) {...}
}
public class Make
{
private string Name;
public Make(string name) { this.Name = name; }
public override string ToString()
{
return this.Name;
}
}
public class Model
{
private string Name;
public Model(string name) { this.Name = name; }
public override string ToString()
{
return this.Name;
}
}