按照正确的 OO 设计使用另一个对象的功能 - 封装

发布于 2024-08-03 11:33:09 字数 445 浏览 3 评论 0 原文

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

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

实现逻辑后,就不再需要辅助方法(或辅助对象)。

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

在这种情况下,局部变量会更合适吗?在将使用其功能的方法中声明并实例化辅助对象?我的原始类中声明和实例化此类辅助对象的最佳位置在哪里?

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

I am debating the proper, OO-design to use another object's functionality (methods) from a java class, while both objects remain decoupled as much as possible.

For example, at some point in my class, to implement my logic, I need to call a method that belongs to another object, say a helper class. This helper class does not need to be in any way related to my original class, it just has a specific method which is visible to, and available for my class to use.

After the logic is implemented, the helper method (or the helper object) is not needed down the line.

Obviously, I would need a reference to this helper object in order to use its method. But to enforce encapsulation, I should not declare an instance variable in my original class to reference this helper object? Is that reasoning correct? Also, the helper class is not aware of any client class that might use it.

Would a local variable be more appropriate in this case? Declare and instantiate the helper object in the method which will make use of its functionality? Where is the best location in my original class to declare and instantiate such a helper object?

I was wondering if there is a high-level example, or if this is explained with a bit more elaboration in OO articles. I'd appreciate any encapsulation-focused input or hint on the above.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

掩于岁月 2024-08-10 11:33:09

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

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

相关注意事项是:

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

基本选择是:

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

高级选择:

  • 外部单例 - 对象管理它自己的生命周期,
    保证一个将可用
    你。对于某些事情来说工作正常,但是
    很可能过度使用。
  • 依赖注入 - 其他人(通常是框架管理的)
    通过配置文件)打破了你的
    封装并放入对象中
    你会需要的。

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

But to enforce encapsulation, I should not declare an instance variable in my original class to reference this helper object? Is that reasoning correct?

No, declaring an instance variable has nothing to do with breaking encapsulation.

The relevant considerations are:

  • dependency: by default you depend on the utility class you use, and it doesn't depend on you. Various techniques (e.g. interfaces, strategy patterns, dependency injection) can be used to reverse or reduce that dependency, if required. But in the simple case, depending on it is probably ok.
  • object lifetime: if it is an object, you need it to exist at the point you use it. It existing may mean something semantically (i.e. change the behaviour of other parts of your program), or may have performance implications (it is expensive to create, or ties up a lot of memory if left hanging around when not needed). So you need a way of handling it's lifetime that is compatible with both it's nature and your goals.

The basic choices are:

  • local unshared variable in one or
    more of your functions - it is
    created when needed, goes away as
    soon as the function exits. Probably the default choice, everything else is an optimisation or special case.
  • shared instance variable created in
    constructor
    - created only once, but
    last until your object itself gets
    garbage collected/destroyed.
  • shared instance variable created first time
    used
    - as above, but delaying
    creation at the cost of complexity.
  • external static function - no object, so no lifetime issues. Suitable for something with no internal state and simple interface, otherwise you end up having a implicit object lifetime managed only by the comments to the functions (as in C library functions like strcpy).

Advanced choices:

  • external singleton - object manages it's own lifetime,
    guaranteeing one will be available to
    you. Works ok for some things, but
    very possible to overuse.
  • dependency injection - someone else (typically a framework managed
    by configuration files) breaks your
    encapsulation and puts in the object
    you will need.

All the other ways of doing this (e.g. add the object to constructor or method arguments) add extra dependencies to the system and so shouldn't be done unless at least the basic choices above aren't suitable.

随波逐流 2024-08-10 11:33:09

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

  • 您不想将所需的逻辑放入 C 中。
  • 您已将其放入 H 中。
  • HM() 仅由 C 使用一次。
  • H 与客户端无关。
  • 因为您说“显然,我需要对此辅助对象的引用才能使用其方法”,所以我假设您只能使用 HM()< /code> 是一个实例方法。

有几个解决方案:

  • 评估 M 作为静态方法是否会更好。如果我',这对于静态方法来说是一个非常引人注目的用途。我见过一个。您没有提到任何有关 H 维护状态的内容。

  • 使用策略模式。如果 HM() 表示执行某事的特定方式,则 H 是该模式中的策略对象对于C。如果还有其他类似 H 的类具有类似的 M() 方法,那么您可以从中选择不同的策略。

