views:

111

answers:

6

Hello,

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.

A: 

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

Zed
you mean create a static method in my business logic class?
denchr
Yep. it all depends on whether the business logic class needs to maintain state or not. If not, make the method static. If there is only a global state, go for a singleton. If the state needs to be stored for each individual user, you need to maintain references in the users.
Zed
Yes, the business logic class, will do some calculations using methods from the helper objec, and will change its internall state. Possibly this means that the logic class alters one of its instance variables after using those helper methods.
denchr
+1  A: 

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.

John Feminella
The helper class does not need to maintain state. It can be used by other business logic classes, but it will help change the internal state of the client which is using it.I hope this makes my question a bit clearer. I see what you mean by using the strategy pattern. I will look into that.The ibjective is to also avoid having a cyclical reference between H and C. I mean, there wouldn't be a point in having H reference C and C reference H right?
denchr
Just a quick additional question, the strategy pattern would require C to have H as an instance element right? H could also be an interface of course. Thanks in advance for your clarification.
denchr
A: 

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. :)

crunchdog
+2  A: 

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.

soru
Thanks for the comprehensive list of choices and their explanations!
denchr
A: 

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     
}
djna
Yes, exactly, this is pretty much the pseudocode scenario, with all my questions. Is there a particular advantage to initialize aHelper inside the constructor? Even if I will be using aHelper.doSomeWork(); locally within my business logic method?I was under the impression that the constructor should not be doing a lot of the work... not sure when this applies though . could be a whole different conversation. But in case I only use aHelper locally, I guess I could declare, *and* initialize locally?Many thanks again.
denchr
I don't do a "new RealHelper()" down in the logic method, because we don't want to couple to a specific implementation of the HelperInterface. Simply accepting an injected helper and saving the reference in the Constructor is doing very little work. Constructor injection like this is a common technique, quite legitimate I think. Completely localising the reference to the business method does result in that method needing to know how to "make" a helper class instance, that tends to increase coupling.
djna
A: 

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.

luis.espinal