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?

  • 2
    Possible duplicate of Avoid getters and setters, displaying user informations – Andy Mar 11 '18 at 10:17
  • 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 '18 at 10:18
  • @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". – Robert Bräutigam Mar 11 '18 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 '18 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. – Robert Bräutigam Mar 11 '18 at 21:55
4

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.

  • Actually some people believe it is all about the dots! – Frank Hileman Mar 12 '18 at 4:12
  • Well, those people are wrong :) – Filip Milovanović Mar 12 '18 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.

-2

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.

  • 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...) – Filip Milovanović Mar 11 '18 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. – Filip Milovanović Mar 11 '18 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). – Robert Bräutigam Mar 12 '18 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). – Filip Milovanović Mar 12 '18 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)? – Filip Milovanović Mar 12 '18 at 8:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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