The right answer depends on the nature of the relationship between the helper class (H) and the method you use on it (M), and the original object class (C). You mentioned a few key points:

  • You don't want to put the logic needed into C.
  • You have placed it into H instead.
  • H.M() is used only once by C.
  • H is client-agnostic.
  • Because you say "obviously, I would need a reference to this helper object in order to use its method", I assume that you can only work with instances of H and that M() is an instance method.

There are a couple of solutions:

  • Assess whether M wouldn't be better as a static method. This is a very compelling use for a static method if I've ever seen one. You don't mention anything about H maintaining state.

  • Use the Strategy pattern. If H.M() represents a specific way of doing something, then H is the Strategy object in the pattern for C. If there are other H-like classes with similar M() methods, these are the different strategies you can pick from.

蓝礼 2024-08-10 11:33:09

那么是这样的场景吗?这些是问题吗?

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;
}

这可以使测试变得非常容易,您的测试可以注入一个模拟助手类。

或者您可以使用惰性初始化器。如果您的助手是方法中的局部变量,那么这非常方便。同样,工厂可以根据您的喜好注入或静态。

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

    return aHelper     
}

So is this the scenario? Are these the questions?

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. I think you should declare aHelper as an Interface

 HelperInterface aHelper;

Then we are not coupled to any specific implementation.

Q2. If you only use it in one place declare it in that function, otherwise as a member variable. You don't increase coupling by doing either.

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

Q3. Initialise either in a constructor or with a lazy getter using a factory.

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

This can make testing very easy, your tests can inject a Mocked Helper class.

Or you can use a lazy intialiser. This is quite handy if your helper is a local variable in your method(s). Again the factory can be injected or be static depending upon your preference.

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

    return aHelper     
}
野心澎湃 2024-08-10 11:33:09

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

也就是说,我会将助手作为参数发送给方法,或者更确切地说是助手接口。这样你的类就完全不知道如何创建助手,甚至不知道是什么类型。无知是福。班级只会知道如何使用它,美丽的东西。 :)

Static methods are hard to test, or rather static methods beeing called in another method is hard to mock. I find it easy to do good design by thinking in terms of testing. If the method is a non-static member, you can easily unit-test code calling that method without also testing that method. Are you with me? Let's say a method m uses the network to do stuff. If that method is static, every time you test code that uses the method m, it will do stuff at the network. And if the network stuff fails, your test fails, but not the code you want to test. You see? If it is not static, you can mock the object with the method, and make it always return "OK".

That beeing said, I would send the helper as a parameter to the method, or rather a helper-interface. That way your class is totally oblivious of how to create a helper, or even what type. Ignorance is bliss. The class will only know how to use it, beautiful stuff. :)

指尖凝香 2024-08-10 11:33:09

我正在讨论正确的面向对象设计
使用另一个对象的功能
(方法)来自 java 类,而
两个对象都保持解耦
尽可能。

你在这方面投入了太多的精力。解耦并不意味着完全没有联系。如果您打算使用辅助对象一次,只需将其作为参数传递给使用它的块的代码,或者让您的类从工厂获取它的实例。

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

但不要太过分了。我们的目标是找到一个解决方案,而不是玩解耦游戏,让事物适应人为的、冗余的类层次结构。后者是 OO 概念最糟糕的用法。

I am debating the proper, OO-design to
use another object's functionality
(methods) from a java class, while
both objects remain decoupled as much
as possible.

You are putting too much effort in this. Decoupling does not mean not having a connection at all. If you are going to use the helper object once, just pass it as a parameter to the code of block that uses it, or have your class get an instance of it from a factory.

But at some point you have to have a reference to it. Obviously you do not want to have an member instance referencing it (potential memory leak). So that would call some sort of factory or instance manager that gets you a reference to a helper.

But don't go overboard with this. The goal is to get a solution, not to play decoupling games and getting things fit into artificial, redundant class hierarchies. The later is the worst usage of OO concepts.

春花秋月 2024-08-10 11:33:09

为什么不选择静态方法,或者使用单例辅助对象?

Why don't you go for a static method, or have a singleton helper object?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文