是否应该坚持一致性而不是编程约定?


14

在设计类时,是否应该在行为习惯上优于常规编程实践?举一个具体的例子:

一个常见的约定是:如果一个类拥有一个对象(例如,它创建了一个对象),则它负责在对象完成后对其进行清理。.NET中的一个具体示例是,如果您的类拥有一个IDisposable对象,则应在其生命周期结束时对其进行处置。如果您不拥有它,请不要触摸它。

现在,如果我们看一下StreamWriter.NET 中的类,则可以在文档中找到在关闭/处置它时关闭基础流的信息。如果在StreamWriter编写器创建基础文件流并因此需要关闭它时通过传入文件名实例化的情况下这是必需的。但是,也可以传入作者也关闭的外部流。

这使我烦恼了很多次(是的,我知道您可以制作一个非关闭的包装器,但这不是重点),但是显然微软已经做出决定,无论来自何处,始终关闭流是更加一致的。

当我在一个类中遇到这种模式时,我通常会创建一个ownsFooBar标志,如果FooBar通过构造函数注入该标志,则将其设置为false;否则,将其设置为true。这样,当调用者显式传递实例时,将其清除的责任传递给了调用者。

现在我想知道一致性是否应该优于最佳实践(或者我的最佳实践不是那么好)吗?有反对意见吗?

编辑以澄清

“一致性”的意思是:类的一致行为总是获取所有权(并关闭流)与“最佳实践”相比,仅当创建对象或显式转移所有权时才获取对象的所有权。

例如,它令人眼花ying乱:

假设您有两个给定的类(来自某个第三方库),它们接受一个流以对其进行处理,例如创建和处理一些数据:

 public class DataProcessor
 {
     public Result ProcessData(Stream input)
     {
          using (var reader = new StreamReader(input))
          {
              ...
          }
     }
 }

 public class DataSource
 {
     public void GetData(Stream output)
     {
          using (var writer = new StreamWriter(output))
          {
               ....
          }
     }
 }

现在,我想像这样使用它:

 Result ProcessSomething(DataSource source)
 {
      var processor = new DataProcessor();
      ...
      var ms = new MemoryStream();
      source.GetData(ms);
      return processor.ProcessData(ms);
 }

这将失败,但Cannot access a closed stream数据处理器中会出现异常。这有点构造,但应该说明这一点。有多种方法可以修复它,但是我仍然觉得自己可以解决一些本不应该做的事情。


您介意解释为什么StreamWriter的图示行为会使您感到痛苦吗?您是否要在其他地方使用相同的流?为什么?
2012年

@Job:我已经澄清了我的问题
ChrisWue 2012年

Answers:


9

我也使用这种技术,我称其为“切换”,并且我相信它值得模式化。当对象A接受一次性对象B作为构造时间参数时,它还接受一个称为“ handoff”的布尔值(默认为false),如果为true,则丢弃A会级联到丢弃B。

我不赞成激增其他人的不幸选择,因此我永远不会接受微软的不良做法作为既定惯例,也不会认为他人对微软不幸选择的盲目追求无论以何种方式,形式或形式都是“一致的行为”。 。


注意:我在博客中的一篇文章中对此模式进行了正式定义:michael.gr:“移交”模式
Mike Nakis 2012年

作为模式的进一步扩展handOff,如果在构造函数中设置了这样的属性,但是存在另一种修改它的方法,则该其他方法也应该接受一个handOff标志。如果handOff为当前持有的物品指定了它,则应将其丢弃,然后应接受新物品并handOff更新内部标志以反映新物品的状态。该方法不需要检查新旧引用是否相等,因为如果原始引用已“移交”,则原始调用者持有的所有引用都将被丢弃。
超级猫

我同意@supercat,但是我对最后一句话有疑问:如果当前项的交接为true,但是新提供的项通过参考当前项而相等,然后处置当前项实际上是在处理新提供的物品。我认为重新供应同一物品应该是非法的。
Mike Nakis

除非对象是不可变的,实际上不拥有任何资源,并且不关心它是否被处置,否则重新提供相同的物品通常应该是非法的。例如,如果Dispose忽略了对系统画笔的请求,则“ color.CreateBrush”方法可能会随意创建一个新的画笔(需要处理)或返回一个系统画笔(将忽略处理)。重用一个对象,如果它关心处置,但允许重用,如果它不关心,那就是处置它
。– supercat

3

老实说,我认为您是对的。我认为,由于您描述的原因,Microsoft搞砸了StreamWriter类。

但是,自那以后,我看到了很多代码,人们甚至不打算丢弃自己的Stream,因为框架会为他们做这些事情,所以现在修复它弊大于利。


2

我会坚持下去。正如您对大多数人所提到的,有意义的是,如果您的对象创建了子对象,它将拥有它并销毁它。如果从外部提供参考,则在某个时间点,“外部”也将持有相同的对象,因此不应拥有该对象。如果要将所有权从外部转移到对象中,请使用Attach / Detach方法或接受布尔值的构造函数,该布尔值可以清楚地表明所有权已转移。

键入所有内容以获得完整答案后,我认为大多数通用设计模式不一定与StreamWriter / StreamReader类归为同一类。我一直认为,MS选择让StreamWriter / Reader自动接管所有权的原因是,在这种特定情况下,拥有共享所有权没有多大意义。您不能有两个不同的对象同时读取/写入同一流。我敢肯定,您可以编写某种确定性的时间份额,但这将是一种反模式。因此,这可能就是他们说的原因,因为共享流毫无意义,所以让我们始终接管它。但是我不会认为这种行为曾经成为所有类的通用惯例。

您可以将Streams视为一致的模式,其中从设计的角度来看,传入的对象是不可共享的,因此没有任何其他标志,读取器/写入器仅接管其所有权。


我澄清了我的问题-一致是指始终拥有所有权的行为。
ChrisWue 2012年

2

小心。您认为的“一致性”可能只是习惯的随机集合。

“协调用户界面的一致性”,SIGCHI Bulletin 20,(1989年),第63-65页。抱歉,没有链接,这是一篇旧文章。“……为期两天的由15名专家组成的研讨会无法得出一致性的定义。” 是的,它是关于“接口”的,但是我相信,如果您暂时考虑“一致性”,就会发现您也无法定义它。


您是对的,如果专家来自不同的领域,但是在开发代码时,我发现很容易遵循我的团队已经熟悉的一些现有模式(或习惯)。例如,有一天,我们中的一个人在询问我们执行的一些异步操作,当我告诉他,它们的行为与Win32 Overlapped I / O相同,在30秒内他了解了整个接口...在这种情况下,我自己和他来自同一服务器后端Win32背景。一致性!:)
DXM 2012年

@布鲁斯我明白你的意思-我认为很清楚一致性的含义,但显然不是。
ChrisWue 2012年
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.