Manage Dependencies not Aesthetics
The Single Responsibility Principle states that "a class should have one, and only one reason to change."There is another definition that seems to float around which sounds more like what the name implies: "a class should do one thing" but I think that this definition is more about aesthetics than managing dependencies and leads us in the wrong direction. The "do one thing" definition leads us to things like this:
display.print(person.getAddress().getZipCode());
instead of this:
display.print(person.getZipCode());
//in Person
public String getZipCode()
{
return address.getZipCode();
}
or even better, this (employing "Tell, don't ask"):
person.printZipCode(display);
//in Person
public void printZipCode(Display display)
{
display.print(address.getZipCode());
}
Now wait a minute! The Person has a dependency on Display? That's NUTS, right?
Disclaimer:
Yes, dependency on Display IS Nuts. There is some talk of this in the comments below.
That said, a better solution with a similar approach would be to use the builder pattern:
public void export(Person exporter)
{
exporter.setZipCode(zipCode);
}
That solves the current problem, and solves it for any other representations of the Person's data.
This is really the direction I was headed w/ this article to begin with. I just hadn't put 2 and 2 together with this problem and the builder pattern. Live and learn!
Now, back to our regularly scheduled blog.
Well lets look at the implications of all of this.
Imagine that new stories for zip codes come in and we realize that a String representation of zipCode is not sufficient. So we introduce a ZipCode class. Now let's see what changes in the three examples:
In the "do one thing" example, there are two things that have to change:
* The Address class changes its zipCode from a String to a ZipCode, and
* the printing code changes to:
display.print(person.getAddress().getZipCode().toString());
And you can be certain that there are a lot of clients trying to print the zipCode.
In the second example, two things need to change also:
* the Address class changes its zipCode from a String to a ZipCode
* the Person class changes its getZipCode() method to this:
public String getZipCode()
{
return address.getZipCode().toString();
}
but the likelihood is that there are fewer clients of Address than there are of Person, which means less code will have to change. But this still leaves us with another problem, which is that the zipCode concept is now represented by a String in the pre-existing code but it's represented by a ZipCode object in the newly introduced ZipCode management code.
Which leads us to the third scenario - the one with the crazy dependency from Person to Display. What has to change in that scenario?:
* the Address class changes its zipCode from a String to a ZipCode
* the Person class changes its printZipCode(Display display) method to this:
public void printZipCode(Display display)
{
display.print(address.getZipCode().toString());
}
Still two things, but very isolated. No clients of Person who want to print its zipCode will have to change AND all the clients dealing with ZipCodes directly will treat them as ZipCode objects, not some as Strings and some as ZipCodes. So while the dendency appears to be an aesthetic violation, it is a lesser violation of SRP than the previous two examples.
Although the principles often seem like they are about aesthetics, they are not. They are about managing dependencies. And managing dependencies should not be driven by aesthetics like "a Person shouldn't know about a Display" but rather by more pragmatic issues like isolating changes.
!commentForm
I don't know that it has to be A or B, sometimes C is good. Persons don't have to know about display, and zips don't actually have to know how to display themselves. There is a nice python framework for adapters, where you ask it to give you the version of X for topic are Y.
A lot of things are easier in python than otherwise, granted, but it's still a very clean separation to my way of thinking.
A lot of things are easier in python than otherwise, granted, but it's still a very clean separation to my way of thinking.
OK, so you know you're supposed to have a printZipCode() function. But why should it be part of Person? It should be part of the client. Person shouldn't depend on Display; Address shouldn't depend on Display; ZipCode[?] shouldn't depend on Display.
The second solution (where Person has getZipCode()) is a good solution. The first is too fragile and violates Demeter's law.
The second solution (where Person has getZipCode()) is a good solution. The first is too fragile and violates Demeter's law.
The problem with getZipCode() on Person is that when zipCode changes from a String to a ZipCode[?], either all the users of that method have to change to handle the new type or we have to live with zip codes sometimes being Strings and sometimes being ZipCodes[?]. Both of those make me say "ugh" - but maybe its just my sense of aesthetics.
Agreed that having printZipCode on Person seems strange, but that's really the heart of what I'm talking about. What IS the problem w/ that dependency? If Display is an interface there's no violations of Open/Closed, Liskov or Dependency Inversion. You could argue there's an Interface Segregation violation. There's no Demeter violation. We use "Tell, don't ask". Of the three options I listed above, that seems to be the one in which all the players have the least likelihood of having to change as the result of another one changing.
Agreed that having printZipCode on Person seems strange, but that's really the heart of what I'm talking about. What IS the problem w/ that dependency? If Display is an interface there's no violations of Open/Closed, Liskov or Dependency Inversion. You could argue there's an Interface Segregation violation. There's no Demeter violation. We use "Tell, don't ask". Of the three options I listed above, that seems to be the one in which all the players have the least likelihood of having to change as the result of another one changing.
Having printZipCode in aPerson class publishes private by all other means getZipCode() to a limited audience - Displays only. It does not prevent from changing the Display.print method.
How many similar cases should the Person class handle? Shall we add Monitors, Translators, Transformers etc.?
I beleive that Person should manage its own responsibilities rather than be responsible for limiting access to itself.
Perhaps in order to manage access its better to make Person implementing multiple interfaces?
E.g. RegularPerson[?]( no zip code included) and DisplayablePerson[?] with the getZipCode()?
How many similar cases should the Person class handle? Shall we add Monitors, Translators, Transformers etc.?
I beleive that Person should manage its own responsibilities rather than be responsible for limiting access to itself.
Perhaps in order to manage access its better to make Person implementing multiple interfaces?
E.g. RegularPerson[?]( no zip code included) and DisplayablePerson[?] with the getZipCode()?
The tricky part of this is that the return value of getZipCode() changes from a String to a ZipCode[?]. The more clients that use getZipCode(), the more clients have to change. So hiding getZipCode() (a query) in favor of exposing printZipCode() (a command - "Tell, don't ask") results in isolating the effects of the Person's internal structural changes to inside the Person. Maybe there's a better, more generic concept than Display, like StringReceiver[?] or TextReceiver[?] or PrintSurface[?] or whatever. I like that: person.printZipCode(surface). Anyway - you get the idea.
According to your criteria, one reason to change, your prefered solution violates the SRP the most.
- At one point, clients will need the value of ZipCode[?]. Person has to change to accomidate Person.getZipCode()
- Clients need the whole address. Person has to change to accomidate Person.getAddress()
- Clients need to print the ZipCode[?] in bold face. Person.printZipCode() has to change.
- Clients need to print the ZipCode[?] in USPS bar code font. Person.printZipCode() has to change again.
Details, details :)
OK - so first of all, lets change the change Display to Formatter. Now the client interested in that formatting just needs to deliver the appropriate implementation of Formatter to the Person:
The person doesn't need to change to accomodate the bold and bar code printing issues (these are managed by the addition of new Formatters - open/closed) and we still protect the rest of the world from the zipCode changing from a String to a ZipCode[?].
If some client really needs the zipCode, he's going to have to change anyway when we change the zipCode from a String to a ZipCode[?], so I'm not convinced that we're saved from adding or modifying getZipCode() in any of the above cases.
Lastly, the whole Address issue, that's going to affect the Person in any of the above scenarios and that may be the point at which the Address becomes a class instead of a bunch of Strings inside Person. Also, what client needs the Address? I was talking w/ Duncan Pierce about this in Denver so credit for this goes to him, but let's say there's a Letter and you want to put the Person's Address on the Letter. There are two ways you could do it (at least):
letter.setAddress(person.getAddress());
or
Addressable letter = new Letter();
person.address(addressable);
What's wonderful about the latter approach is that you can mock out the Addressable for your test, and any changes to the structure of Addresses or Letters or Persons are local to those objects and affect NONE of their clients.
OK - so first of all, lets change the change Display to Formatter. Now the client interested in that formatting just needs to deliver the appropriate implementation of Formatter to the Person:
Formatter formatter = new BoldFormatter();
display.print(person.formatZipCode(formatter));
//in Person
public void formatZipCode(Formatter formatter)
{
formatter.format(address.getZipCode().toString());
}
The person doesn't need to change to accomodate the bold and bar code printing issues (these are managed by the addition of new Formatters - open/closed) and we still protect the rest of the world from the zipCode changing from a String to a ZipCode[?].
If some client really needs the zipCode, he's going to have to change anyway when we change the zipCode from a String to a ZipCode[?], so I'm not convinced that we're saved from adding or modifying getZipCode() in any of the above cases.
Lastly, the whole Address issue, that's going to affect the Person in any of the above scenarios and that may be the point at which the Address becomes a class instead of a bunch of Strings inside Person. Also, what client needs the Address? I was talking w/ Duncan Pierce about this in Denver so credit for this goes to him, but let's say there's a Letter and you want to put the Person's Address on the Letter. There are two ways you could do it (at least):
letter.setAddress(person.getAddress());
or
Addressable letter = new Letter();
person.address(addressable);
What's wonderful about the latter approach is that you can mock out the Addressable for your test, and any changes to the structure of Addresses or Letters or Persons are local to those objects and affect NONE of their clients.
Let us look at the original problem.
There seems to be a Display class with a print method. What are the parameters of the print method? There can be two overloaded versions, one that takes a String, and the other that takes an Object. The second method should just call the toString() for the object passed in and then invoke the first method.
On second thoughts, since String is an Object, and so has the toString() method, we need only one method: print(Object obj).
What does this have to do with the presence or absence of the ZipCode[?] class? The print method does not care what Object is passed to it. It asks for the toString() method of that Object. Now, for the problem that is being discussed, we are asking what happens if the person.getZipCode() now returns a ZipCode[?] instead of a String?
The answer is nothing as far as the call to the Display.print() method is concerned. It still looks like:
display.print(person.getZipCode());
When this is executed, the toString() method of ZipCode[?] class will be called. Presumably, the ZipCode.[?]toString() method knows how to expose its contents. (Just like a call to System.out.println(HashMap[?]) will use the toString() method of HashMap[?].)
Any other code that expects Person.getZipCode() to return a String will have to be changed. That is an inherent problem with statically typed languages.
I believe that looking hard at the Display.print method and making simple changes to it is the most change frriendly of all the options.
In all this, I am assuming that, for the purposes of this discussion, we do not care how a Person class gets the ZipCode[?]; whether Address or any other class needs to be changed is immaterial.
As far as having either the Person, Address or ZipCode[?] class be aware of the Display class is concerned, I will unequivocally state that it is bad design, pure and simple.
Software is built in layers. If the layers become interdependent, you get such a tangled layer of dependencies that it will become impossible to maintain or enhance it. The use of Display class in Person and other classes is the first step leading to a first-class mess.
This unnecessary dependency that gets created is also the reason I have argued strongly against the Visitor pattern of the Gang of Four Design Patterns book. (See Uncle Bob's articles on the Visitor pattern in this web site.) It is a pattern that should not be implemented in the way suggested by many authors.
There seems to be a Display class with a print method. What are the parameters of the print method? There can be two overloaded versions, one that takes a String, and the other that takes an Object. The second method should just call the toString() for the object passed in and then invoke the first method.
On second thoughts, since String is an Object, and so has the toString() method, we need only one method: print(Object obj).
What does this have to do with the presence or absence of the ZipCode[?] class? The print method does not care what Object is passed to it. It asks for the toString() method of that Object. Now, for the problem that is being discussed, we are asking what happens if the person.getZipCode() now returns a ZipCode[?] instead of a String?
The answer is nothing as far as the call to the Display.print() method is concerned. It still looks like:
display.print(person.getZipCode());
When this is executed, the toString() method of ZipCode[?] class will be called. Presumably, the ZipCode.[?]toString() method knows how to expose its contents. (Just like a call to System.out.println(HashMap[?]) will use the toString() method of HashMap[?].)
Any other code that expects Person.getZipCode() to return a String will have to be changed. That is an inherent problem with statically typed languages.
I believe that looking hard at the Display.print method and making simple changes to it is the most change frriendly of all the options.
In all this, I am assuming that, for the purposes of this discussion, we do not care how a Person class gets the ZipCode[?]; whether Address or any other class needs to be changed is immaterial.
As far as having either the Person, Address or ZipCode[?] class be aware of the Display class is concerned, I will unequivocally state that it is bad design, pure and simple.
Software is built in layers. If the layers become interdependent, you get such a tangled layer of dependencies that it will become impossible to maintain or enhance it. The use of Display class in Person and other classes is the first step leading to a first-class mess.
This unnecessary dependency that gets created is also the reason I have argued strongly against the Visitor pattern of the Gang of Four Design Patterns book. (See Uncle Bob's articles on the Visitor pattern in this web site.) It is a pattern that should not be implemented in the way suggested by many authors.
The Person class should not care at all about how its contents are displayed. Formatting is a function of the Display class; never, but never, of the Person class.
If the zip code needs to be displayed in bold, then the formatting should be applied to the result of person.getZipCode() invocation inside the Display class.
I might start with a printBold method in Display class. After some thought, I'd probably replace that with a printFormatted method that accepts a Formatter.
Something like:
//Display class
void printFormatted(Object obj, Formatter fmt)
{
System.out.println(fmt.format(obj.toString()));
}
You can easily build on it. What if we want to apply a series of formattings, for example, bold italic 24pt. red color? Simply define a Formatter object with the required properties and use it. The Formatter class may need to be changed for this, but not the Display class. In essence, there would be negligible impact on the code. Only those portions of the code that wanted to use the new features would have to change; others that used a single formatting option would not need to change.
The use of Formatter inside Person class would make it difficult to maintain. What if Address class needs to be displayed in some format? Must we change Address class, too?
More generally, what if one application wants to display ZipCode[?] as bold, and another wants to display it normally? How would the Person class know when to use bold, and when to use normal? Or should we add an Application class dependency to the Person class?
You see where this leads to - a tangled web of dependencies.
This is the reason why it is bad design to have Person class become aware of the Display/Formatter/Application/... classes.
If the zip code needs to be displayed in bold, then the formatting should be applied to the result of person.getZipCode() invocation inside the Display class.
I might start with a printBold method in Display class. After some thought, I'd probably replace that with a printFormatted method that accepts a Formatter.
Something like:
//Display class
void printFormatted(Object obj, Formatter fmt)
{
System.out.println(fmt.format(obj.toString()));
}
You can easily build on it. What if we want to apply a series of formattings, for example, bold italic 24pt. red color? Simply define a Formatter object with the required properties and use it. The Formatter class may need to be changed for this, but not the Display class. In essence, there would be negligible impact on the code. Only those portions of the code that wanted to use the new features would have to change; others that used a single formatting option would not need to change.
The use of Formatter inside Person class would make it difficult to maintain. What if Address class needs to be displayed in some format? Must we change Address class, too?
More generally, what if one application wants to display ZipCode[?] as bold, and another wants to display it normally? How would the Person class know when to use bold, and when to use normal? Or should we add an Application class dependency to the Person class?
You see where this leads to - a tangled web of dependencies.
This is the reason why it is bad design to have Person class become aware of the Display/Formatter/Application/... classes.
In this situation, it seems that whenever you want a new function associated with the Person(such as "Formatting" and "Addressing"), you have to modify the Person class. So I think the best solution in this context is applying Visitor pattern. This pattern can make both of our concerns(OCP, SRP and Isolation) satisfitied. Do you think so?
//in Person
public void Accept(Visitor v)
{
v.Visit(this);
}
interface Visitor ...
class Formatter implements Visitor ...
//in Formatter
public void Visit(Person p)
{
format(address.getZipCode().toString());
}
class Addressable implements Visitor ...
//in Person
public void Accept(Visitor v)
{
v.Visit(this);
}
interface Visitor ...
class Formatter implements Visitor ...
//in Formatter
public void Visit(Person p)
{
format(address.getZipCode().toString());
}
class Addressable implements Visitor ...
Ooooh, this is fun.
OK - "Formatting is a function of the Display class; never, but never, of the Person class."
I agree! We're not asking the Person to Format anything at this point. We're asking her to accept a Formatter that takes a String. Whoever wants different formats gives the Person the appropriate formatter.
Now the notion of display.format(person.getZipCode()) sounds good to me - the notion that the format(Object object) method calls to String(). It removes the dependency from Person to Formatter and it requires no changes to Display when the zipCode becomes a ZipCode[?]. But it does still leave open this question: if the Person starts off with her own zipCode and then that moves to an Address, which is better:
or this:
The former requires fewer changes outside of Person, but Micah thinks that Person now envies Address. I think both approaches have merit, and in the end the decision may come down to the current state of the system when the change is made. In other words, maybe we can't say unequivocally that one approach is better than the other outside the context of an evolving system. Any thoughts on that?
OK - "Formatting is a function of the Display class; never, but never, of the Person class."
I agree! We're not asking the Person to Format anything at this point. We're asking her to accept a Formatter that takes a String. Whoever wants different formats gives the Person the appropriate formatter.
Now the notion of display.format(person.getZipCode()) sounds good to me - the notion that the format(Object object) method calls to String(). It removes the dependency from Person to Formatter and it requires no changes to Display when the zipCode becomes a ZipCode[?]. But it does still leave open this question: if the Person starts off with her own zipCode and then that moves to an Address, which is better:
person.getZipCode();
//in Person
public String zipCode()
{
return address.zipCode();
}
or this:
person.getAddress().getZipCode();
The former requires fewer changes outside of Person, but Micah thinks that Person now envies Address. I think both approaches have merit, and in the end the decision may come down to the current state of the system when the change is made. In other words, maybe we can't say unequivocally that one approach is better than the other outside the context of an evolving system. Any thoughts on that?
On the question of person.getZipCode() vs person.getAddress().getZipCode(), it boils down to the intent and business usage.
Are we saying, "I need the zip code of this person, I don't care about the other parts of the address"; or are we saying, "I need the zip code associated with this address".
If a person can have more than one address, whether at a given point in time or over time, then I would prefer that address return the zip code.
If a person can have only one address, and we care more about the zip code than the address, then I'd prefer that the Person return the zip code.
Incidentally, there is no conflict in having both! Depending upon the circumstances, we could ask either the Person class or the Address class to get the ZipCode[?]. With the minimalist approach, we could first start off with the Address returning the ZipCode[?], and if we find that the Person class clients often need to get the Person's ZipCode[?], then we would add the Person.getZipCode() method!
Are we saying, "I need the zip code of this person, I don't care about the other parts of the address"; or are we saying, "I need the zip code associated with this address".
If a person can have more than one address, whether at a given point in time or over time, then I would prefer that address return the zip code.
If a person can have only one address, and we care more about the zip code than the address, then I'd prefer that the Person return the zip code.
Incidentally, there is no conflict in having both! Depending upon the circumstances, we could ask either the Person class or the Address class to get the ZipCode[?]. With the minimalist approach, we could first start off with the Address returning the ZipCode[?], and if we find that the Person class clients often need to get the Person's ZipCode[?], then we would add the Person.getZipCode() method!
The reason we should keep knowledge of Display away from Person is simple: dependency. What if I intend to use the Person class in another application that just needs to find the BankAccounts[?] that the Person has. In order to use the Person class in this new application, we must import the Display class also even though we do not need it!
I've seen applications that have been built in such a way that we can never use a part of it; we must always use all of it or none of it!
Giving People class a knowledge of Display class now couples them together, unnecessarily so. It is the first step towards a monolithic application in the sense that you can use all of it or none of it. You can not "componentize" it. Thus, reuse is discouraged.
Of course, it keeps consultants in business and provides job security to employees; but I feel that that is a short term gain. (And is a totally different topic, too.)
I've seen applications that have been built in such a way that we can never use a part of it; we must always use all of it or none of it!
Giving People class a knowledge of Display class now couples them together, unnecessarily so. It is the first step towards a monolithic application in the sense that you can use all of it or none of it. You can not "componentize" it. Thus, reuse is discouraged.
Of course, it keeps consultants in business and provides job security to employees; but I feel that that is a short term gain. (And is a totally different topic, too.)
Granted that a solution may not violate any of the Open/Closed Principle, Liskov Substitution Principle, Dependency Inversion or Demeter's Law, it still may not be the correct choice because it violates even more fundamental principles. (By the way, I think Dedpendency Inversion is a technique, not a Principle; and hence you can not violate it!)
To me, one of the most important software principles is loose coupling. It enables software to be built in layers on top of other lower level layers. This permits reuse and gives great flexibility to software. When a suggested solution violates that principle, it does not matter at all if it is in accordance with the other principles mentioned at the top. If you are driving on the wrong side of the road, it just does not matter that you are obeying all other traffic laws!
The use of Display in Person class tightly couples the two together, making my code practically useless for reuse or building layers on top of it. I would never do it unless somebody pointed a gun to my head and told me to!
To me, one of the most important software principles is loose coupling. It enables software to be built in layers on top of other lower level layers. This permits reuse and gives great flexibility to software. When a suggested solution violates that principle, it does not matter at all if it is in accordance with the other principles mentioned at the top. If you are driving on the wrong side of the road, it just does not matter that you are obeying all other traffic laws!
The use of Display in Person class tightly couples the two together, making my code practically useless for reuse or building layers on top of it. I would never do it unless somebody pointed a gun to my head and told me to!
My reply to this very interesting post was rather long winded, so I put it on my blog: <http://blogginman.blogspot.com/2005/09/dependencies.html>
Thanks for a very thought provoking blog!
Thanks for a very thought provoking blog!
Agreed on the problems w/ Display - I think we let go of that several posts back in favor of the Formatter which I believe is generic enough (think string.Format in .NET) to live in the model layer without reducing its reusability.
Agreed that loose coupling (the goal of dependency management in my view) is King and all of the other principles (techniques if/where you prefer) are subservient.
The initial blog entry above resulted from what I now realize was a simple struggle between Demeter and SRP - Demeter guiding us to write person.getZipCode() even if getZipCode() delegates to the person's address, while SRP guides us to write person.getAddress().getZipCode().
There are other factors that play into this, like testability and how you approach testing. I find myself using mock objects more and more and for the most part liking the resulting structures. If you favor the SRP approach (person.getAddress().getZipCode()) you have more complex set up if you're mocking a Person than you do if you favor the Demeter approach (person.getZipCode()).
If you take that a step further, and let Demeter combined w/ "Tell don't ask", then your tests are even simpler (and less brittle) because you're not concerned with the return value of getZipCode() - just that the Person receives the formatZipCode(formatter) message.
Perhaps another blog is in order....
Agreed that loose coupling (the goal of dependency management in my view) is King and all of the other principles (techniques if/where you prefer) are subservient.
The initial blog entry above resulted from what I now realize was a simple struggle between Demeter and SRP - Demeter guiding us to write person.getZipCode() even if getZipCode() delegates to the person's address, while SRP guides us to write person.getAddress().getZipCode().
There are other factors that play into this, like testability and how you approach testing. I find myself using mock objects more and more and for the most part liking the resulting structures. If you favor the SRP approach (person.getAddress().getZipCode()) you have more complex set up if you're mocking a Person than you do if you favor the Demeter approach (person.getZipCode()).
If you take that a step further, and let Demeter combined w/ "Tell don't ask", then your tests are even simpler (and less brittle) because you're not concerned with the return value of getZipCode() - just that the Person receives the formatZipCode(formatter) message.
Perhaps another blog is in order....
If we use DIP and let Person accept Formatter or Display as an interface there is no problem to use it in isolation or in another application which can use NullDislpay[?] as the implementation if needed.
Using getters instead of "tell don't ask" can lead to some nasty dependencies and long hours of refactoring.
Using getters instead of "tell don't ask" can lead to some nasty dependencies and long hours of refactoring.
Calling printZipCode(Display) or display.print(person.getAddress().getZipCode()) implies enough significant domain assumptions to limit and confound reuse and system evolution.
Assumption #1: An Address is a US Postal Service Address.
Assumption #2: A Person has exactly one default (unqualified) Address.
Assumption #3: The ZipCode[?] of an Address has exactly one display format.
Any code that pries the ZipCode[?] out of a Person directly is fragile. It matters little whether you get Person to do the dirty work of breaking Address's encapsulation. The original client code will break as soon as any of the domain level assumptions above break. In the example, if you are promoting ZipCode[?] to a first-class class, then likely Address should step up for more responsibility as well.
Frankly, if you are firm that none of the above assumptions will ever become false, then why even bother with an Address class? You might as well just make it a struct with all the members public. Accessors are just a bother if you're going to have your way with it anyways.
With the IDE that I use, refactoring a code level change is not a big deal. I'll get 37 red squigglies and a list of compile errors within a few seconds. Adding ".toString()" to them is tedious, but in the scheme of things not that error prone or long. Changing the multiplicity of an address or adding a field could be a royal pain. Even if I get the red squigglies, I will still have to break my head over each change.
Given that the point was not about addresses but dependencies. I prefer value objects to be dumb; immutible is even better. I would never put a dependency to Display in a value object. It might be serialized to a server not having this class. Formatting is volatile enough with meaningful responsibility to justify a class: it should both offload the client and the value object.
Each situation is different, though. You could make every formatter as flexible as the DateFormat[?] of Java. Though that would be overkill, it illustrates what the serious minds do when they want to keep different responsibilities separate.
Assumption #1: An Address is a US Postal Service Address.
Assumption #2: A Person has exactly one default (unqualified) Address.
Assumption #3: The ZipCode[?] of an Address has exactly one display format.
Any code that pries the ZipCode[?] out of a Person directly is fragile. It matters little whether you get Person to do the dirty work of breaking Address's encapsulation. The original client code will break as soon as any of the domain level assumptions above break. In the example, if you are promoting ZipCode[?] to a first-class class, then likely Address should step up for more responsibility as well.
Frankly, if you are firm that none of the above assumptions will ever become false, then why even bother with an Address class? You might as well just make it a struct with all the members public. Accessors are just a bother if you're going to have your way with it anyways.
With the IDE that I use, refactoring a code level change is not a big deal. I'll get 37 red squigglies and a list of compile errors within a few seconds. Adding ".toString()" to them is tedious, but in the scheme of things not that error prone or long. Changing the multiplicity of an address or adding a field could be a royal pain. Even if I get the red squigglies, I will still have to break my head over each change.
Given that the point was not about addresses but dependencies. I prefer value objects to be dumb; immutible is even better. I would never put a dependency to Display in a value object. It might be serialized to a server not having this class. Formatting is volatile enough with meaningful responsibility to justify a class: it should both offload the client and the value object.
Each situation is different, though. You could make every formatter as flexible as the DateFormat[?] of Java. Though that would be overkill, it illustrates what the serious minds do when they want to keep different responsibilities separate.
Neil makes a few good points about the trivial nature of the example - but that is often a problem with examples in articles, blogs and books. If you use a real one its too complex to be a good teaching tool, but if you use a simple one it seems too trivial to bother with.
The point that I was trying to make is not about Persons and ZipCodes[?]. As I give it more thought, its really about tension between SRP and Demeter and the opposite directions they lead us. Let's imagine you're modeling a video game and you've got a Person that has to do things - not just a dumb value object - but it does things like jump, run, draw a sword, etc. This Person is a much more complex object graph. He's got body parts to control, sounds to make, skin tone and lighting to deal with when he draws himself, etc, etc.
So in this example, SRP leads us to this:
person.getLimbs().getRightLeg().bend(20);
person.getLimbs().getLeftLeg().bend(20);
person.getBack().bendForward(3);
person.getLimbs().straightenWithVelocity(10);
person.getLimbs().straightenWithVelocity(10);
while (person.isInAir())
{
person.getLimbs().straightenWithVelocity(.25);
person.getLimbs().straightenWithVelocity(.25);
}
person.getLimbs().straightenWithVelocity(2);
person.getLimbs().straightenWithVelocity(2);
while Demeter says do this:
person.jump();
So this primary domain concept which uses many other classes to do what it does must expose its parts according to SRP, and therefore tie its clients to its structure, while Demeter protects its clients. But Demeter places the responsibility of absorbing change on the object sitting at the top of the graph.
That make more sense?
The point that I was trying to make is not about Persons and ZipCodes[?]. As I give it more thought, its really about tension between SRP and Demeter and the opposite directions they lead us. Let's imagine you're modeling a video game and you've got a Person that has to do things - not just a dumb value object - but it does things like jump, run, draw a sword, etc. This Person is a much more complex object graph. He's got body parts to control, sounds to make, skin tone and lighting to deal with when he draws himself, etc, etc.
So in this example, SRP leads us to this:
person.getLimbs().getRightLeg().bend(20);
person.getLimbs().getLeftLeg().bend(20);
person.getBack().bendForward(3);
person.getLimbs().straightenWithVelocity(10);
person.getLimbs().straightenWithVelocity(10);
while (person.isInAir())
{
person.getLimbs().straightenWithVelocity(.25);
person.getLimbs().straightenWithVelocity(.25);
}
person.getLimbs().straightenWithVelocity(2);
person.getLimbs().straightenWithVelocity(2);
while Demeter says do this:
person.jump();
So this primary domain concept which uses many other classes to do what it does must expose its parts according to SRP, and therefore tie its clients to its structure, while Demeter protects its clients. But Demeter places the responsibility of absorbing change on the object sitting at the top of the graph.
That make more sense?
I don't think that SRP necessarily forces Demeter violation. There are times when you can do something like this:
The transformation is kind of like the Bridge Pattern.
new RightLegActor(person).bend(20);
The transformation is kind of like the Bridge Pattern.
I'm imagining this in RightLegActor:
Isn't that the same problem? RightLegActor has to change as the Person's graph changes. Thoughts?
public void bend(int howFar)
{
person.getLimbs().getRightLeg().bend(howFar);
}
Isn't that the same problem? RightLegActor has to change as the Person's graph changes. Thoughts?
It could be, but instead of looking like this:
the code could look like this:
it's letting Person have a lower level API. Yeah, RightLegActor does have to change in the same way that the client code would have to in what you posted above, but hopefully it's localized there and not in all clients.
public void bend(int howFar)
{
person.getLimbs().getRightLeg().bend(howFar);
}
the code could look like this:
public void bend(int howFar)
{
...
GentleFlexion flexion = new GentleFlexion(howFar);
person.freezeAspects();
person.flexMesh(Person.QUAD4, flexion);
person.calculateMeshes();
}
it's letting Person have a lower level API. Yeah, RightLegActor does have to change in the same way that the client code would have to in what you posted above, but hopefully it's localized there and not in all clients.
My issue is less with Persons and ZipCodes[?] per se. It's more with bloating entities with complex procedures which are bound to drag all sorts of dependencies.
When I only want Person.sitAndListen() I don't need the code for Person.jump() and Person.lambada().
Again, I'm not anal about Demeter. Many time it's expedient. It's not necessary to keep everything ultra clean. It's just that you should keep the ultraclean stuff away from the quick and dirty.
That said, I'd much prefer to have a class, Acrobatics, with:
jump(Acrobat);
spring(Acrobat);
summersault(Acrobat);
so it's separate from SquareDance[?] having
aLaMain(Dancer, Dancer);
doSiDo(Dancer, Dancer);
Person can implement the Dancer and Acrobat interfaces and hide even more from the controlers.
It all keeps Person clean and simple. It keeps the Tango steps out of the Lambada and allows for new dances without affecting either the entity or the sibling subclasses.
I'm much more with Michael on this.
When I only want Person.sitAndListen() I don't need the code for Person.jump() and Person.lambada().
Again, I'm not anal about Demeter. Many time it's expedient. It's not necessary to keep everything ultra clean. It's just that you should keep the ultraclean stuff away from the quick and dirty.
That said, I'd much prefer to have a class, Acrobatics, with:
jump(Acrobat);
spring(Acrobat);
summersault(Acrobat);
so it's separate from SquareDance[?] having
aLaMain(Dancer, Dancer);
doSiDo(Dancer, Dancer);
Person can implement the Dancer and Acrobat interfaces and hide even more from the controlers.
It all keeps Person clean and simple. It keeps the Tango steps out of the Lambada and allows for new dances without affecting either the entity or the sibling subclasses.
I'm much more with Michael on this.
Neil Pitman wrote: I'd much prefer to have a class, Acrobatics, with:
jump(Acrobat);
spring(Acrobat);
summersault(Acrobat);
That is interesting. Isn't OO all about obejcts and sending messages to objects?
The construct suggested above seems procedural to me. You have procedures "jump", "spring", "somersault" etc., and an object passed to them. It almost sounds like the invocation of a procedure with parameters passed to them.
Wonder how much of so-called OO code is actually procedural code wrapped inside classes?
This is not to say I disagree with Neil's comments. This is just to point out that no paradigm can solve all types of problems. Sometimes OO is the objectively correct way to go, at other times procedural is how we should proceed; and at other times a functional approach functions well. All approaches have their place.
jump(Acrobat);
spring(Acrobat);
summersault(Acrobat);
That is interesting. Isn't OO all about obejcts and sending messages to objects?
The construct suggested above seems procedural to me. You have procedures "jump", "spring", "somersault" etc., and an object passed to them. It almost sounds like the invocation of a procedure with parameters passed to them.
Wonder how much of so-called OO code is actually procedural code wrapped inside classes?
This is not to say I disagree with Neil's comments. This is just to point out that no paradigm can solve all types of problems. Sometimes OO is the objectively correct way to go, at other times procedural is how we should proceed; and at other times a functional approach functions well. All approaches have their place.
It's easy to say nay to aesthetics. Certainly it's like the case of "a foolish consistency" being fool, but a wise consistency being wise. Aesthetics do matter, since the code would run just as well were all variables and classes named 'a1..aN'. It would run as well, but would be awful to run.
But that is neither here nor there. If you accept that maximum cohesiveness means that all methods use all variables, then you can see that having a class do more than one thing means that many method would not use many variables. It seems that having more than one responsibility means having the internal interface in need of segregation. Hmm.
The old RDBMS guys say "when in doubt, break it out". I think that a multipurpose class might be better served by multiple classes. I don't see any problem with one of those classes being an extension, decorator, or adapter. I've been lax in pursuing my ideas sometimes, but I'm convinced that a system like the one based on python adaptation would have far superior structure in terms of both dependency and cohesion than either of the solutions offered here.
But that is neither here nor there. If you accept that maximum cohesiveness means that all methods use all variables, then you can see that having a class do more than one thing means that many method would not use many variables. It seems that having more than one responsibility means having the internal interface in need of segregation. Hmm.
The old RDBMS guys say "when in doubt, break it out". I think that a multipurpose class might be better served by multiple classes. I don't see any problem with one of those classes being an extension, decorator, or adapter. I've been lax in pursuing my ideas sometimes, but I'm convinced that a system like the one based on python adaptation would have far superior structure in terms of both dependency and cohesion than either of the solutions offered here.
Add Child Page to ManageDependenciesNotAesthetics