在正确的OO设计之后使用另一个对象的function – 封装

我正在讨论正确的OO设计,以便从java类中使用另一个对象的function(方法),而两个对象尽可能保持解耦。

例如,在我的类中的某个时刻,为了实现我的逻辑,我需要调用一个属于另一个对象的方法,比如一个帮助类。 这个帮助器类不需要以任何方式与我的原始类相关,它只是有一个特定的方法,可供我的类使用。

在实现逻辑之后,不需要帮助方法(或辅助对象)。

显然,我需要引用这个辅助对象才能使用它的方法。 但是为了强制封装,我不应该在我的原始类中声明一个实例变量来引用这个帮助器对象? 这种推理是否正确? 此外,帮助程序类不知道可能使用它的任何客户端类。

在这种情况下,局部变量是否更合适? 在方法中声明并实例化helper对象,它将使用它的function? 原始类中最好的位置在哪里声明并实例化这样的辅助对象?

我想知道是否有一个高级示例,或者是否在OO文章中对此进行了详细阐述。 我很欣赏任何以封装为中心的输入或提示。

情节就是这样吗? 这些是问题吗?

Class MyClass { private SomeState myState; public voic oneMethod() { // Q1 : what type is **ahelper** ? // Q2 : where do we declare it ? // Q3 : where do we initialise it? aHelper.doSomeWork(); // update your state depending upon the results } } 

Q1。 我认为你应该将aHelper声明为一个接口

  HelperInterface aHelper; 

然后我们没有加入任何具体的实现。

Q2。 如果您只在一个地方使用它,请在该函数中声明它,否则作为成员变量。 你不要通过这两者来增加耦合。

  HelperInterface aHelper = ? what here? aHelper.soSomeWork(); 

Q3。 使用工厂在构造函数或惰性getter中初始化。

 public MyClass(HelperInterface injectedHelper) { aHelper = injectedHelper; } 

这可以使测试非常简单,您的测试可以注入Mocked Helper类。

或者你可以使用懒惰的intialiser。 如果您的助手是您方法中的局部变量,这非常方便。 工厂可以根据您的喜好再次注入或静电。

 private getHelper() { if (aHelper == null ){ make the helper, perhaps using a factory } return aHelper } 

但是为了强制封装,我不应该在我的原始类中声明一个实例变量来引用这个帮助器对象? 这种推理是否正确?

不,声明实例变量与破坏封装无关。

相关的考虑是:

  • 依赖 :默认情况下,您依赖于您使用的实用程序类,它不依赖于您。 如果需要,可以使用各种技术(例如,接口,策略模式,dependency injection)来反转或减少该依赖性。 但在简单的情况下,取决于它可能没问题。
  • 对象生存期 :如果它是一个对象,则需要它存在于您使用它的位置。 它的存在可能意味着语义上的某些东西(即改变程序其他部分的行为),或者可能具有性能影响(创建起来很昂贵,或者如果在不需要的时候闲逛,会占用大量内存)。 所以你需要一种处理它的生命周期的方法,它既符合它的本质又符合你的目标。

基本选择是:

  • 一个或多个函数中的本地非共享变量 – 它在需要时创建,一旦函数退出就会消失。 可能是默认选择,其他一切都是优化或特殊情况。
  • 在构造函数中创建的共享实例变量 – 仅创建一次,但最后直到您的对象本身被垃圾收集/销毁。
  • 第一次使用创建的共享实例变量 – 如上所述,但是以复杂性为代价延迟创建。
  • 外部静态函数 – 没有对象,所以没有生命周期问题。 适用于没有内部状态和简单接口的东西,否则最终只能通过对函数的注释来管理隐式对象生存期(如在C库函数中,如strcpy )。

高级选择:

  • 外部单例 – 对象管理它自己的生命周期,保证您可以使用它。 适用于某些事情,但很可能过度使用。
  • dependency injection – 其他人(通常是由配置文件管理的框架)会破坏您的封装并放入您需要的对象。

执行此操作的所有其他方法(例如,将对象添加到构造函数或方法参数)会向系统添加额外的依赖关系,因此除非至少上述基本选择不适合,否则不应该这样做。

正确的答案取决于辅助类(H)与您在其上使用的方法(M)和原始对象类(C)之间关系的性质。 你提到了一些要点:

  • 您不希望将所需的逻辑放入C
  • 你把它放到了H
  • HM()仅由C使用一次。
  • H与客户无关。
  • 因为你说“显然,我需要一个对这个辅助对象的引用才能使用它的方法”,我假设你只能使用H实例,而M()是一个实例方法。

有几种解决方案:

  • 评估M作为静态方法是否更好。 如果我见过一个静态方法,这是非常引人注目的用法。 你没有提到关于H维持状态的任何事情。

  • 使用策略模式。 如果HM()表示某种特定的做事方式,则HC模式中的Strategy对象。 如果还有其他具有类似M()方法的H类,那么这些是您可以选择的不同策略。

静态方法很难测试,或者说在另一种方法中调用的静态方法很难模拟。 通过考虑测试,我发现很容易做好设计。 如果该方法是非静态成员,则可以轻松地对调用该方法的代码进行unit testing,而无需测试该方法。 你跟我在一起吗? 假设一个方法m使用网络来做东西。 如果该方法是静态的,那么每次测试使用方法m的代码时,它都会在网络上执行操作。 如果网络内容失败,则测试失败,但不是您要测试的代码。 你看? 如果它不是静态的,您可以使用该方法模拟对象,并使其始终返回“OK”。

那个说法,我会将帮助器作为参数发送给方法,或者更确切地说是辅助接口。 这样你的class级完全忘记了如何创建一个助手,甚至是什么类型。 无知是幸福。 该课程只会知道如何使用它,美丽的东西。 🙂

我正在讨论正确的OO设计,以便从java类中使用另一个对象的function(方法),而两个对象尽可能保持解耦。

你在这方面付出了太多的努力。 解耦并不意味着根本没有连接。 如果您要使用辅助对象一次,只需将其作为参数传递给使用它的块的代码,或让您的类从工厂获取它的实例。

但在某些时候你必须有一个参考。 显然你不想让一个成员实例引用它(潜在的内存泄漏)。 因此,可以调用某种工厂或实例管理器来获取对助手的引用。

但是不要过分夸大。 目标是获得一个解决方案,而不是发挥解耦游戏并使事物适合人为的冗余类层次结构。 后者是OO概念的最坏用法。

为什么不选择静态方法 ,还是拥有单例辅助对象?