Reputation: 9771
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{};
}
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:
- O’s
- M’s parameters
- Any objects instantiated within M
- O’s direct component objects
- 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;
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
In applying the law of demeter and the needs/capability interface separation we should initially get:
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:
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.
Upvotes: 14
Views: 979
Reputation: 236
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.
Upvotes: 2
Reputation: 8429
I guess there's a "mis-communication" between you and the author.
Step 1: you have this:
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:
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.
Upvotes: 3
Reputation: 10855
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.
Upvotes: 3