ResponsibilityDrivenDesign seen to Conflict with YouArentGonnaNeedIt
Example:(from PrimaryNoun). Suppose we need an operation to assign an employee to an office site.
assignToSite(emp, site) // procedural // OOP emp.assignToSite(site) // OO variation 1 Or site.assignEmp(emp) // OO variation 2 Or site.employeeList.add(emp) // OO variation 3 assignments.insert(emp, site) // relational{The last line is peculiar. What makes it "relational"? "Relational" isn't any programming paradigm that I'm aware of, and the syntax suggests it should be "OO variation 4". It appears that an object reference called 'assignments' has a method called 'insert' which is being invoked with arguments 'emp' and 'site'.}
Not quite. There is a pure engineering concern here. Employee instances are likely to land in lots of lists - you don't want to open up the class every time some new list is defined. Nor is there any value in the class knowing what lists it is in. On the other hand, Site is clearly a container of employees and other things. The maker of lists needs to know what's in them, so the dependency arrow goes from Site to Employee, putting the methods in Site.
There is no dilemma, only a red herring. Either one or neither of the classes of site and emp might be the appropriate one to take responsibility for assigning one to the other. These sorts of decisions are almost never arbitrary; almost always there will be some hint in the problem domain as to where the responsibility lies best, or else those part of the solution already existing will let you know. A smart developer might well, if not guided either way by the factors already mentioned, introduce (or find) a third class to hold the responsibility - exactly to avoid an artificial choice of one or the other. In this particular case, that's probably what I'd do (in the absence of any other information). That would give us, perhaps:
locationPlan.assign(emp,site) // OO variation 3Which is, in many ways, very little different from your procedural form at the top.
I would consider that procedural in OO clothing unless there was more to the locationPlan class. If not, then it is anti-YagNi IMO.
There's no conflict between YouArentGonnaNeedIt and ResponsibilityDrivenDesign. If you think otherwise, perhaps you'd like to fill in ResponsibilityDrivenDesignConflictsWithYagni.
If you have x.y when there is only a "y" at the moment, then it does conflict with YouArentGonnaNeedIt. (I would note that I don't always agree with YagNi either, but this is not one of them unless better justification is given.)
You are not properly taking time into account. Typically, the responsibilities that a system has to meet are exposed over time, but even supposing that they were all known at the beginning of development, they can't all be implemented at once. So, one way or another the implementation of responsibilities is always partially serialized,
I am not sure what you mean by "serialized". It usually refers to conversion to text for transfer in this context in my experience. Serialize - make serial, ie make come one after the other.
In what jargon? Java's? I've seen that called "marshalling" in many other places.
The question is, what class (we'll suppose that we're using a class based language) is the best choice to bear this next responsibility?
No, I'm saying that the idea of YagNi
class locationPlan { method assign(...) {// only method for class right now stuff... } } function assignToSite(...) { stuff... }The second one looks like it better fits YagNi to me.
So, what are the possibilities in the case presented? Well, it might be that we've already seen fit to create Site and Employee, or one of them, or neither of them, before we get round to knowing that employees can be assigned to sites. We might already have created SitePlan?, because of some other responsibility that we have already seen, although that seems less likely. Anyway, the point is that we might very well not "have x.y" already, that is, when we come to consider assignment of Employees to Sites, perhaps we've not yet written Employee::assignToSite(Site), nor Site::assignEmp(Employee), perhaps we need to make a decision about which to write. And perhaps the answer is neither. And even if we do already have x.y, we might very well see fit to move y to some other class than X, as we discover more about the problem (and the solution).
To introduce a new class to handle the newly discovered relationship between sites and employees would be an example of reification, a very useful design technique, one not widely enough known, understood, or applied. And one that in no way conflicts with the notion of only addressing the currently needed requirement. -- KeithBraithwaite
It appears to be yet another case of OO forcing you to pick a taxonomy up front, and then all the rework associated with undoing your decision when the real world poops on it. Rather than reduce the impact of change, the OO crowd has embraced code rework and attached fancy labels to it to make it sound acceptable. "Refactoring Engineer". Translation: OO taxonomania mess cleaner-upper. Don't put taxonomies in code. They don't belong there. The less classifications and grouping of things you do in code, the more change-friendly it will generally be. OO designs often have one pulling their hair out trying to decide whether to put the operation in the likes of:
In the above example, if we want to query a Site about no. of employees assigned to it, we may be better of using
site.assignEmp(emp)otherwise, if we want to query an Employee about the site to which he is assigned, we may have to use
emp.assignToSite(site)If both queries are necessary, then probably doing both will be useful.
Normally, OO design decisions cannot be made by focusing on a single relation but also the effects that they have on the objects. -- VhIndukumar
Good point on queries cited above. You might need to do both:
emp.assignToSite(site) site.assignEmp(emp)
That sounds like poor normalization.
Yagni applies perfectly: where a class does not have responsibility, neither does it have code. When you do need it, such as adding a site query site.listEmployees(), you add the method and the call to site.assignemp().
I am not quite clear on this.
The quantity of queries may change over time. It seems like an artificial early choice is being pushed upon us here. That is why I prefer leaving the action alone by itself. Surely, there must have been a query in mind before introducing this method (definitely, this relation is being used somewhere). Decide according to the usage. Later, when there are more queries, we will get to know whether it makes sense to place it in a separate 'SiteRelation?'. But for the present, placing it inside one of the classes seems sufficient. -- VhIndukumar
Well, notice that putting the management of the relationship in a separate class allows for ShieldPattern, providing the flexibility you might be looking for. -- KeithBraithwaite
Putting the "management of the relationship" in a separate class allows for what I typically see as a huge amount of code duplication in typical OO design. I'd definitely put it in an object instead. -- CostinCozianu
Don't you mean "method" instead of "object"? No, I mean exactly what I say. Simply put the relation between Employee and Site is a particular instance (and not sub-class nor sub-type) of a very powerful mathematical concept called Relation. Even approaching the design from an OO point of view, to manage it through an object is preferable. See also ManyToManyChallenge.
What? Unless you're thinking about a solution in SelfLanguage or similar, then the object that implements the relation will be an instance of a class, exactly the class to which we have assigned responsibility for managing the relation (and exactly the class I was referring to).
''I guess I need to be more explicit: the object will be in the well known and very ugly Java syntax
BinaryRelation? employeeSiteRelation= new BinaryRelation?();and to "manage" it, I'd do something like :
employeeSiteRelation.add(new Object[] {emp, site} ) ; employeeSiteRelation.remove(new Object[] {emp, site}) ; employeeSiteRelation.remove( new Predicate () { /* predicate expression as inner class goes here */} ); employeeSiteRelation.select( new Predicate () { /* predicate expression as inner class goes here */} );Ouch! Which part of the requirement as stated makes you think you need all this stuff? Especially all that Predicate nonsense? Oh, wait a minute though, this is interesting. Perhaps the reason that the RelationalWeenies think that the ObjectOrientedWeenies? are busy "reinventing the database in the application" all the time is that the RW's can't imagine a way to implement a trivial feature like the one described without pulling in all this over-generalized junk right away, and exposing it, which indeed would be reinventing etc. And maybe they think, therefore, that the OOW's can't think of any other way to do it, either. Perhaps here is an aspect of the ObjectRelationalPsychologicalMismatch laid bare?
Especially all that Predicate nonsense? How about the nonsense in your comments and invalid inferences? I don't need "all that stuff", I just wrote it once. If you think you have a better solution, than by all means show it. Be my guest to ManyToManyChallenge. Actually you're not enough OO weenie to realize that the above code sketch is the paradigmatic OO way of doing things. You put common abstraction in classes and you don't rewrite the same or analogue code over and over again. One such abstraction is "relation", but since you have a psychotic allergy to anything related (pun intended) to relational you refuse to acknowledge it. Objects don't have relations, they have associations and collections. The difference is fundamental. And if you think that duplicating that abstraction all over the place in classes like UserToSiteRelationship? is a good OO design, than maybe it's time to go back and study your OO better.
Deary me. The example says "Suppose we need an operation to assign an employee to an office site." Ok, then, I'd provide an interface perhaps like this:
interface SitePlan?{ void assign(Site s, Employee e); }That interface can completely support the stated requirement. Now, the question is, how to implement this interface? Some other parts of the requirement will guide us. It might be that the only query that's needed is a list of the employees assigned to a site. In which case we might have SitePlanImpl? delegate to Site, which would require perhaps only an array of Employees. Maybe some other queries are required. If so, and if they are sufficiently complicated, then the interface will grow, and the implementation might end up looking like what you suggest. The implementation (not interface) that you suggest is a perfectly reasonable point to expect to reach - if the application ends up doing lots of clever things with Sites, Employees, and who's at which. But to jump straight into that stuff, and to mix classes like BinaryRelation? with classes like Employee, on an equal footing in the same area of code, that way lies madness. YouArentGonnaNeedIt.
I don't even need a specific interface to support stated requirement. I just need an object which is an instance of a very generic class (it is not at all mixed with Employee as you claim). In order to set it up, I need just a one liner: specificRelation= new BinaryRelation? (). While you will need more, a lot more typically: think of the support needed for basic operations like at least removing users from sites, removing sites , removing users. Plus I will use the one liner consistently for lots of binary relations that you can see all over the place in object models. So it is you who is in breach of YAGNI, cause you are never going to need to customize the same relationship management code for lots of particular relations. I can use it for EmployeeTOSites, EmployeeToGroups?, DepartmentHierarchy?, EmployeeTODepartment, EmployeeToProjects?, DepartmentTOProject and so on so forth. While you will rewrite the same boring code to add and subtract pointers from various arrays and hashmaps.
I guess the only problem you could see is that BinaryRelation? is not readily available in your favorite collection framework. If you had it ready made it would've been a no brainer to use it. If you don't all you could do is whine that it would be, quote "over generalized junk". Well, I guess it was procedural weenies that complain about OO abstraction that was "over generalized junk". Now you've just taken over their position, thinking that you fight against relational evil empire or something :)
"fight against [the] relational evil empire" Don't flatter yourself :)
First of all, I envy your ability to predict the future. From that single phrase "Suppose we need an operation to assign an employee to an office site." you are confident in inferring all those other requirements! I wish I could do that, accurately. But I can't. And I don't believe that you can either.
But of course that's a RedHerring what you're objecting to. First, you don't need to be able to predict the future in order to use a proper relation object, it's enough to predict the past. We started from a contrived example mostly unspecified, to which I commented that typically I'd use an object rather than a class to manage a relation. And you objected to that, saying something to the effect that I didn't know what I was saying. I proved to you I did know what I was saying, and again you launched in an uncalled for diatribe waving at me some XP rhetoric and some quite insulting non-sense about YAGNI. Even when your alternative solution would be clearly in breach of YAGNI under the reasonable premise that BinaryRelation? is already written, and that's because it is more complex, it requires more lines of code and it is more fragile.
As for predicting the future you just have to look in the past code you've written and see that for the a quasi majority of cases relationships needs to be created as well as destroyed, business objects need to be created as well as deleted. Guess why all collection classes have add as well as remove? Only these requirements applied to your average of more than 5 relations per object model makes it more than reasonable to write your BinaryRelation class even if you didn't have it.
Secondly, a long time before I learned about YagNi and the rest, I learned another, but similar, principle which has served me well - always UseTheLeastPowerfulConstruct? that will do the job. As such, I'd never as a first choice use so powerful a class as your BinaryRelation. (By the way, where does it say in the requirement that Employee and Site are equal partners in the relationship? Is your BinaryRelation bidirectional? If so which part of the requirement makes you think that flexibility is worth paying for in this case?).
Well, if you think that BinaryRelation is "such a powerful class", I should direct you to read the standard ADT libraries in Lisp, Haskell, SML, Ocaml, Clean to give just a few examples. They have much more powerful constructs (with list comprehensions, higher order functions and all enchilada) for free and they use those as regularly as your average Java dude uses the array construct. So much for your stupid objection of "overgeneralized junk".
By the way, I do think that directionality of relationships is OO non-sense, and unless you convince me that we're in some kind of embedded system that makes an extra pointer too high of a price to pay (an unlikely case here), I pay nothing for having my relations proper relations, and programmers who care about directionality end up paying more. Just look above, I've written one line of code, so where the hell is the cost? Now pick your favorite algebra book and show me where they speak about "directionality" of relations. Relations do not have directionality, this is a useless concept in >90% of the cases.
Thirdly, if a slew of extra requirements such as you suggest came along, I would not be "rewriting the same boring code", I'd be pulling up commonality into shared base classes and getting reuse. And I might, at some time, when sufficient of these requirements have been found, switch to a library-provided relation class such as you suggest. This is all a matter of taking small steps, and making suitable engineering tradeoffs at each step. Notice that this sequence of events could take a little time as a few minutes.
Sure. I have nothing against your style, but don't make it my style, will you? Talk is easy, how about taking the ManyToManyChallenge?
No thanks. You've already trolled that one into the ground.
So Costin is a troll also for suggesting that relational theory may simplify this issue? RelationalWeenie == Troll in your mind, it does seem.
Maybe I'm reading too much into the problem, but it seems to me that "employee" and "site" are data, while the association between them is business logic. Shouldn't these things be separate? -- MikeSmith
Relationships are data. Logic is behavior. -- Randy Stafford
Do I sense an ObjectRelationalPsychologicalMismatch condition coming on here, or am I misinterpreting the question?
Seems more like a determinedly naive, obtuse even, interpretation of OO to me.
The simplest thing that works is y(), and not x.y(). If you do that a lot, then your code is larger than it needs to be.
If you do that a lot the code should probably be refactored into a method of "x". Then you can use "y()" without an object. The only time you should do "x.y()" is when something other than x needs to send x the message y.
I though this discussion is about making a separate class. The issue of which class to embed it with is a topic for PrimaryNoun it would seem to me.
My point is that when you "do that a lot" it's best to stop doing that. Hence the objection is invalid if the code is reasonably refactored.
The fact that minor differences in opinion WRT OO philosophy or initial requirements can result in at least 3 variations which could easily change over time approaches a "smell" that something is wrong with "every method must have a class". It is "wobbly" IMO.
Who's saying "every method must have a class"? What, in fact, is that meant to mean, as a criticism? In an class-based OO language (pace CLOS), every method will belong to a class. Just as in a modular language every procedure belongs to a module, or in C, every function is within a compilation unit. Etc. etc. It's not an overhead, it's a facility. No one is suggesting that every time the need for a new method is discovered a new class must be created to hold it.
I suppose this returns to the issue of "what is OOP". There is also the issue of calling. One still has to instantiate it if in a sparse class in most OOP languages. And, you still have the 3 possibilities to decide between.
Most popular OO languages provide ClassMethod?s that are associated with the class and not an instance. You don't have to instantiate anything to call them. The class name acts as a namespace to keep track of where they live. OO includes procedural in this way. The fact that there are multiple solutions to a problem isn't a smell, just a common aspect of most interesting problems.
In other words, the solution is to mimic procedural, but in a slightly more bloated way. A ClassMethod? is like a procedural module. The solution is still procedural in nature, not OO.
The solution is available in OO programming languages, no matter what you call it. If you don't want to use an object you don't have to.
You seem to be saying, "enough OOP programming languages allow procedural or procedural-like constructs that you can almost have it that way if you want." How about a plain-jane function then? Why beat around the bush?
class A {
static void B() {...}}
That's a plain-jane function. No beating around the bush. In practice I never use them, but they are available if you want them.
You still have the "dot problem" of A.B() unless you redefine it locally for each using class (possibly a OnceAndOnlyOnce violation of sorts). YagNi dictates calling B() instead of A.B() unless A.B() solves some immediate problem. And, it is still more syntax to set up than a function. Notice the 2 levels of nesting instead of the one that would be needed for a function.
There is no dot problem. YAGNI doesn't say "write procedural code". YAGNI says don't add features you don't need. Sheesh.
What do you mean? You don't need the stuff before the dot. It is not needed. It serves no purpose at the time of writing the code. It is superfluous to identified needs. How else can it be interpreted? A function is less code both on the defining side and caller side (if outside the original class).
YAGNI says don't add features you don't need. The stuff before the dot is not a feature and not addressed by YAGNI.
I see no reason to exclude class wrappers from YagNi.
Class "wrappers" are features? Did a customer ask for them?
No. That is yet another reason to exclude them.
Then we agree. Class "wrappers" are not features and as such are not subject to YAGNI. YAGNI is about features.
For the sake of argument and your characterization of YagNi is correct, then is there not a coding equivalent of YagNi that perhaps goes by a different name?
Not that I know of. My rule for code is that it should be easily understood for an average programmer. I am just as verbose as I need to be to meet that requirement and no more.
I quit trying to write "less code" when I upgraded my 32x16 character display and 64K RAM. Now I try to write just enough code so that any programmer familiar with the language and idioms will be able to easily understand what I'm doing.
Perhaps a case of QwertySyndrome. I like lean code. If something before the dot does not contribute, then I would rather see it cut off. Sometimes with bloated code I have to get a highlighter pen and highlight the stuff that really matters so that I can read it. If you have the ability to read bulky code fast without a highlighter pen, I applaud you. I guess I am too much like PaulGraham......without the millions.
[Ha ha ha ha! You, my friend, are nothing like PaulGraham.]
{I generally meant "in that regard". He believes that unnecessarily long code is a smell that your coding abstractions are not sufficient. BTW, your ha's are as repetitious as your code.}
Aside: why do some people who are ignorant of a technology, methodology, or way of thinking insist on accusative, confrontational language, instead of a humble, inquisitive approach? You end up in situations like this one, anyone who's done ANY OO programming knows about static methods. Yet all this effort was expended due to a confrontational approach from someone who had no clue about OO and made an accusation that you can't do functional programming in OO languages. Reminds me of UseNet.
Like it says at the top of this page, it's trolling. Accusation and confrontation are the goal, not wisdom sharing.
{I simply set out to try to explore something about OOP that bothers me. Why is that "trolling" or "confrontational"? I honestly feel that the "other guy started it". Maybe I am delusional and need deep therapy and it really is all my evil fault, but I did not *consciously* set out to start fires, but to convey my view of things as clearly as I could in order for others to study them with me. Maybe when emotions cool off a bit, we can go back and soften any harshness found (without changing the meaning).}
Fix my language if you don't like it. It is a round-a-bout function in OO clothing. The bloat builds up over time.
a = foo.bar(b) + foo.bar(c) + foo.bar(d) + foo.bar(e) Verses: a = bar(b) + bar(c) + bar(d) + bar(e)It makes code harder to read IMO. If you disagree and still find it just as easy to read, that is fine. Everybody is different. But why do you insist in calling me a troll because I claim it makes code harder for me to read???? It is objectively longer. Look at it!
vs
In a method of someClass:
int a = foo.barAndAdd(b,c,d,e);then in foo's class:
int barAndAdd(int b, int c, int d, int e) { return bar(b)+bar(c)+bar(d)+bar(e); }Now the dependency between someClass and foo's class is smaller. someClass sends foo one message instead of 4. It may be more characters than the procedural line, but that isn't enough reason to go back to a procedural language. OO languages add enough value to justify the added verbosity.
As I said before, I don't call you a troll. I call what you do trolling.
It is still a vague word and rude. If I call your content "biased" instead of your person, that is slightly nicer, but still rude.
The word is well defined. It fits in this situation. Is there a less rude term we can use?
So you are admitting to being rude? That is a start. I saw no consensus under TrollDefinition, and some definitions are about person A's guess as to person B's evil motivations rather than the nature of content.
Every word/phrase/idiom falls someplace along the rudeness-politeness spectrum. I see a consensus on TrollDefinition. Your posts are a good example of the term. Is there a less rude term we can use?
I don't see any goddam consensus. You are the "troll". A function is objectively less code than option #3, not an opinion. It is less ASCII characters that a 4-year-old can count. You are wrong. Swallow the living fact and shut up now. I am offended at being called a troll, and shall call you a blind stubborn OO shithead in response. You have been cranking out bloat for so long that you accept it as a way of life.
The addition of an object or class name to the front of a method provides a common idiom for communicating subject-verb or direct object-verb relationships between OO programmers. Longer does not (in this case) mean harder. IDEs make it easy to type verbose identifiers once they have been created. Reading verbose identifiers can be much easier than reading terse identifiers. There are rare occasions when a method truly belongs to no class.
I still see this one-class thing as arbitrary. There is no force in nature that I can identify that naturally selects one and only one entity for every action. Perhaps it models your own brain patterns well, or is a UsefulLie of some sort, but I see nothing universal or external about it.
So what if it is arbitrary? So what if you can identify a force? It works well for me and many other programmers.
So lets declare EverythingIsRelative, delete wiki and go home.
I haven't had one of those for years. If I have one tomorrow I'll create a class for it to live in and get on with the job. I'm not ashamed to write a line of procedural code. Every one of my methods contains purely procedural code. Most of the methods they call are in the same class. My first OO language (C++) began as a preprocessor for a procedural language. OO is, for the most part, just a better way to organize procedural code.
{So I've heard, but not seen.}
[You sound convinced that you should write in a procedural language. Sorry, this is a tired old debate that's been repeated countless times on this wiki and through the annals of OO history, I'm not sure many people care to argue this again and again, especially in a confrontational manner.]
Confrontational? You guys called me a "troll" first! Hypocrits! Arrrrg! [I never called you a troll] Well, then my frustration is directed at the being who did.
Are you saying I am wrong? I am not wrong. x.y is anti-YagNi when y is appropriate. Are you saying it is too minor to matter? Or are you just so used to doing that way that you write it off as the cost of doing OOP? Is this wiki only for "major" issues? It could add up to roughly say 20 percent more code bulk. That is hardly "minor". IMO you are calling me a troll to avoid facing the issue. Prove my hunch wrong. Troll is such stupid vague word anyhow.
[No I'm not saying you're wrong. You're kind of like the guy who comes along and tells me C++ is much better than Java (enumerate reasons). However that "debate" is so old and tired and subject to personal taste that I would rather stare at the wall for 15 minutes than spend 15 minutes debating the issue with you. I also think that the brain evolved to grep colors and icons and spatial features rather than just monochrome text, and therefore I like working in GUI applications better than Vi. But you won't find me arguing that one very often either].
We write it off as a cost. The occurrence of a classless method is very rare.
It is possible that you habitually force the activity to go with one or the other noun in a somewhat arbitrary decision. I cannot very your code because it is InternalEvidence, but a possibility to consider.
You're ignoring my next sentence:
My methods tend to grow from my classes through refactoring.
Think about that for a second. The only time I add a method is when one class's method needs to ask an object or class to do something. There's nothing arbitrary about which class the method belongs to, or if it belongs to a class at all.
I would use a procedural language, or use an OO language in a more procedural way if I wanted to. YAGNI is not a dictate to do the dumbest or hardest to maintain thing that could possibly work. YAGNI is about features. Don't add features that aren't needed. Don't day dream in code. -- EricHodges
Well, IMO there should be a YagniForCodeRule? also. Lean code makes it easier for me to read and understand (all else being equal). I find unnecessary dot-danglers and other doo-dads distracting. That is the bottom line. You cannot take that away from me, for I know my mind better than you.
I have no intention of taking anything away from you. After working with large teams on large code bases I find that Java provides a very useful mix of verbosity and simplicity. I'd much rather maintain millions of lines of well refactored Java than C. We write for the average Java programmer, not any individual brain. -- EricHodges
I suppose you could argue that somebody not good at reading "well decorated" code should not be in the business. But us lean-coders would probably say that "decorators" should not be in the business.
Say whatever you like. I've written PDP-11 C code that was limited to 6 character variable names. If that's the sort of thing you consider "lean code", I hope I never see another line of "lean code" as long as I live. There are many more like me who have come to the conclusion that code is first and foremost a document through which programmers communicate with each other, and that languages like Java are well suited to that task. -- EricHodges
I don't consider cryptic variables part of the "lean" philosophy, because good naming you *do* need. I would like to see more evidence that Java is well suited for programmer communication. JavaAndTeams? perhaps.
Talk to Java programmers for whom Java is not their first language (like me). -- EricHodges
Just because the first language you used stunk does not mean Java is the ultimate solution. C? I don't think highly of C either.
You can talk to me if you want more evidence for why I use Java, or you can ignore me. I didn't claim Java is the "ultimate solution". -- EricHodges
Re: However that "debate" is so old and tired and subject to personal taste...
I perfectly agree that it could be subjective. SoftwareEngineeringIsMostlyAboutPsychology?. I never suggested otherwise. I am only attempting to do a "brain dump" of why I prefer it that way, and YagNi appears to be part of the reason. I am trying to explain things that "bother" me about OO. Maybe it bothers some people but not others. Someone mentioned that a similar debate came up a long time ago. Apparently somebody deleted it. So let's document the viewpoint once and for all rather than keep rediscovering it. You don't have to agree with the argument, only recognize the opinion. Deal?
Re: Longer does not (in this case) mean harder. IDEs make it easy to type verbose identifiers once they have been created. Reading verbose identifiers can be much easier than reading terse identifiers.
Automating bloat with an IDE is not an excuse IMO (CodeGenerationIsSmell [sp?]). I like terse. It helps me read code better (as long as important info is not lost). "locationPlan" adds nothing but distraction here. You see, what I do is make a compact little sub-language customized for the task or application at hand. That keeps the code describing the process and only the process, not bureaucratic nonsense (IMO) like "locationPlan". Maybe with your style it does not matter for some reason, but for mine it does. I try to push off the red tape to side areas where it does not get in the way. I keep the key arena of biz logic clear. The coaches are to stay off the floor during the game.
Great. I'm glad that works for you. I don't enjoy working on that sort of application every day. I enjoy writing databases more than using them.
Well, there are probably far more database users than database builders, so we better get used to it.
Someone has to write them. It might as well be me. If not, I can always become a farmer. -- EricHodges
{Unfortunately, that kind of work is probably going to places like India where direct communication and interaction with a specific customer is less important and the labor rates are much lower.}
[Plus, there's code generation and then there's code generation. Generating entire classes or methods is smelly. Doing autocomplete on variable names isn't, neither are the IDEA refactoring functions.]
Doing something like system.print(....) all over the place bothers me also. I know one can add a method local to the class, or classes' parent, to shorten it, but that is repetition for each class that uses it. Besides, it might be a violation of the LawOfDemeter to hardwire the source of "print".
Are you Andy Rooney?
Yeah, I am the Andy Rooney of software engineering.
Re: Fix my language if you don't like it. It is a round-a-bout function in OO clothing. The bloat builds up over time.
You use a.b(c) in place of b(a, c), where 'b' operates on 'a' and 'b' is logically related to 'a' and NOT 'c'.
This is not the variation being described in that context. That section was discussing OO variation number 3 above, not 1 and 2.
I do not see how YAGNI applies here. Are you saying that you are not gonna need that DOT? Come on. Moreover, readability is increased by the 'dot'. It says that 'b' belongs to 'a' and not 'c'. Operation is more explicit than b(a, c) where you have to guess if 'b' is actually related to 'c' or 'a'. NOTE: By LawOfDemeter, 'b' must be allowed to change only 'a' ('b' is logically a part of 'a') and not 'c'. Since b(a, c) does not make it clear, a.b(c) makes it very clear.
In the TrollFest? of this page, somebody (EricHodges) proposed a funny refactoring from:
x= foo.bar(a) + foo.bar(b) + foo.bar(c) + foo.bar(d)to
x= foo.barAndAdd(a,b,c,d) // ... where foo now contains the splendidly written method int barAndAdd(int b, int c, int d, int e) { return bar(b)+bar(c)+bar(d)+bar(e); }Wow !!! What a feat. And the person who wrote this was calling the other person a troll. That might be, but if the above was not a troll, well it was a sheer display of confusion and/or incompetence.
Therein lies the epitome of ObjectOrientedPsychologicalMismatch?: people are getting so confused with all these classes, objects, refactorings, and now YAGNI + XP + agile and stuff that they forgot to apply common sense to writing programs. The psychological mismatch happens especially among that part of the OO population that devoured DesignPatterns, Refactoring, and all that jazz, before learning the basics of programming, therefore they ended up more confused than enlightened about what software is all about. And when people can't discuss software without having a common ground on the basics of it a TrollFest? is sure to happen.
Boys and gals, we have news for you: you can throw out the window all those fancy-shmancy pattern books of yours and replace them with only one: StructureAndInterpretationOfComputerPrograms. It's also available on-line for free.
You need to learn first how to program at least half-way decently, it ain 't that difficult unless you keep distracted of those fancy patterns and refactorings. Before you can engage in TrollFest? on how ObjectOriented subsumes procedural programming through the use of static methods you need to learn first what procedural programming is. Try to start with StructureAndInterpretationOfComputerPrograms, it will do you much good, you'll even learn more about objects and abstractions than you ever had a chance to learn from your favorite OO book.
If you feel some pain reading SICP you might want to try to ease yourself into the subject with the wonderfully written HowToDesignPrograms (http://www.htdp.org) which has such fine guidelines for when to use auxiliary functions, how to introduce variables, and all the basics.
I don't get it. What's wrong with refactoring all those calls to foo into foo's class? From all the available evidence, that's a better place for them. -- EricHodges
It was an example that showed how many x.y() calls in a row can add up to visually more complicated code. It could by different classes and methods being called, not just the same ones. Beyond that, we would probably need more realistic examples to examine.
To cut a long story short the code for barAndAdd is brittle and ugly looking. What if the next client want to add only bar(x) + bar(y) + bar(z)? What if another client will want to do bar(x[1])*bar(x[2])*bar(x[3])*bar(x[4])*bar(x[5])?
You just created a method in Foo that is directly tied to the client who wanted to do bar(a) + bar(b) + bar(c) +bar(d) so you coupled the Foo beyond belief when all Foo needed to provide was the bar method. Now if a client wanted to simplify an expression like bar(a)+bar(b)+ bar(c)+ bar(d) or any similar expression, then the Foo class is the wrong place where to put that simplification.
As a matter of fact decent languages already have the simplification and generalization ready made for you, in the standard library. If you wanted to apply Foo.bar to a list of value, the wrong solution is to add that code to Foo. Because you already have a generic piece of code: List.map function list which applies the function parameter to all the values in the list yielding a list with the results. If you want to apply a binary operator to all the elements in the list you have List.fold_left operator start list. For example the function that returns the sum of elements in a list can be written List.fold_left (+) 0 where the operator is (+) and the start element is 0. So if the client wants to write foo.bar(x1)+foo.bar(x2)+ foo.bar(x3) + foo.bar(x4) + foo.bar(x5) in a slightly more elegant way he can say: (List.fold_left (+)) 0 ( List.map Foo.bar [x1;x2;x3;x4;x5]).
Now the only responsibility in the Foo part of code is to provide the bar function and nothing else. It is entirely fruitless to clutter the Foo module or class or namespace or file or whatever you have in your favorite language with the function
barAndAdd(int,int,int,int)for the sole purpose of allowing a client to simplify a trivial expression like Foo.bar(a) + Foo.bar(b) +Foo.bar(c) + Foo.bar(d). Now we tied the Foo to the fact that client has precisely four values that it will pass to bar, and that the results will be combined in a precise way. Anything at all changes in the needs of the client and either the barAndAdd becomes useless or it has to be changed as well, you can't have coupling worse than that.
The only way in which Foo.barAndAdd can be justified as a piece of code is if barAndAdd reflected an operation of particular significance in the context of Foo so that it will always have 4 values and the addition bar(x1)+bar(x2)+bar(x3)+bar(x4) represented something really important. But that wasn't how that discussion evolved, and in any case then the operation should be called Foo.doSomeImportantComputation(int x1, int x2, int x3, int x4), while the name Foo.barAndAdd clearly reflects the intention of chaining the bar function and the addition operator. Well, it should never be the responsibility of particular classes to do such stuff.
To go to the root of that part of the troll, the claim that: Foo.bar(a) + Foo.bar + ... is worse than bar(a) + bar(b) + ... had not any merit either.
To begin with, it is a non-issue in most OO languages which allow the second form as well. It is one of JavaDesignFlaws : it doesn't allow free standing functions. In other words I have to use class names + static methods volens-nolens, for example: Math.cos(x1)*Math.cos(x2)*Math.cos(x3) [nobody thought of Math.cosAndMultiply(x1,x2,x3)??]. However this is a Java (and SmallTalk) specific design problem and even Java is gonna provide a workaround next time (in 1.5).
Not Smalltalk. The Smalltalk expression is "theta sin" or "1.5 tan". It doesn't need free standing functions. The Java 1.5 workaround just saves keystrokes - it doesn't fix the problem that OO in Java is an afterthought bolted on to yet another C-like language. (Anyone who disagrees has the job of explaining wrapper classes.)
Second, when you see Foo.bar(a) + Foo.bar(b) you normally think that Foo is used as a namespace to disambiguate the name bar which potentially can be used by several modules. And in non-trivial software you'll always have some name clashes, and you will need namespaces. So Foo.bar will enhance program clarity. Even if you program in a procedural language you'll often times want to say Foo.bar() instead of just bar(), and as a matter of fact it is a common pattern in large PascalLanguage programs where Foo is an unit and the function bar can be named Foo.Bar.
Even databases (tablizers' favorite pet troll) use namespaces in order to organize their names better. So you'll see for example SELECT * FROM Accounting.Journal instead of SELECT * FROM Journal, and organizing names is so unrelated to what was discussed in the page that the whole thing should be wiped out.
All in all, MuchAdoAboutNothing? as any serious troll fest, however some accents were really fun.
I don't like those in SQL either. If such references are not needed, I don't use them. Yes, it may be a personal preferences, but many things in software engineering are.
Foo's class can still provide bar(), so adding barAndAdd doesn't limit how other classes use foo's class. A List wasn't used in the example, so I didn't introduce one for the refactoring. The point of the refactoring was that most of my method calls are within the same class and the objection to repeating the object or class name is baseless. Don't read too much into it. -- EricHodges
No, it doesn't limit how other classes uses Foo, but it clutters Foo with useless code.
If you were designing Java library, would you put Math.cosAndAdd(x1, x2, x3, x4 ) in the Math class because I don't like the aspect of Math.cos(x1) + Math.cos(x2) + Math.cos(x3) +Math.cos(x4)? I didn't think so. The solution is to fix the language design so that I can write cos(x1) + cos(x2) + cos(x3) + cos(x4). So if you provided examples in Java you had to eat your cake and acknowledge the limitation. Saying that you work around it by creating barAndAdd is nonsense.
The ugliness of Math.cos(x1) + Math.cos(x2) + Math.cos(x3) + Math.cos(x4) is a language design problem that has nothing to do with the current troll fest (which was procedural vs OO I think?).
But even in Java 1.5, when I'll be able to write cos(x) instead of Math.cos(x), I still won't be able to write
List.map( myList, cos );I'll have to write
List.map(myList, new Function() { public Object calculate(Object argument) { return cos((Double)argument)).doubleValue(); } };And once I have to compose more than one function in there it will be totally butt-ugly bloated code. Therefore your claim that you can do functional programming with static methods is kind of non-sense. Of course, tablizer's reply was non-sense as well.
I'm not writing a library. I'm explaining why the cost of object.method or class.method is not high enough to ignore Java as a language.
It doesn't matter if you're library writer or simple programmer, Java as a language is not adequate for functional programming, while your claimed was that you could do functional programming by using static methods. That's factually false.
I never made that claim. You're confusing me with someone else. -- EricHodges
I apologize for the confusion. The claim was made and implied several times on this page.
The similarity of the operations in the example was taken too literally, I am afraid. More likely the pattern would resemble:
result = cos(x) + sin(y) + tan(z) * arctan(x) + exp(x, z) ....I find that much easier on the eyes and "grok lobes" than:
result = math.cos(x) + math.sin(y) + math.tan(z) * math.arctan(x) + math.exp(x, z) ....I find it slightly easier. Java 1.5 will add support for static imports. We'll still have classes to prevent name collisions, but we won't have to type them everywhere. -- EricHodges
Java's math function library disguised as a static class is one of the several pathologies that distinguish Java from an object oriented language. What it should look like is:
result = x.cos + y.sin + z.tan * x.arctan + x.exp(z) ...-- MarcThibault
I guess I am a visual thinker. I like to create a code image that is close to the IdealMentalImage, and all the class markers interfere with that, putting large distracting space between the parts that are conceptually relevant. Maybe my head needs more RAM or something.
Perhaps. Read this: http://www.grandin.com/inc/visual.thinking.html.
So I am autistic? AspergersSyndrome perhaps, but not autistic.
Many folks categorize AspergersSyndrome as a mild form of autism. It doesn't matter what you call it. Temple Grandin thinks in pictures and has written about it. It may be useful to you. Personally I don't think visual thinking has anything to do with syntax, but who am I to judge? I think in sounds.
Perhaps most autistic or Asperger people are "visual thinkers", but that does not necessarily mean that most visual thinkers are also autistic and/or Asperger.
What sounds do the class-dot thingies make? So
x = a + b * c x = glob.a + glob.b * glob.cThese are nearly equally grokkable to you? I personally find the first greatly superior.
Dots make the "dot" sound. "glob.a" sounds like "glob dot a". Neither of your examples is clear. Are a, b and c in the same namespace as x? If so, there's no need for glob dot. If not, the first statement is meaningless.
Mathematicians have kept their variables short for hundreds of years for good reason. (They generally define and describe the longer versions elsewhere.) The "path bath" approach is going against centuries of tradition. Also, repeating "glob" over and over is a violation of OnceAndOnlyOnce. I like to factor out the bloat and repetition that distracts one from the purpose of the task. The closer the code is to pseudo-code the better in my opinion.
There should be no conflict between RDD and YAGNI (or between RDD and any other XP concept, value or practice).
IMHO, RDD is a great tool to grab a firm understanding of the problem domain of any system (problem domain discovery and exploration).
RDD also is a great tool for OO Analysis and Design because it really helps with the concepts of high cohesion and loose coupling.
I would like to see such explained in more detail, perhaps under CouplingAndCohesion.
It would seem inconsistent that the same tool is unable to determine how to assign a certain responsibility between two "candidates" (classes) and at the same time to be a great tool to attain high cohesion and loose coupling.
Besides, if RDD is related to the problem domain, it should mean that it would not be directly linked to any given aspect of the implementation of a system.
As I see it, YAGNI is more related to the implementation of a system than it is related to the problem domain of such a system.
By the way, we should try to keep things smooth and even, like having smalltalk (pun intended) among peers.
David West, in his Microsoft book, ObjectThinking, ISBN 0735619654 , says that SoftwareDevelopment is a social activity: what a great concept (I think AlistairCockburn would agree).
So is battle.
Excuses are the "not to do" part of the "There is no try, there is only do". Excuses are not associated with what drives design, but rather with what does not. A good programmer will act in such a way as to discover the difference and act accordingly.
Related: