14

In essential skills for the agile developer, in the needs vs capabilities interface, chap, 12, I'm trying to understand the main solution proposed to the challenge of applying the LAW OF DEMETER that the author mention by the end of this chapter.

To make the story short.

We start with the following study case:

public class City {
  public string name{};
  public City twinCity{};
  public Street[] streets{};
}
public class Street {
  public string name{};
  public House[] houses{};
}
public class House {
  public int number{};
  public Color color{};
}

Model of a City Grid

Where the author state that:

Models like this encourage us to expose rather than to encapsulate. If your code has a reference to a particular City instance, say one that maps Seattle, and you wanted the color of the house at 1374 Main Street, then you might do something like the following:

public Foo() {
  Color c = Seattle.streets()["Main"].
    houses()[1374].
    color();
}

The problem, if this is done as a general practice, is that the system develops dependencies everywhere, and a change to any part of this model can have effects up and down the chain of these dependencies. That’s where the Law of Demeter, which states2 “Don’t talk to strangers,” comes in. This is formalized in object systems as the Law of Demeter for Functions/Methods. A method M of an object O may only invoke the methods of the following kinds of objects:

  1. O’s
  2. M’s parameters
  3. Any objects instantiated within M
  4. O’s direct component objects
  5. Any global variables accessible by O

and suggest that when applying the LAW of DEMTER we should aim for something like

public Foo() {
   Color c = Seattle.ColorOfHouseInStreet("Main",1374);
}

and quickly warns from the following:

Although this would seem to be a wise policy initially, it can quickly get out of hand as the interface of any given entity can be expected to provide literally anything it relates to. These interfaces tend to bloat over time, and in fact there would seem to be almost no end to the number of public methods a given glass may eventually support.

Then after a quick detour at explaining the the problem of coupling and dependency, where he mentions the importance of separating a client and its service by the interface of the service, and possibly furthermore by separating the client "needs interface" from the "service capability interface" via the use of an adapter as something ideal but not necessarily practical;

Ideal decoupling

he suggests that to remedy to the problem, we could combine the LAW of DEMETER and the needs/capability separation, using a facade pattern as explain below:

From the original dependency

Violation of the law of demeter

In applying the law of demeter and the needs/capability interface separation we should initially get:

Too complex

But given that it is just impractical especially from the point of view of mocking, we could have something simpler with a facade as follow:

LoD needs/Capability separation Mock

The problem is that i just don't see how that exactly solve the problem of not violating the Law of Demeter. I think it maintains the law between the original client and the original service. But it just moved the violation within the FACADE.

MaatDeamon
  • 9,532
  • 9
  • 60
  • 127

3 Answers3

3

I'd say, from your descriptions, he has merely introduced a black-box component around the Cirty/Street/House functionality in the form of the CityMapFacade. Further, that component adheres to a public facing interface in the form of an interface, IClientNeeds. This kind of black-box encapsulation protects, and makes testable, the larger application because the complexities of the City/Street/House relationship are not exposed. It seems what was once old is new again :) This kind of black-box component-style methodology (for lack of a better term) has been around for a long time. It focuses on creating reusable, encapsulated components of functionality with a well defined interface. The client doesn't care about the implementation as long as the public-facing contract is fulfilled. The component is replaceable, upgradeable, mockable. Within the component, the complexity is also diminished because it is only responsible for an isolated subset of the application. It would typically be fully unit testable to ensure it correctly implements its public contract / interface. In my opinion, the author has brushed a deeper discussion of this all under the rug so as to simplify his explanation.

