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?

5
  • 3
    Possible duplicate of Avoid getters and setters, displaying user informations
    – Andy
    Mar 11, 2018 at 10:17
  • 1
    I could post another answer here, however, your question is pretty much the same as the original I linked, where I already answered the misconception about avoiding getters in general.
    – Andy
    Mar 11, 2018 at 10:18
  • 1
    @DavidPacker Just because it is the "UI", does not mean you can ignore the principles of object-orientation. It is a normal part of an application, part of the requirements even. It is not exempt, and there are better solutions then just publish all data from the "business". Mar 11, 2018 at 19:16
  • @RobertBräutigam, if you read my answer till the end, one of my suggestions are View Models (which is exactly what you suggested in your answer by the self-presentable objects). However, it's just circumventing the made up problem, which did not exist in the first place, by splitting the access into multiple files, hence feeling like you solved the getters problem while merely moving it to a different files. Once again, using getters in the presentation layer is not a problem. If the hierarchy is too complicated, use drawable components, otherwise just grab for the nested property, it's fine.
    – Andy
    Mar 11, 2018 at 19:31
  • @DavidPacker It always comes back to this: How many classes do you have to modify if I were to add/delete/modify a "property" in any of the "business" classes? Just take a current project (with UI, WS endpoints, everything!), and really count it! In my projects, it usually takes 1, the class itself, and that's it. If it's fine to grab properties, especially nested ones, it is unlikely that you get any modification localized to 1 class. Mar 11, 2018 at 21:55

6 Answers 6

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.

2
  • Actually some people believe it is all about the dots! Mar 12, 2018 at 4:12
  • Well, those people are wrong :) Mar 12, 2018 at 8:05
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.

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.

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

5
  • 1
    I disagree we are dealing here with a taboo or a dogma - we can discuss this. The UI code is supposed to depend on the business objects, it's supposed to change if the business objects change. That's the desired direction of coupling. What we want to avoid is having to change business objects when the details of the presentation change. Now, if you let business objects draw themselves, then you are coupled to the UI, and you are violating the Single Responsibility Principle - which again leads to coupling between the code that handles the two responsibilities within the objects; (continues...) Mar 11, 2018 at 22:41
  • (...continued) unless you mitigate that somehow. E.g., by relying on an abstraction of the UI framework (say, via a required interface, enabling you to draw the objects with different UI FWs, maybe on different platforms). Another way to do it is to have the business layer return a representation of the data to the UI. This is just a data structure, not an object in the OO sense, but it is a (data) abstraction that keeps the layers decoupled. And it helps if your representation doesn't map 1-to-1 to your business objects. It does involve copying and some boilerplate, though. Mar 11, 2018 at 22:44
  • Things that change together should be together. That is what maintainability is all about, and that is also what SRP tries to say. If you say the UI changes if the business objects change then obviously you have things that change together but aren't together. You don't have to put details of the UI into the objects make the objects present themselves, only the parts that change together with the object (parts that rely on the data in the object). Mar 12, 2018 at 7:22
  • "then obviously you have things that change together" - no you don't; because although the business objects (or rather, requirements) can influence the UI, they can still change independently in certain ways. "You don't have to put details of the UI into the objects" - I agree with that; I'm not saying that the details of the UI should depend on the domain (you wan't to be able to fiddle with those, so that should be decoupled from the domain), but the UI-related component overall should depend on the business objects (directly or indirectly). Mar 12, 2018 at 8:03
  • Now, sure, in certain domains it may make sense for the business objects to know how to present themselves, I'm not fundamentally against that. But it's not (IMO) a universal solution - e.g., how would you go about adding a different view of the same data (say, you show it in a table, but you want to add graph of some sort), without changing the code of the business object(s) (assuming the form of the data is already suitable for this purpose)? Mar 12, 2018 at 8:03

Not the answer you're looking for? Browse other questions tagged or ask your own question.