将对象两次传递给相同的方法还是与合并的接口合并?


15

我有一种方法,可以在与数字板交谈后创建数据文件:

CreateDataFile(IFileAccess boardFileAccess, IMeasurer boardMeasurer)

这里boardFileAccessboardMeasurer是相同的实例Board对象,同时实现了IFileAccessIMeasurerIMeasurer在这种情况下,仅用于一种方法,该方法将使板上的一个引脚处于活动状态以进行简单的测量。然后,使用将该测量的数据本地存储在板上IFileAccessBoard位于一个单独的项目中。

我得出的结论CreateDataFile是,通过快速测量然后存储数据来做一件事,对于其他使用此代码然后必须进行测量并写入文件的人来说,使用同一方法进行这两种操作更直观作为单独的方法调用。

对我来说,将同一对象两次传递给方法似乎很尴尬。我认为做一个本地接口IDataFileCreator,可以扩展IFileAccessIMeasurer再有一个包含一个实现Board的实例,将只需要调用所需的Board方法。考虑到同一板对象将始终用于测量和文件写入,将同一对象两次传递给方法是否是一种不好的做法?如果是这样,使用本地接口和实现是否是合适的解决方案?


2
几乎不可能从您使用的名称中识别出代码的意图。传递给名为CreateDataFile的方法的名为IDataFileCreator的接口令人费解。他们在争夺持久数据的责任吗?无论如何,CreateDataFile是什么类的方法?测量与持久数据无关,所以很明显。您的问题不是关于代码所遇到的最大问题。
马丁·马特

是否可以想象您的文件访问对象和度量对象可能是两个不同的对象?我会说是的。如果现在进行更改,则必须将其更改版本2,该版本支持通过网络进行测量。
user253751 '19

2
但是,这是另一个问题-为什么数据文件访问和测量对象首先相同?
user253751 '19

Answers:


40

不,这很好。这仅表示该API 与您当前的应用程序有关

但这并不能证明数据源和度量者永远不会有不同的用例。API的目的是为应用程序程序员提供可能性,但并非全部都会使用。您不应该人为地限制API用户可以执行的操作,除非它使API复杂化,从而使网络的可理解性下降。


7

同意@KilianFoth的回答,这很好。

不过,如果需要,您可以创建一个方法,该方法采用实现两个接口的单个​​对象:

public object CreateDataFile<T_BoardInterface>(
             T_BoardInterface boardInterface
    )
    where T_BoardInterface : IFileAccess, IMeasurer
{
    return CreateDataFile(
                boardInterface
            ,   boardInterface
        );
}

没有普遍的理由认为参数必须是不同的对象,如果方法确实要求参数不同,那将是一个特殊的要求,它的合同应明确说明。


4

我得出的结论CreateDataFile是,通过快速测量然后存储数据来做一件事,对于其他使用此代码然后必须进行测量并写入文件的人来说,使用同一方法进行这两种操作更直观作为单独的方法调用。

我认为这实际上是您的问题。该方法没有做一件事。它正在执行两个截然不同的操作,这些操作涉及到不同设备的 I / O ,这两项操作都将卸载到其他对象上:

  • 获取测量
  • 将该结果保存到某处的文件中

这是两个不同的I / O操作。值得注意的是,第一个文件不会以任何方式改变文件系统。

实际上,我们应该注意,这是一个隐含的中间步骤:

  • 获取测量
  • 将测量序列化为已知格式
  • 将序列化的测量结果保存到文件中

您的API应该以某种形式分别提供每一个。您如何知道呼叫者不希望将测量结果存储在任何地方都不会?您怎么知道他们不想从其他来源获得测量值?您怎么知道他们不想将其存储在设备以外的地方?有充分的理由使操作脱钩。在一个裸露的最低限度,每个单独的片应当是可用的任何呼叫者。如果我的用例不要求将测量结果写入文件中,则不要强迫我将其写入文件。

例如,您可以这样分离操作。

IMeasurer 有一种获取度量的方法:

public interface IMeasurer
{
    IMeasurement Measure(int someInput);
}

您的测量类型可能很简单,例如stringdecimal。我并不是在坚持要求您为其提供接口或类,但是这会使此处的示例更加通用。

IFileAccess 有一些保存文件的方法:

interface IFileAccess
{
    void SaveFile(string fileContents);
}

然后,您需要一种序列化测量的方法。将其构建到表示度量的类或接口中,或使用实用程序方法:

interface IMeasurement
{
    // As part of the type
    string Serialize();
}

