【问题标题】:How to enforce how an interface is consumed如何强制执行接口的使用方式
【发布时间】:2016-03-09 14:59:19
【问题描述】:

假设我有一个界面:

public interface MyInterface
{
    void CallMeFirst();
    void CallMeDuringApplicationLifecycle();
    void CallMeOnApplicationExit();
}

强制执行调用代码使用此接口上的方法的顺序的最佳方式是什么?

如果按错误的顺序调用方法应该是一个例外。

如果任何方法根本没有调用,这也应该是一个例外。

【问题讨论】:

  • 你有工作代码吗?如果是这样,请将您的问题发布到codereview.stackexchange.com
  • 感谢@roryap 的链接。我不知道代码审查网站 - 看起来很有用。但是,这个问题更像是一个我经常遇到的通用问题。有没有一种模式可以用来强制执行接口的使用方式。
  • 这看起来是一个有趣的问题,我会在Software Engineering 上提问。问题是您有时间耦合,并且接口的目的不是强制执行除了给定成员集的存在 之外的任何内容。接口无法执行您所要求的任何事情,更不用说抛出异常了。
  • 接口公开一个 ... 接口。不是一个过程。您正在谈论流程管理。这可以通过工作流、状态机或许多其他机制来处理
  • 谢谢@Mat'sMug。你是对的,这里显然存在时间耦合。我可以看到如何使用像 this 这样的方法来消除时间耦合以进行简单的初始化。但是,如果您需要在此初始化之后 调用方法,我看不出它会有什么帮助。我当前的问题涉及我必须将缓冲区刷新到服务器,并且我只想在应用程序退出时执行一次。

标签: c# design-patterns


【解决方案1】:

强制执行调用代码使用此接口上的方法的顺序的最佳方式是什么?

你不能。即使你的实现强制执行异常,你也不能保证其他人会这样做。

关心它们的顺序的方法有所谓的时间耦合,这给代码增加了一堆脆弱性。在可能的情况下,您应该重新设计代码,以便方法可以独立运行。

也就是说,在某些情况下您可以做一些事情:

  1. 使用抽象基类。然后,您可以强制执行某些行为,同时将其他行为抽象化以供实施者提供。

  2. 有多个类。当您调用第一个方法时,它会返回一个新对象,让您执行第二步。第二种方法返回一个新对象,让您可以进行第三步,依此类推。

这些都不能解决“如果没有方法被调用”的情况 - 你一定需要 something 来检查,但没有更多细节我不知道如何解决这个问题。

【讨论】:

  • 多类/多接口方案似乎是最无懈可击的。
【解决方案2】:

一个简洁的解决方案是:

public interface First 
{
    Second doFirst();
}

public interface Second
{
    Third doSecond();
}

public interface Third 
{
    void doThird();
}

有人可能会打电话:

FirstFactory.create()
    .doFirst()
    .doSecond()
    .doThird();

但不能调用:

FirstFactory.create()
    .doFirst()
    .doThird()
    .doSecond();

另一种技术解决方案(但从设计角度来看非常糟糕):

public interface MyInterface
{
    First CallMeFirst();
    Second CallMeDuringApplicationLifecycle(First first);
    void CallMeOnApplicationExit(Second second);
}

并以某种方式创建FirstSecond 实现类MyInterface 的内部类。调用可能是:

CallMeOnApplicationExit(CallMeDuringApplicationLifecycle(CallMeFirst()));

【讨论】:

    【解决方案3】:

    也许可以考虑Template Method 设计模式。您的设计引入了temporal coupling,这会带来风险并且可能被视为设计缺陷。

    【讨论】:

      【解决方案4】:

      m3th0dman 展示了一般原则,但他的解决方案并不完全适合我理解的问题,因为您的方法名称让我认为 CallMeDuringAPplicationLifecycle 可以被多次调用。

      在 C# 中,需要清理的资源(例如刷新缓冲区或关闭连接)应使用实现 IDisposable

      interface IConnectionFactory
      {
        IConnection connect(); // This is CallMeFirst
      }
      
      interface IConnection : IDisposable
      {
        void send(); // This is CallMeDuringApplicationLifecycle
      }
      // And the Dispose() method of IDisposable is CallMeOnApplicationExit.
      

      这样,您强制首先调用CallMeFirst,因为没有其他方法可以获取IConnection。由于 C# 程序员识别 IDisposable,他们知道在完成连接后必须调用 Dispose。您实际上无法在 C# 中强制执行此操作,因此这已尽可能接近。 (你也可以编写一个终结器来检查 Dispose 是否被调用,但是你不能做任何事情,只能在那里记录错误,并且不能保证终结器会被调用。)

      【讨论】:

        最近更新 更多