M-R
M-R

Reputation: 431

Referring to Object of Object of Object

Assuming we have an object inside an object, inside another object, what is the best way to retrieve the value of a private variable outside the two objects?

The simplest way seems to be to do something like this:

object1.object2.object3.getvalue();

Is this acceptable? Or would it be better to call a method which calls a method, which calls a method?

The second option seems unnecessarily laborious, considering you would basically be having the same method created in 3 different classes.

Upvotes: 0

Views: 204

Answers (5)

Adrian Shum
Adrian Shum

Reputation: 40056

It depends on your definition of "acceptable". It may be acceptable in your case. It is hard to tell without proper context.

However, there are something you may consider, level-by-level:

1. Use of getters

Although such kind of getters are still far from satisfactory, it is still better than using direct property access

i.e. Instead of accessing object1.object2 by direct field access, provide Object2 getObject2() in Object1, so that the code looks like:

object1.getObject2().getObject3().getValue()

2. Null handling

Usually when we chained such kind of property navigation, we will have problem that in some level, null is returned, which makes object1.getObject2().getObject3().getValue() throwing NPE.

If you are using Java 8, consider returning Optional<>. e.g. in Object1, getter of object2 should look like Optional<Object2> getObject2()

With such change, your code can be made null-safe by something like:

Value value = object1.getObject2()
                     .flatMap(Object2::getObject3)
                     .map(Object3::getValue)
                     .orElse(Value.emptyValue())

3. Law of Demeter

In order to make a more loosely-coupled design, you may want to provide access to that value in API of Object1, instead of exposing multiple levels of indirection. Hence:

Value value = object1.getFooValue();

(Keep using Optional<> if it fit your need)

for which internally it retrieve the value from Object3. (Of course, Object2 may also want to do something similar)

4. Getter is evil

Always remember you should try to avoid providing internal representation of your object. Your objects should provide meaningful behavior instead of simply act as a value object for you to get or set data. It is hard to give an example here but ask yourself, why do you need to get the value for? Is that action more appropriate to be provided by your object itself?

Upvotes: 2

Basil Bourque
Basil Bourque

Reputation: 339561

The best way is to not think of your objects as data stores. A class should be defined to have some work to do, some cluster of related responsibilities. In order to perform that work to fulfill those responsibilities some internal data may be kept, and some nested objects contained. Serving out data should not be the goal of your objects, generally speaking.

Encapsulation

The whole idea of encapsulation in object-oriented programming is to not expose that internal data and nested objects. Instead publish the various available chores by declaring methods on your higher/outer object. Encapsulation frees you to change those internals without breaking the outside calling code – avoiding fragility is the goal.

For example, an Invoice object can contain a collection of LineItem objects. In turn each LineItem object contains other objects for product, quantity, price, extended cost, taxability, tax rate, tax amount, and line cost. If you want to know the total amount of sales tax added across the items, instead of asking the Invoice for the LineItem, and then asking the LineItem for TaxAmount object, define this chore as a method on Invoice, getTotalTaxAmount. Let that method figure out (and keep to itself!) how to go through the contained objects to collect the relevant information.

If you absolutely must expose that nested data, again define a method at the highest level that returns a copy of the desired data or a collection of the desired objects (probably copies of those objects). Again, the goal is to avoid exposing the objects within objects within objects.

Then, within that highest method, as the correct Answer by Raaga stated, define a getter that calls a getter.

Getter Methods versus Direct Member Access

In a very simple structure of data you could access the objects directly. But generally better to use getter methods. Again the reason is encapsulation. Having a getter method allows you the flexibility of redefining the implementation details of the stored data.

For example, presently you could store the "Sex" variable as a String with values of "F" or "M". But later you may decide to take advantage of Java's nifty enum feature. So you replace those single-character "F" & "M" strings with enum instances Sex.FEMALE and Sex.MALE. Having a getter provides a level of insulation, so the Strings can be replaced internally with enums. The getter method continues to return a String (and internally translating the enum to an "F" or "M" String to be returned). This way you can work on restructuring your class without breaking those dependent outside objects.

Upvotes: 1

Java Doe
Java Doe

Reputation: 346

What you described may be the simplest way (if object2 and object3 are accessible) but it is definitely not the way to go. As Raaga pointed out getters are a lot better to retrieve members of a class and these members should then be private or protected to prevent errors.

If you can do

object1.object2.object3.getvalue();

you can also do something like

object1.object2 = null;

which is most likely not what you want to allow. This is one of the basic concepts of object oriented programming. Classes should handle their implementation details / secrets and not directly offer them to the outside! This is what getters/setters are for.

This way you have more control over the access and what can be done and what can't. If you should only be able to retrieve object2 from object1 but not be able to change it, you can only offer a getter and no setter.

If you should also be able to change it, it is also better to use setter for more control, because you can do checking in your setter to prevent my example where I put a null pointer as your object2

And just in case you worry about efficiency that calling a method might not be as efficient as directly accessing a member, you can rely on Java to internally optimize your method call that it is not any slower than the direct access.

Upvotes: 0

Techphile
Techphile

Reputation: 83

object1.object2.object3.getvalue();

This chaining seems incorrect...Object chaining under such scenario is always object1.someMethod().someOtherMethod(). Or something like suggested above in an answer using getter object1.getObject2().getObject3(). I hope it helps.

Upvotes: 0

Gaurav Mahajan
Gaurav Mahajan

Reputation: 119

use getter to get any object

ex: Object obj = object1.getObject2().getObject3();

Upvotes: 2

Related Questions