并非所有接口成员都将实现

时间:2010-07-08 21:43:22

标签: c#

通过说某些类的“某些”接口成员通常只是因为在某些情况下它们不适用所以你在方法体中抛出一个未实现的错误是正确的吗?

例如,假设我创建了一个接口IAPIAuthentication,该接口将服务器作为类的合同,这些类将执行对第三方API(例如Facebook)的身份验证请求,以及我们稍后将实施的其他API。

所以我的IAPIAuthentication接口可能具有以下属性:

// The URI that the auth HTTP Request will go to (minus any querystring values, this is just the base)
AuthenticationURI (property)

// unique ID for your API account with whatever API you are using (Facebook, Picasa, whatever)
ClientID  (property)

// unique secret code also obtained when you sign up for an API account and used in auth calls
ClientSecret (property)

// a confirmation code sent back from the 
AuthenticationVerificationCodeID (method)

// a boolean property set to true if an AuthenticationVerificationID was received back after an Auth request
AuthenticationWasSuccessful (property)

// sends the actual HTTP Request to the specified Uri
SendRequest()

好的,在很多情况下,其他API在其身份验证过程中需要相同的信息(例如PhotoBucket等)

好的,所以我创建了这个接口,当我为这些API创建包装器时将用于各种实现,并且当我开始使用某些API创建功能时,这一点的重点在于在结构和重用方面创建一些良好的通用性。我将在这里构建这些通用接口的包装器。这些是大多数NVP API的基本构建块,因此无论我正在实现什么API,或者我在这里创建的任何接口中的至少90%的值,我在这些接口中放置的内容通常都会被使用。

所以我将创建一个类,例如实现该接口的FacebookAuth。所有发现和花花公子。好的,下次我在一个新项目上工作时我说嘿,我还要实现那个界面,保持团队标准/模式以创建第三方包装器项目)我知道我需要让我们说所有这些属性但可能减去一两个,因为该提供者在其身份验证过程中不需要该信息。让我们说它只是一个它不需要的东西。

所以我的问题是这些:

  1. 当我稍后开始创建所有这些第三方API包装器项目时,我的方法是否对重用和一致性的目标有意义?

  2. 一般来说,如你所知,你可以通过接口来避免实现某种接口方法,如果有人试图在你的子类中使用该方法,那么至少在.NET中抛出一个未实现的异常。我不确定属性......如果你绝对不得不(罕见的一次性)你怎么忽略它们。因此,期望您的界面永远不会“完美”,所有成员将始终全面使用100%,这是“正常”吗?我的意思是可以说只放入将100%使用的元素。好吧,但这取决于......因为有些API在大多数情况下会在这种情况下全部使用..只有一些一次性不会......所以对我来说,包含一些可能不会被使用的东西仍然是有意义的在其他人。

  3. 我只想根据经验获得一些输入......对于Interfaces比我更熟悉的开发人员。我没有多少使用接口..我得到它们是什么(合同,yada yada),但我正在试图找出它们的最佳用途,我真的认为这是一个很好的用途。我也会有抽象类......只有少数所以我知道两者之间的区别。我只是想知道是否可以接受非完美的接口。我猜这就是为什么他们的API版本正确?但是即使在现有版本中,你也会有一些类没有完全实现接口的所有成员,但是实现了这个接口的一致性和重用,无论你的应用程序或包装器总体如何......

    我希望我没有絮絮叨叨。如果我不清楚,请告诉我。并保持在我的ind中我上面的例子并不是真的完整但是得到了重点。

4 个答案:

答案 0 :(得分:17)

通常不希望部分实现接口。

我的建议是将您的问题分开,即将您的界面分成几个界面,以便实现完整和自主。

但是,有些情况下,统一界面的好处超过了关注点的分离,恕我直言,其中很少有这些,但它们肯定存在。一个例子是在.NET BCL中,其中一些ICollection类是只读的,因此不支持AddRemoveClear。为避免使用控制流的异常,ICollection提供IsReadOnly属性以指示不支持这些方法。

如果您仍然决定为所有代码保留一个界面,请抛出NotSupportedException而不是NotImplementedException,这样就可以选择不显示该操作。

答案 1 :(得分:3)

根据您的描述,您正在创建一个将由多个类似服务实现的界面。由于我不知道您的要求,约束或接口作者/实现者之间的责任分工,所以让我来研究一些可用的选项。这绝不是一个详尽的清单 - 只是那些常见的清单:

  1. 创建一个“超集”公共接口,对于某些实现,可能无法完全实现。例外报告不支持哪些功能。
  2. 创建一个“最低公分母”界面,该界面只包含所有实现都可以支持的成员。不会抛出任何不受支持的异常,因为每个人都需要支持所有功能。
  3. 为每个实施服务创建完全独立的接口(这有效地破坏了通用的统一接口的价值)。
  4. 作为#1的变体,添加一个可以报告支持哪些功能的属性/方法(因此您不会使用异常作为检查支持的功能的方法)。
  5. 作为#2的变体,创建继承的接口,逐步添加其他功能。
  6. 选项#1 会使编写可靠代码变得困难。你总是发现自己在try / catch块中包装每个方法调用 - 这可能变得不可读。更糟糕的是,如果您忘记在某处执行此操作,则可以通过调用堆栈传播未处理的异常,这可能本身就是破坏性的。但是,对于那些实现接口来处理的人来说,这种方法最简单 - 如果不支持某个功能,只需抛出一个NotSupportedException - 很容易。

    选项#2 需要进行重要分析才能正确识别支持功能的真正常见子集。如果您错误地猜测所有实现的能力是否支持某个功能,这种方法会变得更加复杂,您可能会发现自己处于默认情况下的情况# - 要求您添加所有必要的逻辑来处理潜在的异常。 / p>

    选项#3 基本上是:“我放弃。这里没有通用界面”。 有时这是一个可以接受的答案。试图楔入一个不会自然出现的接口可能比简单地编写单独的(不相关的)接口或实现类更糟糕。在这种情况下,您通常会将每个已知实现和组功能的“策略”实现为更粗粒度的方法,这些方法可以在所有情况下可靠地实现。

    选项#4 非常有用,它允许您在调用之前首先检查功能是否受支持。如果调用,实现者仍可能抛出NotSupportedException,不同之处在于您不会调用开头不支持的实现。这里的问题是您需要设计一种机制,允许消费者测试支持哪些功能。这种方法不是面向对象或特别可扩展(如果您的接口发展),但它很简单。

    选项#5 尝试通过分区功能改进#2 - 可能是没有有意义的“最低公分母”接口 - 相反,可能有一系列相关接口可以一起使用。此选项还可能导致零碎接口的扩散,这可能成为维护挑战。从好的方面来说,这种方法允许您通过尝试强制转换为特定接口来轻松测试实现支持哪些功能。它比某种SupportedCapabilities枚举更具可扩展性。在这种方法中,每个实现都应该支持接口的所有 none 。这并非总是可以实现,并且与选项#2中描述的挑战相冲突。

    正如谚语所说:“任何编程问题都可以通过添加另一个抽象级别来解决”。但是,您需要决定哪些抽象级别对于您要解决的问题是必要的或适当的。

答案 2 :(得分:2)

除了这里的其他答案之外,如果你真的必须提供一个不完整的接口实现,那么你应该在访问不受支持的成员时抛出NotSupportedException。您还可以使用explicit interface implementation降低这些成员的曝光度。

public interface IExample
{
    void Add(int id);
    void Remove(int id);
    bool Contains(int id);
}

public class ReadOnlyExample : IExample
{
    private readonly HashSet<int> _ids;

    public ReadOnlyExample(IEnumerable<int> ids)
    {
        _ids = new HashSet<int>(ids);
    }

    public bool Contains(int id)
    {
        return _ids.Contains(id);
    }

    void IExample.Add(int id)  // explicit interface implementation
    {
        throw new NotSupportedException("This example is read-only!");
    }

    void IExample.Remove(int id)  // explicit interface implementation
    {
        throw new NotSupportedException("This example is read-only!");
    }
}

答案 3 :(得分:1)

我同意哈佛的回答。此外,关于如何处理属性,您可以让不受支持的属性getter / setter像方法一样抛出异常。例如:

public string ClientID
{
    get
    {
        throw new NotSupportedException();
    }
}