簡體   English   中英

設計一個抽象基類。 使用什么類型,抽象或具體?

[英]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鏈更好用,但它有一些缺點:

  • 每次添加新形狀時,必須使用方法擴展所有形狀,以檢查與新添加的形狀的重疊
  • 目前還不清楚在Rectangle2DIsOverlapping(Circle2D)Circle2DIsOverlapping(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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM