我应该打破哪种模式?

Which pattern should I break?

不要重复自己 要么 封装?

假设我创建了以下内容:

  1. 实现 IList 的接口 IMask。
  2. 一个Class实现IMask的点。
  3. 一个 Class 包含一些整数和一个 Spot 作为字段的标记。

我想让 Marker 实现 IMask 接口。但是然后我会重复自己(最后检查代码) 或者,我可以在标记 public 中设置 Spot。但随后我将公开我的 class 的实现。 或者我可以从 Spot 继承我的 Spot,但这不是理想的解决方案,因为从语义上讲,标记不是特定类型的 Spot。

如果我创建另一个 class 并将 Spot 作为字段并且我想再次实现 IMask 接口怎么办? 我会再次重复自己。 那么,我应该如何进行?我应该在 Spot public 中制作列表吗?然后在 Marker public 中创建 Spot? 还是我应该重复通话?

interface IMask : IList<Point>
    {
        public void MoveTo(Point newCenter);
        // ... other Methods
    }

    public class Spot : IMask
    {
        List<Point> points;

        public void DoSpotyStuff()
        {
            // blabla
        }

        // Other methods
        // ...
        // Finally the implementation of IMask
        public void MoveTo(Point newCenter)
        {
            // blabla
        }

        // And of course the IList methods
        public void Add(Point newPoint)
        {
            points.Add(newPoint);
        }
    }

    public class Marker : IMask
    {
        private Spot mySpot;
        private int blargh;
        // Other fields

        public void MarkeryMethod()
        {
            // Blabla
        }

        // HERE IS THE PROBLEM: Should I do this and repeat myself
        public void MoveTo(Point newCenter) { mySpot.MoveTo(newCenter); }

        // And here I'm REALLY starting to repeat myself
        public void Add(Point newPoint) { mySpot.Add(newPoint); }
    }

观察: 接口 IMask 没有继承自 List。它正在实现 IList 接口,后者又 implements ICollection, IEnumerable 假设标记在语义上不是特殊类型的 Spot。所以,即使我可以从 Spot 继承并解决问题,它也不是最好的解决方案。

一个接口就是建立一种契约,class 将要实现它的class 将有这个方法。所以你可以做的是实现你的接口的 absctract/base class 然后 classes 标记和 Spot 可以从基础 class 继承,你已经准备好了去。

public abstract class BaseClass : IMask {...}

public class Marker : BaseClass{...}

public class Spot : BaseClass{...}

2020 年 1 月更新: 在 C#8 中,你可以有默认的接口方法和实现(这基本上杀死了抽象 classes 的用例),你可以在这里看看:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods

interface IMask : IList<System.Drawing.Point>
{
    public void MoveTo(System.Drawing.Point newCenter);
    // ...
    public void 
}
public class Mask : IMask
{
    // ...
}
public class Spot
{
    public Mask Mask = new Mask();
    // ...
}
public  class Marker
{
    public Mask Mask = new Mask();
    /// ...
}

为了方便而简化,你最好使用属性。

在我看来,您的选择应该基于您期望应用程序如何增长,即向前兼容性。

如果您认为 Marker 会进化,例如也许有一天它会包含多个 Spot,或多个支持 IMask 的对象,然后重复自己是一种方法,因为您将要协调对包含的所有对象的 MoveTo 和 Add 的调用在标记中,你会很高兴在标记中有一个间接层。

如果您认为 Spot 会发展,例如如果 Spot 要添加更多方法,如 ChangeSize 或 Remove,最好的办法可能是将 Spot 公开为类型为 IMask 的标记的 public 属性。这样,新属性会立即公开,而无需编写额外的包装代码。