在正确的OO设计之后使用另一个对象的功能 - 封装

时间:2009-08-29 11:03:22

标签: java oop encapsulation

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

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

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

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

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

我想知道是否有一个高级示例,或者是否在OO文章中对此进行了详细说明。我非常感谢任何以封装为中心的输入或提示。

6 个答案:

答案 0 :(得分:2)

  

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

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

相关的考虑因素是:

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

基本选择是:

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

高级选择:

  • 外部单身 - 对象管理自己的生命周期, 保证可以使用 您。适用于某些事情,但是 很可能过度使用。
  • 依赖注入 - 其他人(通常是管理的框架) 通过配置文件)打破你的 封装并放入对象 你需要。

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

答案 1 :(得分:1)

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

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

有几种解决方案:

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

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

这是情景吗?这些是问题吗?

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     
}

答案 4 :(得分:0)

  

我正在辩论正确的OO设计   使用另一个对象的功能   (方法)来自java类,而   两个对象保持分离   尽可能。

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

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

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

答案 5 :(得分:-1)

为什么不选择static method或拥有singleton辅助对象?