Let me tell you a story about a simple sailboat company...
The owner rents out boats to tourists, charging them by the amount of time they use the boat. After many happy years of low technology, he decides to automate his office in an effort to maximize his profits. So he seeks the company's first "IT solution": What's the average rental time for my boats?
In comes his friend, the UNI grad who took CS 2530.
Let's model a rental session.
Each session i has a start time, s(i), and a finish time f(i). Let's record those. Whenever you want to know the average rental time, we can compute it this way:
n Σ ( f(i) - s(i) ) i=1 -------------------- n
The owner, feeling a little concerned about his first foray into high technology, hires some outside help.
In comes the big-city "expert", a consultant who specializes in "advanced IT solutions".
But that requires us to pair up the f(i) and s(i) values.
This means we have to hold the s(i) value and wait for the corresponding f(i) value. That means we have to defer the computation, storing values in arrays. But how do we know which is which?
But we can use a mathematical trick.
The formula above is equivalent to this one:
n n Σ f(i) - Σ s(i) i=1 i=1 -------------------- n
This model simplifies the task and optimizes the computation. Now we can add start times to one running counter and finish times to another. At the end of each day, we can report the average rental time with a simple calculation.
The client is impressed and asks us to implement the consultant's suggestion. It works. The client loves the consultant, and us!
Then comes the inevitable second request: What's the longest rental time?
The client really does love us, so he drops his request and asks for something simpler: Has anyone ever rented a boat for more than an hour?
The result: we lose a client.
The problem is that the design violates the Principle of Continuity:
A change that is small in the business sense should be small in the program.
There should be a continuity between the problem domain and the solution domain.
One of the goals of object-oriented programming is to achieve such continuity by modeling the problem domain more closely.
Do you think a concert violinist listens to music, reads a book,
and then steps onto stage to perform?
You can learn to write programs programs by studying programs, and learning why they work the way they do. The same is true for design. But that isn't enough. As the above quote from Richard Gabriel reminds us, concert violinists are good because they play violin a lot. It's no wonder they are good. Professional writers are always writing. Professional programmers are always programming.
The best way to learn how to design programs is to design programs and study the results.
You and I are contractors who just won a bid to design a custom coffee vending machine for the employees of Acme Fijet Works to use. Arnold is the owner of Acme Fijet Works and, like many software consumers, eschews standard solutions. He wants his own custom design. He is also, however, a cheapskate. Arnold tells us he wants a simple machine. All he wants is a machine that serves coffee for 35 cents, with or without sugar and with or without cream. That's all. He expects us to be able to put this little machine together quickly and for little cost. We decide that the machine will consist of:
Design the program that runs the machine using objects. What are the components? What are their responsibilities? And how do they collaborate to deliver this simple service:
Kim puts in a quarter and a dime and then selects a coffee.
Students work in teams to identify objects and then write up their design on a single transparency. What constitutes a design? We examine a few as a class... Discuss ambiguities in the class diagram, the lack of dynamic information about the system, the lack of detail in the responsibilities.
This is the sort of design students often create:
Is that enough information for us to begin implementing a program?
Designing any system requires:
This is true of all design. In object-oriented design, we use objects as our components.
In OOP, we focus on responsibility and behavior first because:
Here is a simple object diagram for the design outlined above:
We have seen object diagrams before. They are useful for showing the static features of a design. What about the dynamic behavior of the system?
Here is a simple diagram to show dynamic behavior for the initial scenario, an interaction diagram:
In the interaction diagram, time flows from top to bottom. Directed lines indicate messages, and the labels on the lines indicate the name or type of message being sent.
What does the interaction diagram show us that an object diagram cannot?
A list of objects and responsibilities is not enough to document the result of our design process. They aren't enough information for us to record during the process of designing, either. We use several tools to design systems and to document the result. Among them are:
We build our design by working through a set of specific scenarios that describe how the system should work. The scenarios help us to identify objects and responsibilities. We document them with interaction diagrams.
Or, Arnold Visits the Shop.
After five machines are installed and have been operating for a while, Arnold comes along and says, "I would like to add chicken soup, at a price of 25 cents, to every machine. Change the software."
He gives us a machine with one more button for chicken soup and one more dispenser for instant soup powder.
How do you change your software design?
We might only make a couple of changes:
Here is an object diagram for the new design:
And here is a new interaction diagram:
In what ways is this design better than our first design, if any?
Arnold changed the system's requirements after we wrote our program, either though he wanted just one thing, simple and cheap. That's not unusual. In fact, it's the way things usually go. Their business needs change. Their clients change. The environment in which they work changes.
But it's more than that. An adage of software development is:
Clients don't know what they want until you don't give it to them.
That's because they see what you do give them, and only then do they begin to understand what is possible. They learn to dream bigger.
Shouldn't we want to satisfy dreams?
Another adage of software development says
Change is unpredictable, but we can try to predict change.
By considering different scenarios, we guide the evolution of a design that is more flexible, more evenly balanced among its collaborators. Most designers find that it is easier to grow a design than to create a finished design from scratch.
There is a tension... predicting change versus designing too much too soon...