【问题标题】:Should methods that are required to be executed in a specific order be private? [closed]需要按特定顺序执行的方法是否应该是私有的? [关闭]
【发布时间】:2010-04-28 14:59:40
【问题描述】:

我有一个检索一些数据的类,图像对它们做了一些处理,然后它们使用 Web 服务将它们上传到第三方应用程序。 对象需要按顺序执行一些特定的步骤。 我的问题是我是否应该像这样公开公开每种方法。

myObject obj = new myObject();
obj.RetrieveImages();
obj.RetrieveAssociatedData();
obj.LogIntoThirdPartyWebService();
obj.UploadStuffToWebService();

或者所有这些方法都应该是私有的,并像这样封装在一个公共方法中。

public class myObject()
{
 private void RetrieveImages(){};
 private void RetrieveAssociatedData(){};
 private void LogIntoThirdPartyWebService(){};
 private void UploadStuffToWebService(){};

 public void DoStuff()
  {
   this.RetrieveImages();
   this.RetrieveAssociatedData();
   this.LogIntoThirdPartyWebService();
   this.UploadStuffToWebService();
  }
}

就是这样称呼的。

myObject obj = new myObject();
obj.DoStuff();

【问题讨论】:

标签: c#


【解决方案1】:

这取决于谁知道应该以这种方式调用方法。

  • 消费者知道:例如,如果对象是Stream,通常Stream的消费者决定何时使用Open、@987654323 @ 和 Close 流。显然,这些方法必须是公开的,否则对象将无法正常使用。 (*)

  • 对象知道:如果 对象 知道方法的顺序(例如,它是 TaxForm 并且必须按特定顺序进行计算),那么这些方法应该是私有的,并通过一个更高级别的步骤公开(例如,ComputeFederalTax 将调用 CalculateDeductionsAdjustGrossIncomeDeductStateIncome)。

  • 如果步骤数多于少数,您将需要考虑使用Strategy,而不是将步骤直接耦合到对象中。然后你就可以改变周围的东西,而不会过多地使用对象或其界面。

在您的具体情况下,您对象的消费者似乎并不关心除了正在发生的处理操作之外的任何事情。因为它不需要知道这些步骤发生的顺序,所以应该只有一个名为 Process 的公共方法(或类似的东西)。


(*) 但是,通常对象至少知道可以调用方法以防止无效状态的顺序,即使它不知道何时 实际执行步骤。也就是说,对象应该足够了解以防止自己进入无意义的状态;如果您尝试在Open 之前调用Close,则抛出某种异常就是一个很好的例子。

