4

I want to display the product, and the product card has a lot of information about the product and the owner. How to deal with Law of Demeter in this product - owner relationship?

In controller I currently have:

Product product = productRepository.get(1);

And in template:

product.name
product.description
product.owner.name
product.owner.address

But the last two calls break the Law of Demeter.

So I should be in class Product add getters:

getOwnerName()
getOwnerAddress()

And in template:

product.name
product.description
product.ownerName
product.ownerAddress

But if I have, for example, 10 information about the author, I have to do as many as 10 additional getters, which only duplicate the code.

Maybe better way would be in controller:

Product product = productRepository.get(1);
Owner owner = ownerRepository.get(product.ownerId);

And in template:

product.name
product.description
owner.name
owner.address

But will then such a structure be coherent for another programmer?

How should this be done in professional applications?

Christophe
  • 81,699
vovon
  • 57
  • 3

6 Answers6

5

The motivation behind Law of Demeter (principle of least knowledge) is to avoid the coupling to the internal implementation details of a software component/subsystem, not to limit the number of dots in your statement.

So, it's time to think about the architecture (in the sense of the high level design) of your application. If both Product and Owner are a part of the "surface area", the interface (in a broad sense) to a subsystem - then writing product.owner.name is fine. (At least, until something forces you to redesign the relationship between the product and the owner - but, if that possibility is even worth considering is something you'll have to decide for yourself, so I'll ignore that for the purposes of this answer.)

If the second level member access operation actually obtains something that's really ought to be an implementation detail of the component/subsystem (something that you may want to reorganize, replace with a different implementation, etc.), then you have a violation of LoD. Especially if the call chain extends further, where you obtain objects that are implementation details of implementation details.

3

The law of Demeter is not a law - it is a guideline. Use some common sense.

If a property is logically a property of the product, make it a property, even if it is internally implemented differently.

But the product’s owner is an object in its own right that a user of the product Calais is likely interested in directly.

In this case, publish the owner object. In other cases, use good judgement.

gnasher729
  • 49,096
0

While the Law of Demeter is not an immutable law, and it is fine to break it in some situations, what you are doing in particular does seem like a code smell to me.

Why not nest a template for displaying an owner inside the template for displaying the product? Instead of the product rendering both 'product.owner.name' and 'product.owner.address' it would render the owner template passing 'product.owner' as the argument. Then inside the owner template you just render 'owner.name' and 'owner.address'.

This owner template can then possibly also be reused in other places, where owner information needs to be displayed, and then if fields are added to owners you only need to change this in one place instead of modifying the template for everything that has an owner, thereby being much more compliant with principles such as the Single Responsibility Principle.

Ultimately I would suggest that all your views/templates be nested in the same way that your data objects are. Each view/template should correspond to only a single object, and if that object contains other objects with their own fields then you would have a nested view/template to render that object.

0

I think LOD is just a fancy term of saying - do not expose internal/implementation details(i.e make them public). If the calling object/method wants to know/do something on the internal/implementation stuff, have a method for that, don't just expose the entire object, that would tightly couple the calling object/method and the internal/implementation stuff.

To answer your question, like many other answers here, accessing product.owner.name sounds just fine, owner is part of the public interface, at the same level as product. Having to provide boilerplate wrapper methods or getters does not sound feasible.

0

The Law of Demeter focuses on man-in-the-middle code that is written for that express purpose, i.e.:

getOwnerName()
getOwnerAddress()

The purpose of this code is for the consumer (layer 0) to call the product class (layer 1) to access the owner data (layer 2). It's creating a bridge from layer 0 to layer 2, and that is the issue.

From wikipedia:

The fundamental notion is that a given object should assume as little as possible about the structure or properties of anything else (including its subcomponents), in accordance with the principle of "information hiding".

When you write a method such as Product.getOwnerName() you are making these forbidden assumptions, because Product is now assuming/requiring Owner to have a Name property. Therefore, if you want to change the Owner class structure, you're stuck also having to change the Product class accordingly, and that's bad practice.

Essentially, by not assuming anything about its subcomponents, the product should basically have a method/property which says "here's my related owner object. I don't care what its internal structure is". In other words, you simply return the owner object itself, not its internal contents directly.

You're worried about the template accessing the owner (which is 2 layers removed), but you're trying to fix it by creating Product.getOwnerName() which connects the product with the owner's name, which is also 2 layers removed. You're actually violating LOD in trying to not violate it in another place.


The Law of Demeter does not care about chained method/property calls when each call only drills one level deep, i.e.:

product.owner.name
product.owner.address

The product returns the owner, and the owner returns the name/address. Each action is a single level.


The correct way to create your template is

product.name
product.description
product.owner.name
product.owner.address

There is nothing inherently wrong with having the owner object as a separately fetched object (as per your suggested solution), but you're not required to do this (for LOD). Neither version violates LOD, you don't need to avoid violations here.

Flater
  • 58,824
-3

UI code is not exempt from the Law of Demeter, you are right to seek a better solution.

One possible object-oriented, straight forward, "LoD compliant" way to build the UI is to let the objects present themselves. This is a pretty simple solution, but is unfortunately a taboo topic for dogmatic reasons.

The benefits are obvious:

  • No violation of encapsulation
  • No violation of LoD
  • No weakening of cohesion
  • Actual decoupling of UI code from Business Objects

The last point may need some clarification. In the traditional getter/setter based UI code, all of the UI code usually knows about the business objects, what "properties" they have, how to get them from the user, how to set them back to the business objects, etc. This leads to a tight coupling. Just ask yourself, what if the properties change in the business object? If you need to modify UI code too, that is a sign of pretty obvious tight coupling.

If your object presents itself however, the UI code (Panels, Tables, Pages, etc.) can stay completely and truly free of knowledge of the business.

The business objects will know a little bit more about the UI as in the "traditional" layered way, but they will only know abstract things. Such as there are Tables, Panels, etc. They should definitely not know details about those, like which colors are used, what HTML tags and such.