不要重复自己要么封装?
假设我创建了以下内容:
我想让Marker实现IMask接口。但是然后我会重复我自己(最后检查代码)或者,我也可以在Marker公众圈中找到我的Spot。但是然后我将公开我的课程的实现。或者,我可以从Spot继承Spot,但这并不是理想的解决方案,因为从语义上来说,标记不是Spot的特定类型。
如果我创建另一个具有Spot作为字段的类,又想实现IMask接口,该怎么办?我会再重复一次。那么,我应该如何进行?我应该将Spot中的列表公开吗?然后将Marker中的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从语义上讲,假设Marker不是Spot的特殊种类。因此,即使我可以从Spot继承并解决问题,也不是最好的解决方案。
接口是建立一种契约,该契约将实施它的类将具有此方法。因此,您可以做的是一个实现您的Interface的抽象类/基类,然后Marker和Spot类可以从该基类继承,并且一切都准备好了。
public abstract class BaseClass : IMask {...}
public class Marker : BaseClass{...}
public class Spot : BaseClass{...}
2020年1月更新:在C#8中,您可以使用带有实现的默认接口方法(基本上可以消除抽象类的用例),您可以在此处查看:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods
在我看来,您的选择应基于应用程序的增长方式,即前向兼容性。
[如果您认为Marker将会发展,例如也许有一天它会包含多个Spot或一个支持IMask的对象,然后重复自己的做法是可行的,因为您将需要协调对其中包含的所有对象的MoveTo和Add调用在标记中,您会很高兴标记中有一层间接层。
[如果您认为Spot将会发展,例如如果Spot将添加更多方法,例如ChangeSize或Remove,则最好的办法是将Spot作为IMask类型的Marker的公共属性公开。这样,新属性将立即公开,而无需编写其他包装代码。
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();
/// ...
}
为方便起见,您最好使用属性。