【讨论】:

    【解决方案2】:

    如果方法 B() 真的不能被调用,除非首先调用 A(),那么正确的设计要求 A 应该返回 B 需要的某个对象作为参数。

    这是否总是可行是另一回事,但这就是应该做的。

    【讨论】:

    • 我直觉上喜欢这个答案,但我不知道为什么。您是否有一些参考资料可以更多地解释为什么会这样?
    • @Daniel 这是通过代码设计强制执行调用顺序的唯一方法。我第一次听到它是在与 Bob Martin 的一次会议上。
    • @Rex:我怀疑您看不到这一点的原因是因为以这种方式强制执行呼叫顺序很少实用。此外,如果您仍然有一系列方法,这实际上就是您正在做的事情:“B 需要的对象”只是this,如果this 不处于正确状态,则抛出异常.
    • @John 实际上这通常是不切实际的。虽然我不确定您的示例是否完全相同,因为您说的是运行时错误,而我说的是编译时错误。
    • @Rex M:是的,它被捕获的地方不同。但是,这是一个特定于语言的问题,而不是一般的设计问题(例如,在 Ruby 中,没有区别)。
    【解决方案3】:

    是的,是私密的,否则你为用户做错事敞开了大门,这只会给每个人带来痛苦。

    您是否需要单独调用这些方法中的任何一个?即他们中的任何一个人是否会做任何有用且可能需要单独使用的事情?如果是这样,那么您可能希望将这些公开,但即使您将它们全部公开,您也应该拥有以正确顺序(最好使用有用的名称)调用它们的方法,以使您的用户更轻松。

    【讨论】:

    • 请否决投票者解释原因?
    【解决方案4】:

    这完全取决于操作是否本质上是原子的。在这种情况下,对于我们外人来说,这看起来像是一个单一的操作,但真的是这样吗?如果LogIntoThirdPartyWebService 失败,UI 是否需要显示一个对话框来询问用户是否要重试?在您有单个操作的情况下,重试 LogIntoThirdPartyWebService 操作还需要重做可能代价高昂的操作,例如 RetrieveImages,同时将它们分开可以实现更精细的逻辑。

    在这种情况下我会做的是这样的:

    Images images = RetrieveImages();
    ImagesAndData data = RetrieveAssociatedData(images);
    WebService webservice = LogIntoThirdPartyWebService();
    UploadStuffToWebService(data, webservice);
    

    或者更理想的是这样的:

    UploadStuffToWebService(RetrieveImages().RetrieveAssociatedData(),
                            LogIntoThirdPartyWebService());
    

    现在您在执行正确的操作顺序时有了粒度。

    【讨论】:

    • +1 似乎他的班级可能在内部存储了大量数据,过程中的各种方法都将依赖这些数据。
    • 为什么投反对票?我提出了与 Rex M 相同的建议,没有人反对他的回答。
    【解决方案5】:

    在我看来,从您的对象的消费者的角度来看,对象只做一件事:它将图像从一个地方移动到另一个地方。作为对象的消费者,您需要采取的所有个人步骤都与我无关;毕竟这就是我让你为我做这件事的原因。

    所以你应该有一个单一的DoStuff() 方法,它接受所有必要的参数,并将所有实现细节设为私有。

    【讨论】:

      【解决方案6】:

      Private -- 并在构造函数中获取参数并在那里执行命令。

      不要假设调用者会或知道如何按顺序调用它们。

      所以,我会这样做,而不是您列出的示例:

      MyObject myObject = new MyObject(); // make a constructor to take any parameters that are required to "setup" the object per your requirements.
      myObject.UploadToWebService();
      

      【讨论】:

      • 仅仅因为某些事情需要以特定的顺序执行并不意味着它需要是私有的。例如,在Open 之前在Stream 上调用Close 是没有意义的,但这并不意味着我们应该将这些方法设为私有。
      • 这不是笼统地概括一切,而是基于他的描述。
      【解决方案7】:

      这实际上取决于您是否估计任何人都只想调用其中一种方法,以及它们是单独有意义还是可以独立实现。如果不是,那么最好避免暴露除高级操作之外的任何内容。

      【讨论】:

        【解决方案8】:

        尽可能少地暴露,尽可能多地暴露。如果总是调用FuncA(),然后调用FuncB(),则调用@987654323 @ 并让它调用另一个,或者让public FuncC() 按顺序调用它们。

        【讨论】:

          【解决方案9】:

          是的,它绝对应该是私有的,尤其是所有方法似乎都是无参数的,而您只关心顺序。

          我唯一会考虑显式调用每个方法的情况是,如果它们每个都采用多个不重叠的参数,并且您不希望将这么长的参数字符串传递给一个方法并希望模块化。然后你应该确保清楚地记录下来。但请记住,cmets 是不可执行的……您仍然需要对您的用户多一点信任。

          信息隐藏和 OOP 的最大因素之一……只给用户绝对必要的东西。尽可能减少混乱的空间。

          【讨论】:

          • 另一种情况可能是,如果每个步骤都花费了令人不安的长时间,那么将其拆分并在两者之间做一些事情可能是一件好事(如果多线程不可行)。跨度>
          【解决方案10】:

          公共或私有的问题完全取决于您希望为您的对象公开的合同。您希望对象的用户单独调用方法,还是希望他们调用单个“DoStuff”方法并完成它?

          全部取决于类的预期用途。

          在您给出的示例中,我认为 DoStuff 应该是公开的,其余的应该是私有的。

          【讨论】:

            【解决方案11】:

            您认为哪个对您班级的消费者更容易?

            绝对编写一个以正确顺序执行正确步骤的公共方法。否则,调用者不会做对;他们会忘记一步或跳过某些内容。

            【讨论】:

              【解决方案12】:

              两者都没有。我认为您至少有 3 个对象,否则您将违反单一责任主体。您需要一个“获取和保存图像”的对象,一个“处理图像”的对象,以及一个“管理外部供应商通信”的对象。

              【讨论】:

                【解决方案13】:

                它们公开的一个原因是,如果您希望用户能够在步骤之间插入逻辑。在这种情况下,您应该通过保持一个非常小的状态机来强制在内部以正确的顺序调用函数。如果状态机以错误的顺序转换,除了做错事(例如抛出异常)之外,您还有其他选择。

                但是,如果确实存在需要在步骤之间采取行动的情况,另一种设计允许它们全部保持私密。与其将方法公开,不如提供一个公共回调接口,让用户附加您在流程的每个步骤调用的处理程序。在您现在私有的 doItAll() 方法中,您可以执行如下细化的操作:
                <pre></pre>

                如果(preRetrieveHandlerExists){ preRetrieveHandler() } obj.RetrieveImages();

                如果(postRetrieveHandlerExists){ postRetrieveHandler() } //以此类推

                【讨论】:

                  【解决方案14】:

                  我的软件工程经验法则是始终让用户/消费者/调用者尽可能少地搞砸事情。因此,请保持方法私有以确保正常工作。

                  【讨论】:

                    【解决方案15】:

                    Fowler 使用术语“功能嫉妒”来描述一个对象调用另一个对象的少数方法(尤其是重复)的情况。

                    我不知道他是从哪里弄来的。你在文献中看不到太多,多年来很多人都不知道我在说什么(我不知道为什么,我觉得这个名字一听就很明显。这就是为什么我重复它)

                    【讨论】:

                      猜你喜欢
                      • 2013-09-20
                      • 2022-09-27
                      • 2011-08-01
                      • 2018-09-08
                      • 2012-12-06
                      • 1970-01-01
                      • 2016-10-10
                      • 1970-01-01
                      • 2019-04-19
                      相关资源
                      最近更新 更多