High-level design is the process of breaking a large system into smaller manageable pieces that can work together to solve a larger problem.
This lesson presents a quick overview of classification, the dominant approach to high-level design.
Software design is largely deivided into
CS361 emphasizes this level of design
CS330 emphasizes high-level design
CS361 emphasizes low-level design
In current practice, the primary type of “component” that we seek is the class.
The process of discovering such classes is classification, and that is what this lesson will concentrate on.
We model classes by focusing on
An attribute of a class is a data property that is conceptually a part of that class.
skinparam style strictuml
scale 1.5
class Duration {
hours: int
minutes: int
seconds:int
}
For example, we might say that an duration or elapsed time can be broken down into hours, minutes, and seconds. Shown on the right is a UML diagram for a class with those three attributes.
One possible realization of this class would be to indeed represent attributes as data members:
class Duration {
public:
int hours;
int minutes;
int seconds;
}
Most class designers would balk at this however, preferring
class Duration {
private:
int hours;
int minutes;
int seconds;
public:
⋮
int getHours() const {return hours;}
void setHours (int h) { hours = h; }
int getMinutes() const {return minutes;}
void setMinutes (int m) {minutes = m; }
int getSeconds() const {return seconds;}
void setSeconds (int s) { seconds = s; }
}
Both of these are perfectly reasonable realizations of the idea that “a duration of time has attributes of hours, minutes, and seconds”.
This is another perfectly reasonable realization of that idea:
class Duration {
private:
int secondsSinceMidnight;
public:
⋮
int getHours() const {return secondSinceMidnight / 3600;}
void setHours (int h);
int getMinutes() const {return (secondSinceMidnight % 3600) / 60;}
void setMinutes (int m);
int getSeconds() const {return secondSinceMidnight % 60;}
void setSeconds (int s);
}
This implementation might be favored if we expect to do a lot of calculations on durations (e.g., adding times together).
This demonstrates that we can have the logical idea of an attribute that does not reflect the eventual data members. All of these possible implementations are still represented by the same UML diagram.
Operations are things we do to/with an object that are conceptually more complex than simple storage and retrieval.
skinparam style strictuml
scale 1.5
class Duration {
hours: int
minutes: int
seconds:int
add(Duration otherTime)
subtract(Duration otherTime)
multiply(int scalingFactor)
}
It makes sense, for example, to add one duration to another. E.g., if I listen to a music track that takes 4 minutes, then another that takes 3 minutes and 30 seconds, then I have spent a total of 7 minutes, 30 seconds listening to music. Similarly if I say that one track takes 4 minutes and another takes 30 seconds less than the first, I should be able to compute the acutal duration of the second track by subtraction. I might also want to allow multiplication by an integer so that we could work with concepts like “twice as long as”.
In programming terms, operations will map onto public function members. (It is possible to model private members in UML, but that is generally reserved for the very late states of design.)
It’s possible to have classes that have the same attributes but different operations (and vice-versa).
skinparam style strictuml
scale 1.5
class TimeOfDay {
hours: int
minutes: int
seconds:int
add(Duration duration)
subtract(Duration duration)
subtract(TimeOfDay): Duration
}
Another idea of “time” is that of the time of day – a particular instant in time rather than a duration. The attributes are the same, but the operations would be different. It does not make any sense to add one time of day to another – you can’t add 12:30PM to 4:00AM and expect that to actually mean anything. But you might add a duration of 30 minutes to 12:30PM to figure out what time it is if you arrive for an appointment at 12:30PM and have to wait for 30 minutes.
So far we have talked about what is “inside” a single class. Relations describe important properties between pairs of classes.
skinparam style strictuml
hide empty members
scale 1.5
class TimeOfDay {
}
class Event {
title: string
}
class Duration {
}
Event -right- TimeOfDay : starts at >
Event -down- Duration : lasts >
The most basic form of relation is the association, which is simply any named relationship that we wish to discuss or focus on. We read these connections as meaning
Associations are very general, so much so that, without the label identifying what we mean by them, they would be too vague to be useful.
But when it’s time to write the labels for an association, there are a few of labels that occur so often in practice that they merit their own UML visual signal:
skinparam style strictuml
hide empty members
scale 1.5
class TimeOfDay {
}
class Event {
title: string
}
class Duration {
}
Event -right- TimeOfDay : starts at >
Event -down- Duration : lasts >
Event o- Agenda
This new relation is aggregation, and can be read as “an Agenda is part of an Event” or “an Event has a(n) Agenda”.
As a general rule, aggregation relationships could be rewritten as attributes:
This…
skinparam style strictuml
scale 1.5
class Event {
title: string
plan: Agenda
}
…and this…
skinparam style strictuml
hide empty members
scale 1.5
Event o- Agenda
…mean pretty much the same thing.
Not all attributes, however, can be rewritten as aggregation.
Sometimes we prefer to use the aggregation arrow to make the relationship stand out.
If we think it’s important to our discussion, we may want to make the relationship more visible.
Aggregation is somewhat stronger than an attribute.
skinparam style strictuml
hide empty members
scale 1.5
class UniversityIDCard {
identifies: Student
}
For example, I might be OK with this notion of a Student
being an attribute of an ID card.
skinparam style strictuml
hide empty members
scale 1.5
UniversityIDCard o-- Student
note on link
No!!
end note
But I have a real problem with the idea that a Student
is “part of” an ID card. We don’t press students flat and laminate them onto a piece of plastic!
In many real-world scenarios, we encounter pairs of classes where one class is a specialized form of the other. In programming languages, this is referred to as an inheritance relationship, and could be denoted as an association labeled as “specializes” or (in the other direction) “generalizes”, or, more simply “is a”.
For example, we might say that a Duration
is a kind of “time”, and a TimeOfDay
is another kind of time.
skinparam style strictuml
scale 1.25
class Time {
hours: int
minutes: int
seconds:int
}
class Duration {
add(Duration otherTime)
subtract(Duration otherTime)
multiply(int scalingFactor)
}
class TimeOfDay {
add(Duration duration)
subtract(Duration duration)
subtract(TimeOfDay): Duration
}
Time <|-down- Duration
Time <|-down- TimeOfDay
This diagram, for example, captures the ideas that
The Object-Oriented design philosophy states that
Every program is a simulation, and the quality of a program’s design is directly proportional to how faithfully it mimics the objects in the world being simulated and the way in which those objects interact.
Object-oriented design is all about designing software whose structure mimics the real world.
Remember, first and foremost, that classes are groups of objects and objects are things.
Look through the available documentation on the problem you are trying to solve. Talk to people who work in that world.
Look for the kinds of things (nouns or noun phrases) that get mentioned over and over in discussion the problem area.
Look through the available documentation on the problem you are trying to solve. Talk to people who work in that world.
Look for descriptions of how things interact.
Verbs and verb phrases that occur repeatedly in any discussion about the problem are suggestive of operations, particularly when they describe something that is happening or changing.
“Next we schedule the meeting at the agreed-upon time.”
“We compute the total time of all the tracks onthe album.”
Verbs and verb phrases that describe how things are rather than howthey are changing may be suggestive of relations and/or attributes.
“The meeting starts at a specified time and lasts for a predetermined amount of time.”
“The duration of a meeting can be expressed in terms of hours, minutes, and seconds.”
Example: We have been tasked with automating the means by which public library patrons find books in the library.
We “know” that we will be using a database to permit searches for books.
Nonetheless, it is a mistake to design a LibraryDatabase
class.
Because no one walks into a library, sees a large box labeled “Database”, and says “Ooo, let me look at that.”
Instead we would design a Catalog
class.
Because many generations of library patrons have walked in to a library, moved to the “card catalog”, and searched though it for relevant books.
It is entirely possible that the data structure and algorithms used for low-level design of the Catalog
will be that same database, but that’s a hidden, private decision.
From examination of the physical catalog, we learn that traditional searches have been supported by author, by title, and by subject keyword.
This informs our interface design for the Catalog
class in a way that might not have been obvious if we jumped directly in with a Database
.