// Utility method. Makes more sense if the measurement is not a custom type.
public static string SerializeMeasurement(IMeasurement m)
{
    return ...
}

尚不清楚您是否已分离出此序列化操作。

这种分离可以改善您的API。它使调用者可以决定他们需要什么以及何时需要,而不是强加您对执行什么I / O的先入为主的想法。调用者应具有执行任何有效操作的控件,无论您认为有用还是无效。

一旦对每个操作都有单独的实现,您的CreateDataFile方法将成为

fileAccess.SaveFile(SerializeMeasurement(measurer.Measure()));

值得注意的是,完成所有这些操作后,您的方法几乎没有增加任何价值。上面的代码行对于调用者来说并不难直接使用,并且您的方法最多只是为了方便。它应该是并且是可选的。这是API行为的正确方法。


在排除了所有相关部分之后,并且我们承认该方法只是一种方便,我们需要重新表述您的问题:

什么是呼叫者最常见的用例?

如果要使在同一块板上进行测量和写入的典型用例更加方便,那么将其Board直接用于类上就很有意义了:

public class Board : IMeasurer, IFileAccess
{
    // Interface methods...

    /// <summary>
    /// Convenience method to measure and immediate record measurement in
    /// default location.
    /// </summary>
    public void ReadAndSaveMeasurement()
    {
        this.SaveFile(SerializeMeasurement(this.Measure()));
    }
}

如果这不能提高便利性,那么我根本不会理会该方法。


这是一种方便的方法,带来了另一个问题。

IFileAccess接口是否应该知道测量类型以及如何序列化?如果是这样,您可以将方法添加到IFileAccess

interface IFileAccess
{
    void SaveFile(string fileContents);
    void SaveMeasurement(IMeasurement m);
}

现在,呼叫者只需执行以下操作:

fileAccess.SaveFile(measurer.Measure());

与问题中设想的便捷方法一样简短,而且可能更清晰。


2

当单个项目足够时,使用方就不必处理一对项目。就您而言,在调用之前,它们几乎不会CreateDataFile

您建议的潜在解决方案是创建一个组合的派生接口。但是,此方法需要实现两个接口的单个​​对象,这很受限制,可以说是泄漏性抽象,因为它基本上是为特定实现定制的。考虑一下如果有人想在单独的对象中实现两个接口会多么复杂:他们必须代理一个接口中的所有方法才能转发到另一个对象。(FWIW,另一种选择是只合并接口,而不要求一个对象必须通过派生的接口实现两个接口。)

但是,另一种较少约束/约束实现的方法IFileAccess是与IMeasurerin组成配对,以便其中一个绑定到另一个引用。(这在某种程度上增加了其中一个的抽象性,因为它现在也代表了配对。)然后CreateDataFile可以只接受其中一个引用,例如IFileAccess,仍然根据需要获得另一个。您当前的实现(作为同时实现两个接口的对象)将仅return this;作为构成参考,此处为IMeasurerin中的getter IFileAccess

如果配对在开发的某个时刻被证明是错误的,也就是说有时使用不同的测量器来进行相同的文件访问,那么您可以进行相同的配对,但是要在更高的级别进行,这意味着引入的附加接口会不是一个派生的接口,而是一个具有两个getter的接口,通过组合而不是派生将文件访问和度量程序配对在一起。这样,只要配对有效,使用方就可以考虑一个项目,并在必要时处理单个对象(以组成新的配对)。


另一方面,我可能会问谁是所有者CreateDataFile,问题就出在这个第三方是谁。我们已经有一些消耗性的客户端来调用CreateDataFile,的拥有对象/类CreateDataFile以及IFileAccessIMeasurer。有时,当我们从更大的角度看待上下文时,可以出现替代的,有时更好的组织。由于上下文不完整,因此在这里很难做,因此值得深思。


0

有些人提出 CreateDataFile太多的建议。我可能建议Board这样做做得太多,因为访问文件似乎是董事会其他成员的另一个关注点。

但是,如果我们假设这不是一个错误,那么更大的问题是接口应该由客户端定义,在这种情况下 CreateDataFile

接口分离原则规定,客户不应该比它需要更多的依赖于一个界面上。从中借用该短语这个其他答案中,可以将其解释为“接口是由客户的需求定义的”。

现在,可以使用IFileAccessIMeasurer其他答案所建议的来组合此特定于客户端的界面,但是最终,该客户端应具有为其量身定制的界面。


@Downvoter-这是不正确的或可以改善的吗?
Xtros
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.