有趣的 API 设计/模式

发布于 2024-07-13 00:21:31 字数 1257 浏览 10 评论 0原文

我正在重新设计我们内部 ORM 工具的一部分,并且我想直接向最终开发人员公开 Field(表示数据库中字段的类,如 CustomerFirstName)。

所以这很容易完成,但是 API 有点难看,因为这个 Field 类以前在内部使用并且太开放了。 例如,这只是一个小例子:IsDirty 属性不是只读的,这是最终开发人员不应该能够篡改的。

我考虑过创建两个接口,IPublicField 和 IPivateField,并尝试让字段类来实现这两个接口。 然而,继续 IsDirty 示例,我不想要这样的东西:

Public ReadOnly Property PrivateIsDirty Implements IPrivateField.IsDirty
 ...
End  Property

Public Property IsDirty Implements IPublicField.IsDirty
 ...
End  Property

...这有点难看,而且您仍然可以转换回 Field 类并进入非只读方法。 我也不想引入单独的 setter 方法,因为这将是我不想考虑的另一个重大更改,而且它还会造成与 API 其他部分的不一致。

我最终将 Field 类重命名为 InnerField,并围绕它创建了一个外观/包装样式结构,如下所示:

Public Class Field
    Implements BusinessObjects.IField

    Private InnerField As BusinessObjects.IInnerField

    Public Sub New(ByVal field As IInnerField)
        InnerField = field
    End Sub

    ...

     Public ReadOnly Property IsDirty() As Boolean Implements BusinessObjects.IField.IsDirty
        Get
            Return InnerField.IsDirty
        End Get
    End Property

    ...
End Class

这似乎效果很好。 在内部,InnerField 是适当开放的,我们可以在未来自由地使其更加开放,而不会影响最终开发人员,而在外部,Field 类提供了最终开发人员需要的简化的锁定工具。

因此,假设这是一致的,我想知道在这种情况下你会如何进行,以及我的解决方案从外部看来是否合理。

谢谢!

I am re-designing part of our internal ORM tool, and I want to expose Field (a class that represents a field in the database, like CustomerFirstName) directly to the end-developer.

So that was easy enough to accomplish, however the API got a little ugly because this Field class was previously used internally and is too open. For instance, and this is just one small example: the IsDirty property was not read-only, and that's something end-developers shouldn't be able to tamper with.

I thought about maybe creating two interfaces, IPublicField and IPrivateField, and trying to get the field class to implement both of them. However, continuing with the IsDirty example, I didn't want something like this:

Public ReadOnly Property PrivateIsDirty Implements IPrivateField.IsDirty
 ...
End  Property

Public Property IsDirty Implements IPublicField.IsDirty
 ...
End  Property

...It's just a little ugly, plus you could still cast back to the Field class and get into the non-readonly method. I also didn't want to introduce a seperate setter method because it would be another breaking change that I don't want to think about, and it also would create an inconsistency with other parts of the API.

I ended up renaming the Field class to InnerField, and creating a facade/wrapper style structure around it like this:

Public Class Field
    Implements BusinessObjects.IField

    Private InnerField As BusinessObjects.IInnerField

    Public Sub New(ByVal field As IInnerField)
        InnerField = field
    End Sub

    ...

     Public ReadOnly Property IsDirty() As Boolean Implements BusinessObjects.IField.IsDirty
        Get
            Return InnerField.IsDirty
        End Get
    End Property

    ...
End Class

This seems to be working out pretty well. Internally, the InnerField is suitably open and we have the freedom to make it more open in the future without impacting end-developers, and externally the Field class provides the simplified, locked down tool that end-developers need.

So, assuming that was coherent, I was wondering how you might have proceeded in this situation, and whether my solution seems reasonable from the outside.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧情勿念 2024-07-20 00:21:31

在 isDirty 属性的特定情况下,您可以限制设置器的范围:

public bool IsDirty
     {
        get
        {
            // logic here that the end-devs can use safely.
        }
        private set
        {
            // logic here that is only exposed to other members of your private
            // ORM assembly.
        }
    }

In the specific case of the isDirty property, you could just restrict the scope of the setters:

public bool IsDirty
     {
        get
        {
            // logic here that the end-devs can use safely.
        }
        private set
        {
            // logic here that is only exposed to other members of your private
            // ORM assembly.
        }
    }
