[英]Designing an abstract base class. What types to use, abstract or concrete?
我只是根據叔叔的建議從Java跳進C#。 Java幾何lib看起來比C#的Drawing lib更完整,所以我正在進行一些簡單的移植,只需要一些額外的功能即可開始使用C#。
但是,我遇到了一個設計問題,無法辨別哪個是更好的選擇。 要在抽象基類中使用具體數據類型的多個方法,或者使用較少的方法將抽象基數作為其參數?
public abstract bool isOverlapping(GeometricObject2D o) {}
要么
public abstract bool isOverlapping(Rectangle2D rect) {}
public abstract bool isOverlapping(Circle2D circ) {}
etc...
我腦子里的論點告訴我具體的論據可以防止邏輯錯誤,但是,如果使用的話,我會被教導總是使用抽象數據類型。
如果要將操作放在基類中,請使用抽象類型。 每次決定添加新子類時,您都不希望必須修改基類。
另一種方法是使用訪問者模式之類的東西,並將每個具體的類調度依次發送給訪問者。 然后,交集訪問者將包含有關如何計算每對對象類型的交集的所有知識。
以下是訪問者模式可用於此的方式。 (我將使用Java語法,因為我不是C#程序員)。 首先,在這里使用訪問者模式比通常情況更復雜,因為您必須根據兩個參數的類型修改操作。 實際上,您需要三次調度。 像Clojure這樣的語言直接支持這種語言,但在Java(可能還有C#)中,你需要通過使用兩個級別的訪問者來模擬三重調度。 這很丑陋,但最大的好處是它可以保持幾何層次結構的清潔和可維護性,並且它將所有交叉邏輯集中在一個編譯單元中。
public interface IGeometry {
void accept(IGeometryVisitor visitor);
}
public interface IGeometryVisitor {
void visitCircle2D(Circle2D circle);
void visitBox2D(Box2D box);
// a method for each concrete type
}
public class Circle2D implements IGeometry {
public void accept(IGeometryVisitor visitor) {
visitor.visitCircle2D(this);
}
}
public class Box2D implements IGeometry {
public void accept(IGeometryVisitor visitor) {
visitor.visitBox2D(this);
}
}
public class IntersectionVisitor implements IGeometryVisitor {
private boolean mResult;
private IGeometry mGeometry2;
public static boolean isOverlapping(IGeometry geometry1, IGeometry geometry2) {
return new IntersectionVisitor(geometry1, geometry2).mResult;
}
private IntersectionVisitor(IGeometry geometry1, IGeometry geometry2) {
mGeometry2 = geometry2;
// now start the process
mGeometry1.accept(this);
}
public void visitCircle2D(Circle2D circle) {
mGeometry2.accept(new Circle2DIntersector(circle));
}
private class Circle2DIntersector implements IGeometryVisitor {
private Circle2D mCircle;
Circle2DIntersector(Circle2D circle) {
mCircle = circle;
}
public void visitCircle2D(Circle2D circle) {
mResult = isOverlapping(mCircle, circle);
}
public void visitBox2D(Box2D box) {
mResult = isOverlapping(mCircle, box);
}
}
private class Box2DIntersector implements IGeometryVisitor {
private Box2D mBox;
Box2DIntersector(Box2D box) {
mBox = box;
}
public void visitCircle2D(Circle2D circle) {
mResult = isOverlapping(circle, mBox);
}
public void visitBox2D(Box2D box) {
mResult = isOverlapping(mBox, box);
}
}
// static methods to compute overlap of concrete types
// For N concrete types there will be N*(N+1)/2 methods
public static boolean isOverlapping(Circle2D circle1, Circle2D circle2) {
return /* intersection of 2 circles */;
}
public static boolean isOverlapping(Circle2D circle, Box2D box) {
return . . .;
}
public static boolean isOverlapping(Box2D box1, Box2D box2) {
return . . .;
}
}
歡迎來到雙重派遣土地! 您所看到的問題是虛擬調度語言缺點的經典例證。 理想情況下,您正在尋找一個相對於多個對象是虛擬的函數,因為確定兩個形狀是否重疊的算法取決於兩個形狀。
您的第二個代碼片段(具有多個具體類)是雙重調度問題的一個常見解決方案的開始,稱為訪問者模式 。 它比if
- then
- else
鏈更好用,但它有一些缺點:
Rectangle2D
的IsOverlapping(Circle2D)
或Circle2D
的IsOverlapping(Rectangle2D)
中Circle2D
Rectangle2D
重疊Circle2D
的確定算法的位置 一種常見的解決方案是引入類型ID,並創建處理幾何形狀重疊的代表的2D數組。 這會受到訪問者的第一個問題的影響,但通過集中決策來解決第二個問題。
我會怎么做:
public interface IGeometry
{
bool IsOverlapping(IGeometry geometry);
}
public class Circle2D : IGeometry
{
public bool IsOverlapping(IGeometry geometry)
{
dynamic dyn = geometry;
return Overlapper.Overlap(this, dyn);
}
}
public class Box2D : IGeometry
{
public bool IsOverlapping(IGeometry geometry)
{
dynamic dyn = geometry;
return Overlapper.Overlap(this, dyn);
}
}
public static class Overlapper
{
public static bool Overlap(Box2D box1, Box2D box2)
{
// logic goes here
}
public static bool Overlap(Box2D box1, Circle2D circle1)
{
// logic goes here
}
public static bool Overlap(Circle2D circle1, Box2D box1)
{
return Overlap(box1, circle1); // No need to rewrite it twice
}
public static bool Overlap(Circle2D circle1, Circle2D circle2)
{
// logic goes here
}
}
上帝,我的答案是愚蠢的。 在這種情況下,您無需再調用其他對象,您可以直接將該對發送到靜態類。 無論如何......我的猜測是沒有一種令人印象深刻的簡單方法。
我不認為你可以實現通用邏輯來確定兩個形狀是否重疊,所以我建議重載isOverlapping
與所有類型。
如果你確實使用抽象類型作為參數,那么你仍然需要檢查有問題的具體類型並執行相關的數學運算。 這里的問題是解決方案不太明確 - 你可以傳入一個具體的GeometricObject2D
類型,它在isOverlapping
沒有實現。 那又怎樣? 拋出異常並不是很好,因為你的isOverlapping(GeometricObject2D o)
調用在技術上受到定義的歡迎。 它違背了OOP的觀點,說“我們接受幾乎所有的GeometricObject2D類型!”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.