tcarvin
  • 10,715
  • 3
  • 31
  • 52
  • I fully get that thanks. He properly applied the facade pattern yes, although what about the law of demeter here. Shall we consider that when we do a black-box via a facade it does not matter? – MaatDeamon Jul 16 '14 at 12:58
  • 2
    It is a matter of perspective. He is applying the law of demeter to the application / system by boxing off the CityMap functionality, yes. Now, does the implementation of the CityMap component also follow the law of demeter? It could (as he discussed), but from the larger system's point of view it doesn't matter. Speaking pragmatically, the sausage has to get made somewhere, and sometimes it is messy. I think the author purposely did not go into that because it didn't fit his topic. – tcarvin Jul 16 '14 at 13:08
  • To say it another way, yes he probably recognized it needed to be broken to create a *practical* implementation of the City / Street / House relationship and was showing how to handle that kind of compromise. – tcarvin Jul 16 '14 at 13:11
  • Alright then!! By the way i have re-edit for clarification. So would that be your way to go, if you find yourself in that situation: Where you might have different clients that as a whole, require too many things from your service, leading you to either: provide too many functionality or breaking the encapsulation so those clients can do what they want with what your service own ? would you go for a facade? – MaatDeamon Jul 16 '14 at 14:16
  • Because in a sense i see that for the service it would break the single responsibility principle, if you do go for providing all the functionalities. By having specific facade for each new clients, you maintain the SRP both in the facade and the service and the needs interface. also you ensure your decoupling. So i see the importance. If you go for the facade, you agree that it is one of those case where you should comprise then. If you have another solution i would happy to hear it. – MaatDeamon Jul 16 '14 at 14:17
  • By the way i red in the clean code book which is a bit older (so i don't know if the author took it into account), that one should make the difference between data structure and object. Data structure can open up their fields with get, because they are meant to be red. But object who provide functionality/behavior should not break the law of demeter. So maybe the example is a bit ill-conceived as well. – – MaatDeamon Jul 16 '14 at 14:17
  • If i take into account the comment in clean code, then i would say yes let's go for a face to hide the sub-system complexity from the client, and provide copies with a getter to not break the encapsulation. Although the copy would be a huge copy to do.... – MaatDeamon Jul 16 '14 at 14:27
  • Wow, you added a bunch of content to the question. I like that the author seems to be walking the reader through the dogmatic to the pragmatic. At the end of the day, blanket statements about the right way to do something are not good. Ina non-academic situation, one would have to evaluate the likelihood of additional clients, additional functionality, complexity of the interfaces, etc to say which approach works best. Judge each on a case-by-case basis, and continually refactor mercilessly afterwards, because what you choose now might not the right choice later. – tcarvin Jul 16 '14 at 14:30
  • One last word on my last comment on making copy or more precisely defensible copy. I think it can be avoided if one code in language that have functional flavor (I use scala most of the time). Indeed if one follow the best practice of using immutable variable and structure, well the either the variable is protected from change or the defensible copy is done automatically for you on immutable structure. But again that is relevant only if you consider that the LoD is only about object and not data structure. – MaatDeamon Jul 16 '14 at 22:01
  • 1
    I like the sausage comment. As with all design principles, there are often times when you would actually want to violate them. And in the end, you can't really avoid violating them somewhere (e.g. Factory Method pattern). – CamperWill Jul 24 '14 at 17:31
  • The "Needs" interfaces are a good example of inversion of control. Depend on the abstraction, not the concrete implementation. You can define your "needs" very specifically for your client and avoid violating LoD at the client. The implementation of the needs could very well violate LoD. But now you are limiting that violation to one or two places. – CamperWill Jul 24 '14 at 17:34
3

I guess there's a "mis-communication" between you and the author.

Step 1: you have this:

needs interfaces

I hope you see that there is no LoD violation at this point: each concrete class depends on an abstraction of its needs and thus makes calls to that limited interface only; plus the dependency on the entity (e.g. City for CityCapabilityAdapter), which is allowed by LoD. So, no violations so far.


Step 2: then you have this:

needs facade

I think the author here means that the CityMapFacade depends on all 3 entities conceptually, but physically does not work with them directly. Instead, it works with them via the means of the LoD instruments. e.g. facade could depend on those adapters already introduced before. So, again, no LoD violation here.

To further clarify this interpretation, pay attention to the abstract things on the diagram, they are italicized (UML 2.0). Thus, the CityMapFacade is an interface which is responsible for solving its own LoD problems. And those can be solved by the means of the already demonstrated facts (e.g. adapters). Concrete solution is not shown on the diagram, - it simply talks about abstractions. And there's no chance to argue about LoD violation in the concrete implementation of the facade, since the author already demonstrated that he has instruments to workaround the violation by introducing adapters.

Tengiz
  • 8,011
  • 30
  • 39
  • "I hope you see that there is no LoD violation at this point: each concrete class depends on an abstraction of its needs and thus makes calls to that limited interface only;" I think this is where i start misunderstanding things. I think I understand what you mean by seeing the figure (more or less illustrated in the answer of @Wolf S) but in the same figure you still have Color c = Seattle.streets()["Main"].houses()[1374].color();. If you could further explain that, it would help. – MaatDeamon Jul 24 '14 at 21:12
  • Because from what i see in the first figure is that in the end, the client will have three services. One to deal with cities, one to deal streets and one to deal with house. He will have to pass around the right parameter to get what he wants. But that does not correspond to Color c = Seattle.streets()["Main"].houses()[1374].color(); – MaatDeamon Jul 24 '14 at 21:16
  • Unless the interface of Seattle is NeedsCity and what is returned by Seattle.streets() is a NeedsStreet and so on ..... If that is the case well, that is indeed pretty ugly. It's decoupled, but still you get to know the inner structure anyway.... – MaatDeamon Jul 24 '14 at 21:20
  • You got it, client works with needs, not entities. It might seem ugly, but it gives 2 benefits at the same time: 1 - you solve LoD, 2 - client depends on abstractions (needs) instead of concretions (entities) - which reminded me that it's a Dependency Inversion from SOLID - one more benefit as a refresher. – Tengiz Jul 24 '14 at 21:33
2

I don't know the book you read and when I first read your question, I thought that is "LoD gone mad". And I understand that you ask about a concrete implementation of this Facade without a violation of LoD.

Let me begin with my first thought on that: If I need to know the property "color" of an object "House", I think it is absolutely ok asking the object House about its property. Of course we don't need to discuss that it is a really bad idea to go down the while chain from city over street to houses because of the dependencies you get.

I would just implement a method getHouse as

public class City {
  public House getHouse(street, number) {...}
}

That method might just discover the addressed Street object and ask that about the house with the given number which would not violate LoD.

But of course you would end up with the following code:

public Foo() {
  Color c = Seattle.getHouse("Main", 1374).color();
}

which again is violating LoD if we take it literally.

But anyway that is way I would implement it, if that is the only point where I need the color of a house, since I don't see any benefits in creating ServiceFacades for a single usage. But if it is needed more than once and you really do not want to break LoD it is quite easy to do, but I needed a second view to see that. The implementation of your Facade would simply look as following:

public class CityMapService
{
  public Color getColorOfHouse(City city, String nameOfStreet, int number)
  {
    Street street = city.getStreet(nameOfStreet);
    return getColorOfHouse(street, number);
  }

  public Color getColorOfHouse(Street street, int number)
  {
    House house = street.getHouse(number);
    return getColorOf(house);
  }

  public Color getColorOf(House house)
  {
    return house.getColor();
  }
}

No method breaks the LoD.

Does it make sense to do so? I would say yes. The inner structure of each object is not exposed and the "bigger" structure of how cities and streets and houses are connected is hidden behind your facade. And if any detail of that changes you will most probably need to change only a single method. And mocking is still quite easy and straight forward.

Wolf S
  • 236
  • 1
  • 3