Toolshed Technologies
Andy Hunt
Musician, Author, Programmer
Andy's Guide to OO Development
—Andy Hunt
07/01/1998
Published in Toolshed Newsletters
Welcome to Andy’s Guide to Object Oriented Development (1998).
These are bits and pieces of methods both formal and informal that I have picked up over the years and found helpful in various situations. You may or may not find them helpful. If any of these ideas appeal to you, great. If they don’t, that’s perfectly okay: everyone’s brain is wired a bit differently (which is why there are have been so many published OO methods through the years). If you find a technique not at all helpful, try to figure out why, and how you would think of it differently.
The key is to arrive at method that you are comfortable with, and that adds value to your development effort.
This material is broken up into four sections:
- Effective development Habits
- A Process (or “Method”, if you prefer)
- OO Implementation guidelines
- Graphical User Interface implementation guidelines
Effective Habits
Here are some fundamental habits that I encourage others to try and adopt, regardless of paradigm.
-
Always use a Source Code Control system (RCS, CVS, PCVS, etc.) No project or team is too small not to.
-
Automate the development process. Makefiles, batch files, Perl or shell scripts should drive as much of the process as possible, including generating documentation, production, testing, etc. Manual procedures are prone to error and aren’t as cleanly reproducible.
- The VERY FIRST version of your code should: ([MEYER97])
- Have Cosmetics right. Comments, formatting, naming, etc.)
- Handle Abnormal Cases
- Handle Internationalization
- Use assertions to capture assumptions and constraints</b>
Don’t wait until later to “pretty up” the code; later may never come (see below)
-
Always maintain a working system, someone will always require a demo at the most inconvenient point.
-
Compile frequently; Execute against a regression test set of outputs (not your memory), especially before checking in code.
-
On finding an error, ask yourself:
- Is this mistake elsewhere?
- What next bug is hidden behind this one?
- How can I prevent bugs like this in the future?
-
Don’t be afraid to scrap code. There’s nothing like a hard disk crash to improve code quality.
-
If you’re not lucky enough to have your disk crash, consider refactoring your code as needed during the course of development (see [FOWLER97]). If the code needs to be re-done, don’t put it off!
-
Try to document discarded possibilities along the way. You will always ask yourself (or others will ask) “Why didn’t you do this way?”, and you may actually have had a good answer!
- In a similar vein, more generally: Live for today.
What I mean by this can be illustrated in the following example. What would happen if you were called away from the project you were working on right now, never to return. How much important detail, explanations, and plans live in your head that are not captured in comments or documentation?
When you rationalize a decision, when you leave a stub for some future development, even if you are trying out an algorithm that you don’t think will quite work: identify it as such. Because more than likely you will still be working on this project at some point in the future, but you may have neglected to write down any of that mental buzz that was in your head.
A Process
Note I did not say THE process. This is a general skeleton of a process to produce object oriented software. I do not follow it slavishly, but it is a good general road map.
The metaphor of building construction as applied to software isn’t quite right, it implies that there is a “done” phase, when people can move in and only have to clean the floors and change light bulbs.
But in truth, software isn’t like that, at least not anymore. Rather than starting from an artist’s sketch, developing blueprints, building a structure and calling it done, software today is never done. So it’s more like gardening, or raising children. Do you consider your child “done” when he or she is born? Hardly. The fun is just beginning.
So it is with software applications. Once delivered into the user’s hands, it isn’t done, it is only beginning! It is fundamentally a mistake to consider that any software project is ever “done”, ever final. Parts can be nurtured and grown, other parts may wither and die, new seeds may be planted right next to established plantings.
So bear in mind that, although the following information is provided serially, it may only happen in this order the first time (possibly not even then).
From ground zero, here’s how to start:
-
Start with a Use Case. But not just any use case. See [COCKBURN96] for a template of a semi-formal use case You can capture a lot more of the user’s requirements this way rather than using stick figures and bubbles or simple prose.
-
Use a Glossary. As you discover key concepts and terminology, document them in a glossary that can be accessed by everyone involved on the project (technical or not). Use these same, mutually agreed upon words in the code, the doc, the user’s manual; everywhere. The user’s vocabulary and the designer’s should be one and the same.
-
From the Use Case, try a first pass at a Dynamic Class Model.
Sketch out some rough classes, classifying them as one of: (see [UML-J])- Entity: Objects of this class will be passive. May outlive the use case, doesn’t initiate anything by itself.
- Control: Objects of this class will be active. Doesn’t outlive the use case, initiates some action.
- Boundary: An interface to the outside world beyond this system.
This classification can be helpful in discovering model-view-controller relationships, correct client/server partitioning, etc.
-
Trace the message flows among these roughly sketched classes. Do not delve into actual methods or arguments just yet, but try to establish rough bounds of responsibilities, checking against the Use Case to see if you can do it or not. (The idea of using the dynamic model to drive the static model appeared most recently in [ROSENBERG98]).
-
As you start the Static Class Model, and start allocating functionality amongst various classes, think of each class as follows: (see [BON95])
+-----------------+------------- | CLASS | name +-----------------+------------- | Inherits From: | Queries: | Commands: | Properties: | Constraints: +-------------------------------
Allocating methods/functions/features this way allows you to think in terms of Command-Query Separation, or CQS. Ideally, methods identified as Queries in a class are known to be side-effect free, and can be called repeatedly with no change of state in the corresponding object.
Commands of course, may change the state of the object, and may return a useful value if you desire.
Constraints can be physical constraints imposed by the system, as noted on the Use Case, or even better can be Semantic Invariants that help define the class (see my newsletter article for more on semantic invariants).
-
Implement the system along the “thin thread model”, also known as a “red thread” or a “slice”: a thread of execution, however feature-poor, that runs the system end-to-end, touching all major components (database, UI, etc.). As implementation continues, the thin thread gets fatter and fatter, adding features and functionality in each iteration. A neat twist to project scheduling based on this approach is mentioned in [MARTIN-N]:
The idea is to calculate the completion date of the project based on the length of time it takes to complete each slice and the expected number of iterations necessary to achieve the requisite functionality. This will of course start of as a wild guess, but as each slice is finished, you can continually refine the estimate based on the length of time that slice took, and the amount of functionality added. With this sort of a feedback loop, you can improve the ultimate end date estimate at each iteration. If you are going to miss the end date, you will know sooner rather than later, and can possibly do something about it.
Implementation Guidelines
-
Put documentation in the code. The closer any piece of documentation is to the code it documents, the better then chances it will be updated. Do not put in comments what can be derived from the code itself (that’s just one more opportunity to be out of sync).
-
Do NOT use Hungarian notation, or any other naming convention that attempts to encode type information in the name. That doesn’t work in OO systems (see [MARTIN] for Tim Ottinger’s naming convention suggestions; briefly “make it pronounceable” and keep it consistent with your Glossary).
-
Knowledge lives in ONE PLACE only. Never type the same thing twice; any definition, specification, enumeration, policy, strategy, etc. should have a single, definitive representation within the system. It may be necessary to transform that representation to meet the needs of the development environment; this should be done automatically as part of building the system (for example, by using a Perl script in a Makefile).
-
Design your class to honor the Open/Closed principle [OOSC2]. Another way of looking at this is that: “Change should be ADDITIVE, not INVASIVE.” [VLISS98].
-
Derived classes must be usable through the base class interface without the need for the user to know the difference. (The Liskov Substitution Principle, also part of Design By Contract [OOSC2])
-
Details should depend upon abstractions. Abstractions should not depend upon details. (Principle of Dependency Inversion, [MARTIN])
-
Use Command/Query Separation. Methods which are denoted as Queries should be side-effect free, and do not change the state of the object.
-
Tell, Don’t Ask. Tell objects what to do. Do not ask an object for state information and then send commands to the object; you may be making a decision that the object should have made. Tell the object what you want, let it figure out how to do it. Think declaratively instead of procedurally!
-
Use accessors for properties of an object. Never expose raw variables, even if the language allows it. A convenient naming convention would be to have names like this (Java example):
private int myFoo; // The property as a private variable public int foo() { return myFoo; } // the Reader, Eiffel-style or public int getFoo() { return myFoo; } // the Reader, JavaBeans-style public void setFoo(int aFoo) { myFoo = aFoo; } // the Writer
-
Provide a toString() or similar method for tracing and logging, so that all objects can be represented as a text string.
GUI Guidelines
There are many good books on User Interface design, Human Factors engineering, color theory, and so on. Unless you are well versed in these areas, don’t design a user interface. Okay, that’s a bit of a strong statement, but if you can, have a semi-technical graphic designer assist with layout and design (and possibly workflow if appropriate).
That said, here are some mechanical issues that should be addressed:
-
Allow end users to customize font size, colors, etc. Not all of us can read tiny fonts, and it’s a real drag when you can’t change them (or when changing them causes the interface to implode).
-
Allow all operations to be KEYBOARD driven as well as moused, especially for data-entry intensive applications. Touch typists dislike having to lift their hands from the keyboard.
-
All operations should participate in Undo/Redo; where possible, present some visual representation of the command history. Nothing strikes fear in a user’s heart more than an undoable operation.
-
Any pause in operation should explain what processing is occurring (not just a watch cursor).
References
- [BON95] Waldén, K. and Nerson, J-M. “Seamless Object-Oriented Software Architecture”
Prentice Hall, 1995. - [COCKBURN96] Cockburn, A. “Structuring Use Cases with Goals”
http://members.aol.com/acockburn - [FOWLER97] Fowler, M. “Refactoring”
http://www.awl.com/cseng/titles/0-201-89542-0/Refactoring.htm (Seems to have moved/been removed, but he has ongoing research in this area. The material that was posted here can be found in Martin’s book, UML Distilled (1997 Addison-Wesley) on page 30). - [MARTIN] Martin, R. “Principles of Object Oriented Design”
http://www.oma.com. - [MARTIN-N] Martin, R. “RE: Coates vs Martin; static class structure based on whole/part relations found in problem domain?!”
Netnews posting to comp.object January 12, 1998. - [MEYER97] Meyer, B. “Practice To Perfect: The Quality First Model”.
Object Technology column, IEEE Computer, May 1997. - [OOSC2] Meyer, B. “Object-Oriented Software Construction, Second Edition”
Prentice Hall, 1997. - [ROSENBERG98] Rosenberg, D. “UML Applied: Nine Steps to Incorporating UML into Your Project”.
Software Development Magazine, Vol 6, No. 3. March 1998. - [UML-J] Jacobsen, I. “UML Extension for Objectory Process for Software Engineering”
http://www.rational.com/uml/html/objectory - [VLISS98] Vlissides, J. “Subject Oriented Design”
Pattern Hatching column, C++ Report, Vol 10, No 2. February 1998.
Copyright © 1998 Toolshed Technologies, Inc.
All Rights Reserved
Keep up to date with my low-volume newsletter and don't miss another article or fresh idea:
Latest News
-
Greenfield, Brownfield... Blackfield?
July 24, 2024 -
New article: The Limits of Process
January 25, 2022 -
New article: Habits vs. Practices
January 5, 2022 - List All News...