我有许多类(具有不同的基类等),它们共享一个接口。
public interface IObjectWithSector
{
Sector Sector {get;}
}
但是,我的Sector基类(与接口和所有实现基类在同一个程序集中定义)有一个公共的void AddObject(IObjectWithSector obj);
方法,其中必须包含一条obj.Sector = this;
指令。这带来了一个问题。如果我在接口中公开setter,那么所有使用者类都将能够设置Sector,从而能够绕过AddObject方法中的逻辑。但如果我把它放在我的公共界面之外,即使Sector也看不到二传手。我在考虑"内部接口",但这些东西并不好,因为我不能从它们继承公共接口,所以无论如何都是交叉的。
这方面的最佳模式是什么
与其提供接口,不如提供一个具有内部setter属性的基类。所有使用者都必须派生自此类。
你不能强迫客户以某种方式实现只有你的程序集才能看到的东西。从客户的角度来看,这没有多大意义。如果他们需要实现setter,他们也可以调用它。毕竟,这是他们程序集中的代码。
然而,基类实现是由您自己提供的,因此可以具有内部成员。客户可以扩展它,但你可以保证内部的东西在那里,只有你才能看到。
您要求的是具有只读接口的东西,然后依赖于它也是可写的。从根本上说,这是一个弱抽象,即使你通过转换到内部接口来解决它,也会让用户感到困惑。
如果在其他方面合适,您可以通过使Sector
类成为其他对象的工厂来解决问题:
public class ConcreteObjectWithSector: IObjectWithSector {
private Sector sector;
internal ConcreteObjectWithSector(Sector sector) {
this.sector = sector;
}
// everything else
}
public class Sector {
public ConcreteObjectWithSector CreateConcreteObjectWithSector() {
var obj = new ConcreteObjectWithSector(this);
this.stuff.Add(obj);
// Other logic
return obj;
}
}
你没有一个方法可以处理所有的comer,但你知道你的对象的不变量是满足的,不必损害你的抽象。
可以将公共get与内部set混合使用但由于内部get不得不使用公共get,它变得一团糟其中一个需要显式声明(因为get和set不是分开的,而是单个属性的一部分)IMO的清洁解决方案是使用设置方法
internal void SetSector(Sector sector);
还要注意,内部接口可以继承公共接口
internal interface IPrivate : IPublic { }
private IPrivate blah = ...
public IPublic Blah { get { return blah; } }
工作得很好(这也可以与点网4.0中的集合等的通用协方差一起工作)
为什么不显式地实现内部接口:
public interface IObjectWithSector
{
Sector Sector { get; }
}
internal interface IObjectWithSectorSetter: IObjectWithSector
{
void SetSector(Sector sector);
}
现在你可以做:
public ObjectWithSector: IObjectWithSectorSetter
{
public Sector Sector { get { ... } }
void IObjectWithSectorSetter.SetSector(Sector sector) { ... }
}
我不认为那样会变得一团糟。
EDIT:更改接口的继承顺序,以前的方式会给您带来不一致的accesss编译器错误。
您似乎试图将任意数据(Sector
实例)添加到消费者的对象中,而消费者(消费者)不应该访问或更改这些数据。
有一种方法可以干净地做到这一点(实际上有两种方法),而不暴露任何内容,甚至不强迫消费者实现您的接口。
第一种方法是维护一个Dictionary<object, Sector>
,它将为每个消费者的对象保存Sector
。但这种方式是不好的,因为您不知道何时从字典中删除对象,因为您没有管理消费者对象的生存期。这也将防止消费者的对象被垃圾收集。
第二种(正确的)方法是使用ConditionalWeakTable<object, Sector>
,它正是为此目的而制作的——将任意数据附加到对象,而不影响其GC根状态。只要使用者的对象是"活动的"(不是垃圾收集的),您就可以始终读取其Sector,并且当对象被垃圾收集时,该条目就会被删除。