下雨或天晴 2024-07-20 00:21:31

在 VB.NET 上下文中,您可以指定 IsDirty 属性范围的设置器。 如果您希望程序集中的其他类可以“设置”此属性,只需将其范围设置为 Friend:

Public Property IsDirty() As Boolean
    Get
        Return _isDirty
    End Get
    Friend Set(ByVal trueFalse As Boolean)
        _isDirty = trueFalse
    End Set
End  Property

现在,任何外部程序集都可以检查 IsDirty 属性,但只有同一程序集中的类可以设置它。

In the context of VB.NET, you can give the setter of the IsDirty property scope. If you want this property "set-able" by other classes in the assembly, just make it scoped as Friend:

Public Property IsDirty() As Boolean
    Get
        Return _isDirty
    End Get
    Friend Set(ByVal trueFalse As Boolean)
        _isDirty = trueFalse
    End Set
End  Property

Now, any outside assembly can check the IsDirty property, but only classes within the same assembly can set it.

雪化雨蝶 2024-07-20 00:21:31

您可以尝试使用 CAS 对您有利。 下面是一个示例:

    public bool IsDirty
    {
        get;
        [StrongNameIdentityPermissionAttribute(SecurityAction.LinkDemand, Name = "<assembly name>")]
        set;
    }

该属性位于 System.Security 中。

如果您担心开发人员可能通过反射调用它,您可能需要将 LinkDemand 更改为 Demand:但您也可以“替换”这些开发人员而不是 SecurityAction :)。

唯一的问题是现在您可以在运行时进行检查。 这可能不是您想要的,但如果不进行重大重组工作,我看不出它会以任何其他方式发挥作用。

You could try using CAS to your advantage. Here is an example:

    public bool IsDirty
    {
        get;
        [StrongNameIdentityPermissionAttribute(SecurityAction.LinkDemand, Name = "<assembly name>")]
        set;
    }

The attribute is in System.Security.

You might want to change LinkDemand to Demand if you are worried that your developers might call it through reflection: but you could also 'replace' those developers instead of the SecurityAction :).

The only catch is that now you have the checks at runtime. This probably isn't what you want, but I can't see it working any other way without major restructuring efforts.

陪你搞怪i 2024-07-20 00:21:31

如果您不反对使用 get 和 set 方法而不是属性,请尝试让 IPublicField 实现 IPrivateField。

Public Interface IPrivateField
    Function GetDirty() As Boolean
End Interface

Public Interface IPublicField
    Inherits IPrivateField

    Sub SetDirty(ByVal dirty As Boolean)
End Interface

Public Class Whatever
    Implements IPublicField

    Public Function GetDirty() As Boolean Implements IPrivateField.GetDirty
        ' logic here
    End Function

    Public Sub SetDirty(ByVal dirty As Boolean) Implements IPublicField.SetIsDirty
        ' logic here
    End Sub
End Class

或者,如果您可以放弃 VB 并切换到 C#,这也可以正常工作:

public interface IPrivateField
{
    bool IsDirty { get;}
}

public interface IPublicField : IPrivateField
{
    new bool IsDirty { get; set; }
}

public class Whatever : IPublicField
{
    public bool IsDirty 
    {
        get
        {
            // logic here
        }
        set
        {
            // logic here
        }
    }
}

If you don't object to using a get and set method instead of a property, trying having IPublicField implement IPrivateField.

Public Interface IPrivateField
    Function GetDirty() As Boolean
End Interface

Public Interface IPublicField
    Inherits IPrivateField

    Sub SetDirty(ByVal dirty As Boolean)
End Interface

Public Class Whatever
    Implements IPublicField

    Public Function GetDirty() As Boolean Implements IPrivateField.GetDirty
        ' logic here
    End Function

    Public Sub SetDirty(ByVal dirty As Boolean) Implements IPublicField.SetIsDirty
        ' logic here
    End Sub
End Class

Alternately, if you can ditch VB and switch to C#, this works just fine:

public interface IPrivateField
{
    bool IsDirty { get;}
}

public interface IPublicField : IPrivateField
{
    new bool IsDirty { get; set; }
}

public class Whatever : IPublicField
{
    public bool IsDirty 
    {
        get
        {
            // logic here
        }
        set
        {
            // logic here
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文