剛學寫程式時,每個人都會學到建構子,知道可以透過類別中的建構子來初始化類別的狀態。當一個類別需要依照不同情境來初始化類別時,就會需要多載許多不同的建構子,每個建構子分別帶入不同參數來初始化類別。
假設有一個我們用一個Point類別來表示二維空間中的某一個點
public class Point
{
public double X { get; }
public double Y { get; }
public Point(string coordinate)
{
var axis = coordinate.Split(',');
X = double.Parse(axis[0]);
Y = double.Parse(axis[1]);
}
}
和一個計算兩點距離的類別
public double Calculate2dDistance(string coordinate1, string coordinate2)
{
var point1 = new Point(coordinate1);
var point2 = new Point(coordinate2);
return Math.Sqrt(Math.Pow(Math.Abs(point1.X - point2.X), 2) +
Math.Pow(Math.Abs(point1.Y - point2.Y), 2));
}
一切看似都非常合理,直到需求發生了變化。
有一天世界忽然從二維空間變成三維空間了,我們將傳入三維坐標,計算三維空間的距離。
public double Calculate3dDistance(string coordinate1, string coordinate2)
{
...
}
我們想透過多載Point建構子讓他可以支援三維空間,卻發現新的建構子參數與二維座標的建構子長的一樣,造成編譯結果有錯誤。
public class Point
{
public double X { get; }
public double Y { get; }
public Point(string coordinate)
{
var axis = coordinate.Split(',');
X = double.Parse(axis[0]);
Y = double.Parse(axis[1]);
}
public Point(string coordinate)
{
//出現了問題
}
}
假設我們想辦法透過修改原本的建構子,讓他透過一個flag判斷是否輸入為三圍座標
public class Point
{
public double X { get; }
public double Y { get; }
public double Z { get; }
public Point(string coordinate, bool is3d = false)
{
var axis = coordinate.Split(',');
X = double.Parse(axis[0]);
Y = double.Parse(axis[1]);
if (is3d)
{
Z = double.Parse(axis[2]);
}
}
}
然後新增計算三維座標距離的方法
public double Calculate3dDistance(string coordinate1, string coordinate2)
{
var point1 = new Point(coordinate1, true);
var point2 = new Point(coordinate2, true);
return Math.Sqrt(Math.Pow(Math.Abs(point1.X - point2.X), 2) +
Math.Pow(Math.Abs(point1.Y - point2.Y), 2) +
Math.Pow(Math.Abs(point1.Z - point2.Z), 2));
}
可以發現雖然Point可以支援三維座標了,但是建構子參數也變得複雜了,如果不了解Point建構子實作去看,一眼看下去會不知道座標參數後面的flag表示什麼意思,造成閱讀代碼上的不便。
由此可以發現,根據不同使用情境,運用多載建構子初始化類別有時會造成一些問題
為了解決上面的問題,可以在製造一些Point工廠方法,就可以省去不必要的flag參數。
public class Point
{
public double X { get; private set; }
public double Y { get; private set; }
public double Z { get; private set; }
public static Point Create2d(string coordinate)
{
var axis = coordinate.Split(',');
return new Point()
{
X = double.Parse(axis[0]),
Y = double.Parse(axis[1]),
};
}
public static Point Create3d(string coordinate)
{
var axis = coordinate.Split(',');
return new Point()
{
X = double.Parse(axis[0]),
Y = double.Parse(axis[1]),
Z = double.Parse(axis[2]),
};
}
}
在使用端也能從方法名稱了解其使用情境。
public double Calculate2dDistance(string coordinate1, string coordinate2)
{
var point1 = Point.Create2d(coordinate1);
var point2 = Point.Create2d(coordinate2);
return Math.Sqrt(Math.Pow(Math.Abs(point1.X - point2.X), 2) +
Math.Pow(Math.Abs(point1.Y - point2.Y), 2));
}
public double Calculate3dDistance(string coordinate1, string coordinate2)
{
var point1 = Point.Create3d(coordinate1);
var point2 = Point.Create3d(coordinate2);
return Math.Sqrt(Math.Pow(Math.Abs(point1.X - point2.X), 2) +
Math.Pow(Math.Abs(point1.Y - point2.Y), 2) +
Math.Pow(Math.Abs(point1.Z - point2.Z), 2));
}
透過上面的例子可以知道,工廠方法的好處