The belief that adding classes to a design makes it more complicated.
"No," the novice said, "we should use this design instead of that one because it's clearly simpler; it has fewer classes and relationships."
(...leading to "one big massive 'do it all' class," above.)
In this example, we were using the CompositePattern
, and several novice C++ programmers kept trying to fold the collection class into the abstract base class.
- "Wouldn't it be simpler," they said, "if we just put the collection here, and then we'd have one fewer classes?"
- "But," I said, "then this big expensive collection object would exist in every leaf node, even the node for 'the value of the "age" attribute is (small integer) 25.'"
- "Ick!" they said.
- NO; a good design is generally simplified by adding classes. (You have to look at the code to see this -- not just the class diagram.)
Sometimes, when I teach, I go with student's instincts by showing them how to add what they need to the class in front of them, notice that it is bad, and then extract class. It feels like a better lesson to learn. -- MichaelFeathers
I agree. It is difficult for someone to appreciate aesthetic code without first seeing ugly code. -- MarkAddleman
Isn't this difficult for *anyone*? I know it was for me. -- PaoloPerrotta
But when the team is committed to BigDesignUpFront, it can be a problem: Once it's written into the design documents, changing it later can fall somewhere between gut-wrenching painful and impossible.
Another learning opportunity missed.
Architects don't understand the simplification
We've got this problem. In our case, it stems from the fact that our "architects" (who never see the code, only the class diagram) have the power to override our design decisions [that simplified the code by adding classes].
Question is, how do we fix this? Overthrowing the evil architects might not be possible. I'm not sure they'd be willing to do much PairProgramming
Fix it by becoming an architect. ProgrammingIsEasy. GoodDesignIsHard.
Our problem is that the architects are creating bad
designs, because they never see the code. Maybe I could become a kind of architect who works directly with the code. What title could we give to guys like that? --AdamSpitz
If the architects don't bother looking at the code, why should the coders look at the architecture diagrams? What happens if you just change the code and leave the architects to their diagrams?
Sometimes that's what we did. And sometimes the architects would notice the new classes that we created, say, "All these extra classes are making the design more complex," and go in and remove them (often breaking a bunch of our tests in the process). You could try to argue with them that our design was actually less
complex, but you couldn't convince them, because from their perspective, it was
more complex. More boxes in the picture. If the only thing you ever see is a class diagram, you develop a FearOfAddingClasses
. Or at least an aversion to it. --AdamSpitz
Go in and remove them
? Are there no tests that revert the checkin of a broken build, or at least stop it going forward? You are describing a sociopolitical issue, where two allegedly professional groups do not respect each other sufficiently to collaborate for their common good. The easiest solution to that problem is to go work somewhere else. Changing the culture in your present organization may be above your pay grade. If not then prepare to learn a lot about leadership in a relatively short time. -- SteveHolden
If you're using C++, use "class" for classes "important" enough to appear on the class diagram, and "struct" for the ones that are "just there to simplify the code". Otherwise, learn to hide your code from those who don't look at it.
Our problem is that the architects are creating
bad designs, because they never see the code.
Then you should quit, and the sooner the better. Why work in an environment like that? --RobertDiFalco
in advance... whether you code or not.
You could even go so far as to say that given that we know that we work in an industry where RequirementsChangeEarlyAndChangeOften?
the essence of a good design is one in which changes are easy to incorporate. The ideal is that when the requirements change, you 'go sit up on the mountain' thinking deep thoughts about the design, come back down and make a five minute twiddle of the design that saves months of coding. That is the Zen of Design.
There are some fairly easy principles that fall out of this, such as:
- you *are* going to need it.
- starting with Patterns is bad because Patterns have consequences and because they have a special status it is hard to get them changed or removed without being viewed as some kind of heretic.
- even if they swear black and blue that it will never have to perform the function they want removed, it is quite likely that the RequirementsWillFlipFlop?
and you will have to put the functionality back in, instead simply route around the obsolete code. ( I think that the developer shouldn't route around unneeded code, just delete it and let the SourceCodeManagement? you must be using worry about bringing it back to life when needed
Your problem may be more fundamental than you think when ArchitectsDontCode
Not looking at the code: we saw a nice example of this in a code review today. It's in one of Sun's Java API's.
There's a GUI container that holds a list of items, each of which has a mandatory string and an optional image. There are two constructors for the container. One takes no strings and no images, expecting you to use the append() or insert() methods later. The other takes an array of strings and an array of images (which may be null). The constructor checks to see if the image array is null. If it is builds a new empty array of images so that the other methods in the class don't have to make this check; otherwise it checks whether there are at least
as many references to images (any of which may be null) as strings.
Throughout this class's methods there are loops that iterate over the array of strings, but access the array of images. Clearly a new class, Item, containing a string and optional image, would make life a lot easier for the container class implementor here, but it would surely look a lot more complicated on a diagram. --KeithBraithwaite
I have seen this phenomenon. It stems from an environment where building a class is complicated from a ConfigurationManagement
standpoint. that makes it "cheaper" just to overtax the existing classes; one never writes a new class for something minor...
Java wants to have each class in its own source file (except for inner classes, of course, and non-public classes). This, plus the fact that each compiled class has its own object file, makes for directory clutter, and it bugs me. I suppose I just have to get over it:-)
Try JCreator (http://www.jcreator.com). You can write all classes in one file, when it becomes too much clutter, you just press a button and all classes are put in their own files.
Java packages help. ConfigurationManagement should help.
tools can ease the processes of a mature organization, but I don't think they help with the social and psychological aspects : people intimidated by changes in general have the additional fear of screwing up with the just press a button and all classes are put in their own files.'' -- GuillermoSchwarz
Java packages help. ConfigurationManagement should help.
tools can ease the processes of a mature organization, but I don't think they help with the social and psychological aspects : people intimidated by changes in general have the additional fear of screwing up with the tool.
Yes, when you have to file three copies of form 1729b for every class you create, not to mention having to add classes to 17 Rose diagrams, and talk to 4 PHBs for each class you create, you get discouraged.
This doesn't apply only to classes. Once I had to modify an application accessing a simple database and when I needed a schema correction or a new table I had to convince a boss and then wait several days for a DBA to do what he thought were the requested modifications.
Another reason is the tendency to just "just add one more patch here" to solve today's problem, rather than refactoring to improve the overall design.
A variation on this idea is that there is a strong impulse to add the correction at the point where the problem is found. It does take some addition time and thought after one discovers a problem to decide where one should put the solution to the problem.
What may be good for small demos might be a burden for bigger applications.
In small demos it is practical to add local class members (e.g. observers and listener) as needed, but
"...when you design practical applications, the benefit of having everything in one place becomes the burden of having too much in one place, and you find yourself more inclined to define your observers and listeners separately."
quoted from an introductory Java Course "On To Java" by Prof. PatrickHenryWinston
Many people coming from a non-OO background need convincing that adding classes is often better than making existing ones more complex.
They think in coding units as monolithic blocks, not in objects and how they can be reused.
You want new functionality that's similar to an existing functionality, you don't Refactor to add common superclasses and derive the new functionality from that, you add conditional parts in the existing code instead and parameters to decide which branch to call.
Instead of creating a new class to handle common operations, the code for the operation is included in each class that uses it as a function, etc. etc.
Does OO outright dictate creating a MirrorModel? Or is this practice merely one person's view of OOP? This to me seems like a violation of OnceAndOnlyOnce normalization, and should only be done as a last resort in my opinion.
As I write this, I am taking a short break from some refactoring. I have just finished converting Command and Handler from interfaces into abstract classes, and deleting AbstractCommand?
(pasting code from these into what were previously interfaces). Initially this went against the grain of my intuition, but I was trying to do the simplest thing and recognise that I wasn't gonna need it. When I had finished, I was really pleased with the result, much simpler, mush more intuitive. Today I have also applied the same refactoring, for the same reason, and with as much pleasure, to AbstractHibernateDao?
.... etc .
Whilst I entirely agree with the basic premise of this page, I think we should also remind ourselves to have no FearOfRemovingClasses?
:-) and to be wary of OverEngineering
. Classes that you don't need do increase the complexity of the code!
It seems to me that there's a distinction between adding classes because you recognize their need in existing code, and adding classes because you think you may need them. A lot of my Java designs had a "create an interface, abstract class, and concrete class" pattern similar to what Matthew describes, "just in case" I needed to reuse parts of the interface or implementation. I found the code got a lot cleaner when I started with just classes, then added interfaces only if I needed multiple implementations of the same interface, and added abstract classes only if multiple implementations actually shared code (and it didn't make more sense to create a common utility class). It turned out to be less work changing existing code occasionally to use interfaces/abstract classes than it was to maintain all the additional code necessitated by the first approach.
If you do TheSimplestThingThatCouldPossiblyWork
, you'll usually start out with a very small number of classes initially, then GrowClasses
to simplify complex bits. In other words, FearOfAddingClasses
is not such a bad thing until you have evidence that you need them.
That's probably self-evident for most of the people writing on this page, but I think it's an important disclaimer. :) -- PhilGroce
The naive belief that adding classes to a design makes it more complicated.
The more nodes a graph has, the more "complicated" it is generally considered. Less nodes is generally considered a good thing if complexity is as issue. I think the argument is that more classes can improve *other* factors such that the additional complexity is "worth it", not that complexity itself is not harmed.
The complexity of the design is a function of both the complexity of the class dependency graph and the complexity of the classes (the nodes) themselves. More classes can decrease the complexity of the design when the classes themselves at the same time become less complex.
The the full life cycle cost of code needs to be considered when designing software. The Architects should be creating software architectures that are an effective balance between immediate and long term needs of the organization and that almost always requires alot of compromise. -- MCaldwell
Given two functionally equivalent designs:
- one of which has few classes, with numerous conditionals handling particular cases,
- the other with numerous small classes, each representing a case, and containing as methods, the blocks of the former design's conditionals (minus conditional and other control statements)
- Control statements have been lost but classes and methods gained, however, the number of statements in blocks (statements performing useful work) has not changed. Therefore, arguably, the difference in complexity is negligible.
- The latter will always be easier to understand because:
- The latter will always be easier to extend because:
- each use of the case has been given a MeaningfulName (the method name).
- each use is no longer locked up inside some control structure, so we have LotsOfShortMethods, which are all easier to refer to.
An analogous fear, FearOfAddingTables
exists in database design, where some designers think that adding an attributes as new, smaller, normalised tables is more complicated than adding extra attributes to big, often denormalised tables. We could then, perhaps do with a 1NF, 2NF, etc. of OO.
These are classic RatioWar
s for which there is no consensus.
Lucky I wrote it on c2 then, I guess. ;). --SameAnonymousCoward?
See also: ClassicOoAntiPatterns