如何制作内部接口模式

本文关键字:模式 接口 内部 何制作 | 更新日期: 2023-09-27 17:58:59

我有许多类(具有不同的基类等),它们共享一个接口。

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,并且当对象被垃圾收集时,该条目就会被删除。