Software Construction Assignment: Assignment 2 (Worth 200 Points)
NDSU CSCI 717 Software Construction
Class Design
Textbooks
Bertrand Meyer. Object-Oriented Software Construction, 2nd Edition, Prentice-Hall PTR, 1997. Chapters 22 and 23.
Steve McConnell. Code Complete: A Practical Handbook of Software Construction. 2nd Edition. Microsoft Press, 2004. Chapter 6.
2
Outline
Finding Classes from Requirements Document
Heuristics for Finding Classes
How Many Arguments for a Feature?
Class Size: The Shopping List Approach
3
Finding Classes
Central decision in OOSC
It takes talent and experience, not to mention luck.
You should not expect too much – expecting infallible recipes for finding classes is unrealistic
Motivation of a methodological discussion
Indicate some good ideas
Draw your attention to some illuminating precedents
Alert you to some known pitfalls
Selection technique
What to consider
What to reject
4
Finding Classes from Requirements
The elevator will close its door before it moves to another floor.
Can you find any classes?
5
Nouns & Verbs in Requirements Doc
Function-oriented design
Concentrate on the verbs – actions
Move, close
Object-oriented design
Underline the nouns – objects
Elevator, Door, Floor – voila
Danger!
Requirements in a natural language are open to nuance, personal variation and ambiguity
6
Avoiding Useless Classes
The nouns method covers some classes of the final design.
The method includes many “false alarms” also
Concepts should not yield classes
Do we need class Door?
7
Do We Need Class Door? No
The only relevant property: opened/closed
It suffices to have query/commands in Elevator
door_open: BOOLEAN;
close_door is
…
ensure not door_open
end;
open_door is
…
ensure door_open
end
8
Do We Need Class Door? Maybe
The notion of door may be important enough to justify a separate class.
Do not expect grammatical criteria to be of more than superficial help. Turn instead to the ADT theory
Theory of ADTs – helps ask customers the right questions
Is “door” a separate data type with its own clearly identified operations, or
Are all the operations on doors already covered by operations on other data types such as Elevator?
9
Do We Need Class Floor?
Floors are definitely an important data abstraction for an elevator system.
What properties?
10
Is a New Class Necessary?
Another example of a noun which may or may not give a class in the elevator example is floor.
Unnecessary - floor properties may be entirely covered by those of integers.
Floor number, distance between two floors
Appropriate - significant operations not covered by those of integers
Some floors may have special access rights defining who can visit them
Need access rights and associated procedures
More properties: subtract/compare two floors, but not to add or multiply them.
11
Is a New Class Necessary?
A class does not just cover physical "objects" in the naïve sense. It describes an abstract data type a set of software objects characterized by well defined operations and formal properties of these operations.
A type of real world objects may or may not have a counterpart in the software in the form of a type of software objects a class.
When you are assessing whether a certain notion should yield a class or not, only the ADT view can provide the right criterion: Do the objects of the system under discussion exhibit enough specific operations and properties of their own, relevant to the system and not covered by existing classes?
12
Is a New Class Necessary?
The aim of systems analysis is not to "model the world". This may be a task for philosophers, but the builders of software systems could not care less, at least for their professional activity
The task of analysis is to model that part of the world which is meaningful for the software under study or construction. This principle is reinforced by the ADT approach - objects are only defined by what we can do with them
Principle of Selfishness - If an operation or property of an object is irrelevant to the purposes of the system, then it should not be included in the result of your analysis however interesting it may be for other purposes
For a census processing system, the notion of PERSON may have features mother and father; but for a payroll processing system which does not require information about the parents, every PERSON is an orphan
13
Is a New Class Necessary?
If all of the operations and properties that you can identify for a type of objects are irrelevant, or are already covered by the operations and properties of a previously identified class, then that object type itself is irrelevant: it must not yield a class.
This explains why an elevator system might not include FLOOR as a class because (as noted earlier) from the point of view of the elevator system floors have no relevant properties other than those of the associated integer numbers, whereas a
Computer Aided Design system designed for architects will have a FLOOR class since in that case the floor has several specific attributes and routines.
14
ADT View as the Right Criterion
Do the objects exhibit enough specific operations and properties of their own, relevant to the system and not covered by existing classes?
15
Missing Important Classes
Not only can nouns suggest notions which do not yield classes: they can also fail to suggest some notions which should definitely yield classes
Suppose a requirements has a following sentence: “A database record must be created every time the elevator moves from one floor to another.”
Noun “record” suggests Database-Record class
Any class missing?
16
Missing Important Classes - cont
We may totally miss a more important data abstraction: the notion of a move between two floors.
Move between two floors is important which could be of the form-
class MOVE feature
initial, final: FLOOR; -- or INT if no Floor class
record (d: Database) is …
… other features …
end
17
Why Are Classes Missing
Flexibility and ambiguity of human language
Move is an important class, which a grammar based method would miss because of the phrasing of the above sentence.
Of course if the sentence had appeared as
A database record must be created for every move of the elevator from one floor to another.
Here move would have been counted as a “noun” and would have yielded a class.
Therefore, some crucial abstractions may not be directly deducible from the requirements
18
Why Are Classes Missing
Another reason for overlooking classes is that some crucial abstractions may not be directly deducible from the requirements.
Given the following requirement for text editor program: “The editor must allow its users to insert or delete a line at the current cursor position.”
Naïve designer may his/her attention to the trivial notions of "cursor" and "position" while missing the command abstractions (line insertion and line deletion).
19
Why Are Classes Missing
A third major cause of missed classes, a strategy that uses requirements document as the basis for analysis often overlooks reuse
Some abstractions are likely to be found in existing software, not in the reqs doc for a new project.
Existing software can and should influence new developments.
When faced with a new software project, the object oriented software developer does not accept the requirements document as the alpha and omega of wisdom about the problem, but combines it with knowledge about previous developments and available software libraries.
If necessary, he/she will criticize the requirements document and propose updates and adaptations which will facilitate the construction of the system; sometimes a minor change, or the removal of a facility which is of limited interest to the final users, will produce a dramatic simplification by making it possible to reuse an entire body of existing software and, as a result, to decrease the development time by months.
The corresponding abstractions are most likely to be found in the existing software, not in the requirements document for the new project.
20
Discovery and Rejection
Lesson 1: Do not put too much trust in a requirements document; do not put any trust in grammatical criteria.
Lesson 2: Eliminating bad ideas as important as finding good ones
Concepts may initially appear promising but end up not justifying a class of their own.
We need criteria for rejecting candidate classes
21
How to find classes?
"How to find the classes" means two things:
How to come up with candidate abstractions?
How to unmask the inadequate among them?
These two tasks are not executed one after the other; instead, they are constantly interleaved. Like a gardener, the object oriented designer must all the time nurture the good plants and weed out the bad.
Class Elicitation Principle
Elicitation is a dual process: suggestion, rejection.
22
Danger Signals
Signs of a bad choice of class, not proof!
The grand mistake – designing a class that isn’t
Principle of OO Software Construction is to build modules around object types, not functions - each class corresponds to a meaningful data abstraction
“This class prints the results”
“This class parses the input”
“This class does …”
Writing a module as class ... feature ... end does not make it a true class; it may just be a routine in disguise.
A class is not supposed to do one thing but to offer a number of services on objects of a certain type.
23
Danger Signals - cont
Imperative class names
A verb in the imperative or infinitive, e.g. Parse or Print class names should catch your attention
Either its name is wrong or it ‘does one thing’
Occasionally you may find that the class is right. Then its name is wrong. This is an "absolute positive" rule
Class name rule
A noun, possibly qualified.
An adjective (only for a deferred class describing a structural property), e.g. Comparable
Possible exception
Command classes – action abstractions
Stick to the rule – LineDeletion vs DeleteLine
24
Danger Signals - cont
Single-routine classes
Only one exported routine, possibly calling a few non-exported ones.
Inheritance should be considered
Possible exception: interactive command
Premature classification
Instances vs heirs
SanFrancisco and Houston inherit from City
Rather have the SanFrancisco and Houston as names of instances of City.
25
Danger Signals - cont
No-command classes
Classes with no routines or Queries routines only without commands
Need to probe
A class may describe non-modifiable objects – e.g. obtained from the outside world (sensor)
Some classes are meant for encapsulating facilities e.g. constants, Java String
26
Danger Signals - cont
Mixed abstractions
Another sign of an imperfect design is a class whose features relate to more than one abstraction.
EmployeeCensus ‘is-a’ ListContainer
Meilir PageJones uses the term connascence (defined in dictionaries as the property of being born and having grown together) to describe the relation that exists between two features when they are closely connected, based on a criterion of simultaneous change: a change to one will imply a change to the other.
You should minimize connascence across class libraries; but features that appear within a given class should all be related to the same clearly identified abstraction.
Class consistency principle: all the features of a class must pertain to a single, well-identified abstraction.
27
The Ideal Class
Clearly associated abstraction
Name: noun or adjective,
Adequately characterizing the abstraction.
Class represents a set of run-time objects
Some meant to have only one instance are acceptable
Queries for finding out properties of an instance.
Commands for changing the state of an instance.
Abstract properties can be stated, describing:
How various queries relate to each other (invariant)
Under what conditions features are applicable (preconds)
How commands affects query results (postconds)
28
General Heuristics for Finding Classes
Three broad categories of classes:
Analysis class: describes a data abstraction directly drawn from the model of the external system.
Plane in a traffic control system,
Paragraph in a document processing system
Part in a inventory control system.
Implementation class: describes a data abstraction introduced for the internal needs of the algorithms in the software
LinkedList or Array.
Design class: In-between, a design class describes an architectural choice
Design patterns
29
29
implementation classes and design classes belong to the solution space
analysis classes belong to the problem space.
Analysis classes and design classes describe high-level concepts
Other Sources of Classes
Previous developments
As you write applications, accumulate classes to facilitate later developments.
Adaptation through inheritance
An existing class does not exactly suit present need; some adaptation may be necessary.
Make sure to preserve clients as per Open-Close principle.
Evaluating candidate decompositions
Do they constitute autonomous, coherent modules?
Do they have too much communication with others?
Hints from other approaches (e.g. non-OO: ER modeling)
Use cases
CRC cards
30
30
CRC (Class, Responsibility, Collaboration): designers discuss potential classes in terms of their responsibilities and how they communicate.
Sources of Class Ideas
| Source of Ideas | What to look for |
| Existing libraries | Classes that address needs of the application or describe concepts relevant to the application. |
| Requirements Document | Terms that occur frequently. Terms to which the text devotes explicit definitions. Terms not defined precisely but taken for granted throughout the doc Disregard grammatical categories |
| Discussions with customers/ future users | Important domain abstractions Specific jargon of the domain. Conceptual and material objects. |
31
Sources of Class Ideas - cont
| Source of Ideas | What to look for |
| Doc for other systems in the same domain | Important abstractions of the domain Specific jargon of the domain. Useful design abstractions |
| Non-O-O systems or system descriptions | Data elements passed as arguments between various components Shared memory areas Important files. Record/structure types Entities in ER modeling. |
32
Sources of Class Ideas - cont
| Source of Ideas | What to look for |
| Discussions with experienced designers | Design classes successfully used in previous developments of a similar nature. |
| Algorithms and data structure literature | Known data structures supporting efficient algorithms. |
| O-O design literature | Applicable design patterns |
33
Good Class Interfaces
Important step in creating high-quality classes is creating a good interface.
Consists of creating a good abstraction for the interface to represent and ensuring that details remain hidden behind the abstraction.
Command-Query Separation Principle
Good Abstraction
Good Encapsulation
How Many Arguments for a Feature
Class Size
Dealing with Abnormal Cases
34
Command-Query Separation Principle
Some side effects are harmless and necessary
Functions modify the state (affecting visible features) but then restore the original state during execution.
Change the state of the object only affects properties invisible to clients.
Foolish to dismiss side-effect-full style as thoughtless
Abstract side effect
A concrete side effect that can change the value of a non-secret query
CQSP - only prohibits abstract side effects
Brings referential transparency back
Yields a clean style of design - simple and readable
35
Good Abstraction
Be sure what abstraction the class is implementing
A class interface provides an abstraction of the implementation that’s hidden behind the interface
A classes routines should be cohesive group sharing a common responsibility.
Example of a good abstraction of a class
Employee class containing data describing employee’s name, address, phone # and so on. It would offer services to initialize and use an employee.
Every routine is working towards a consistent end.
36
Good Abstraction: Employee
Class Employee {
public:
// public constructors and destructors
Employee();
Employee(FullName name, String address,
String workPhone, String homePhone,
TaxId taxIdNumber,JobClassification jobClass);
Virtual ~Employee();
//public routines
FullName GetName() const;
String GetAddress() const;
String GetWorkPhone() const;
String GetHomePhone() const;
TaxId GetTaxIdNumber() const;
JobClassification GetJobClassification() const;
…
}
37
Good Abstraction
Present consistent, cohesive level of abs.
Each class should implement one and only one ADT
If a class implements more than one ADT, it is time to decompose the class so each class represents one ADT.
Example of a class Interface with Mixed Levels of Abstraction
The class is presenting two ADTs: an Employee and a ListContainer. Also, the test for inheritance fails
“EmployeeCensus” is a ListContainer” does not make logical sense
The fact container class is used should be hidden from rest of the program.
38
Example of a class Interface with Mixed Levels of Abstraction
class EmployeeCensus: public ListContainer
{
public:
void AddEmployee(Employee emp);
void RemoveEmployee(Employee emp);
Employee NextItemInList();
Employee FirstItem();
Employee LastItem();
private:
.......
}
39
Abstraction of these routines is at “employee level”
Abstraction of these routines is at “list” level
class EmployeeCensus
{
public:
void AddEmployee(Employee emp);
void RemoveEmployee(Employee emp);
Employee NextIEmployee();
Employee FirstEmployee();
Employee LastEmployee();
private:
ListContainer m_EmployeeList;
}
40
The abstraction of all these routines is at “employee” level
That the class uses the ListContainer library is now hidden
Good Abstraction
Provide services in pairs with their opposites
Check each public routine to determine whether you need it’s complement.
Turn on/off, activate/deactivate, …
Move unrelated information to another class
If half the routines use half the data and other half routines use other data, then it is time to decompose the class into two separate classes.
41
Good Abstraction
Make interfaces programmatic rather than semantic when possible
Each interface has a programmatic part as well as a semantic part.
Programmatic parts – data types, other interface attributes
Semantic parts – “RoutineA must be called before RoutineB”. They are not enforceable by compiler.
Semantic part should be documented in comments.
Look for opportunities to convert semantic part to “Assert” statements (programmatic part).
42
Good Abstraction
Beware of erosion of interface’s abstraction under modification
Extending and modifying the class might require additional functionality.
The additional functionality may not necessarily belong the class.
Employee class evolving to become something like in the next slide
No logical connection between zipcode and employee
No logical connection between phone # and employee
SQL queries details are at a much more lower level of detail than Employee and should not be here.
43
Employee: Eroding under Maintenance
Class Employee {
public:
…
//public routines
FullName GetName() const;
Address GetAddress() const;
PhoneNumber GetWorkNumber() const;
…
bool IsJobClassificationValid(JobClassification jobClass);
bool IsZipCodeValid(Address address);
bool IsPhoneNumberValid(PhoneNumber phoneNumber);
SqlQuery GetQueryToCreateNewEmployee() const;
SqlQuery GetQueryToModifyEmployee() const;
SqlQuery GetQueryToRetrieveEmployee() const;
…
}
44
Good Encapsulation
Abstraction helps to manage complexity by allowing you to ignore implementation details
Encapsulation is the enforcer that prevents you from looking at details even if you want.
Without encapsulation, abstraction tends to breakdown.
Both are complementary and necessary. You can not have just one of them.
45
Good Encapsulation
Minimize accessibility of classes and members
Favor the strictest level of privacy that is workable
What best preserves the integrity of the abstraction?
E.g. Don’t put a routine into the public interface just because it uses only public routines
Don’t expose member data in public
Avoid putting private impl. details into the interface
Don’t make assumptions about the clients, unless documented in the interface
A class should be designed to adhere to the class contract implied in class interface
Favor read time convenience to write time convenience
Code is read (used) far more times than it’s written.
Speeding up code writing at the expense of well thought out good code is false economy. Example, not using Object type as a parameter in equals( ) method.
46
Good Encapsulation
Be very wary of semantic violations of encapsulation
Not calling Class A’s InitializeOperation( ) routine because you know that Class A’s PerformFirstOperation( ) routine calls it automatically.
Not calling Class A’s Terminate( ) routine because you know that ClassA’s PerformFinalOperation( ) routine has already called it.
The problem with above examples is that client code is dependent not on the class’s public interface instead on class’ private implementation.
47
Good Encapsulation
Watch for Coupling that’s too tight
Minimize accessibility of classes and members
Avoid friend classes, because they are tightly coupled
Make data private rather than protected in the base class
Avoid exposing member data in a class’s public interface
Be wary of semantic violations of encapsulation
48
Design and Implementation Issues
49
Containment (“has-a relationship”)
The idea that a class contains a primitive data type or object
Implement “has-a” through containment
Employee “has-a” name, “has-a” phone number ….
Accomplished by making them data members of Employee class
Implement “has-a” through private inheritance as a last resort
Privately inheriting from the contained object. (supported in C++ not in Java)
To allow containing class to access protected members functions and data of the class it’s contained.
50
50
Containment (continued)
Be critical of classes that contain more than about seven data members
7± 2 is the number of discrete items a person can remember while performing other tasks
If a class contains more than 7 data members, consider decomposing into number of smaller classes
51
Inheritance (“is-a” relationship)
The idea that one class is a specialization of another class
The purpose is to create simpler code by defining a base class that specifies common elements of two or more derived classes
Common elements – data members, routines, data types
Inheritance helps to avoid repeat the code and data in multiple locations
52
Inheritance
When you decide to use inheritance, need to consider the following decisions
For each member routine,
Will it be visible to derived class?
Will it have a default implementation?
Will the default implementation be overridden
For each data member (variables, constants, enumerations ..)
Will the data member be visible to derived classes?
53
Inheritance
Ins and outs of making decisions
Implement “is-a” through public inheritance
New class is a specialized version of older class
Derived class should adhere completely to the same interface contract defined by base class.
If derived class is not going to adhere to the contract the inheritance is not the right choice, consider containment.
Design and document for inheritance or prohibit it
If a class isn’t designed to be inherited from, make the members non-virtual in C++, final in java
54
Inheritance
Adhere to the Liskov Substitution Principle (LSP)
“You shouldn’t inherit from a base class unless the derived class truly “is-a” more specific version of the base class” – Barbara Liskov
Subclasses must be usable through the base class interface without the need for the user to know the difference
All routines defined in base class should mean the same thing when used in the derived class
55
Inheritance
Be sure to inherit only what you want to inherit
Inherited routines come in three basic flavors:
Abstract overridable routine – derived class inherits the public interface only no implementation
Overridable routine – derived class inherits public interface and default implementation but allowed to override
Non-overridden routine – derived class inherits public interface and default implementation and not allowed to override.
If you want to use a class implementation but not it’s interface then consider containment.
56
Inheritance
Don’t override a non-overridden member function
Don’t reuse names of non-overridden base class routines in derived classes
Move common interfaces, data, and behavior as high as possible in the inheritance
Be suspicious of classes of which there is only one instance (Singleton is an exception to this rule)
Be suspicious of base classes of which there is only one derived class
The best way to prepare for future sub-classes is not to design extra layers of base classes that might be needed some day.
Do not create any more inheritance structure than necessary.
57
Inheritance
Be suspicious of classes that override a routine and do nothing inside the derived routine
A Cat class containing a scratch() routine.
Creating a new class – ScratchLessCat that is a sub-class of Cat. But as ScrachLessCat can’t scratch, so overriding scratch( ) routine to do nothing.
Violates abstraction presented in Cat class by changing interface’s semantics
Difficult to maintain such code
58
Inheritance
Avoid deep inheritance trees
Limit inheritance hierarchies to 7± 2 total number of sub-classes of a base class
Associated with increased fault rates
Increases complexity
Prefer polymorphism to extensive type checking
switch(ShapeType s)
{
case Circle:
s.drawCircle();
break;
case Square:
s.drawSquare();
break;
…………
}
59
Opportunity for object orientation for polymorphism
59
Inheritance
Make all data private not protected
Breaks encapsulation if client can access directly protected members.
If derived class really needs base class’s attributes, provide protected access functions instead.
60
Summary of when to use Inheritance?
If multiple classes share common data but not behavior, create a common object that those classes can contain
If multiple classes share common behavior but not data, derive them from a common base that defines the common routines
If multiple classes share common data and behavior, inherit from a common base class that defines the common data and routines
Inherit when you want the base class to control your interface; contain when you want to control your interface.
61
Member functions and Data
Keep the number of routines in a class to as small as possible
Disallow implicitly generated member function and operators you don’t want
Do it by declaring function or operator as private
Minimize the number of different routines called by a class (fan-out)
Minimize the indirect routine calls to other classes
Minimize the extent to which a class collaborates with other classes
Minimize number of kinds of objects instantiated
Number of different direct routine calls on instantiated objects
Number of routine calls on objects returned by other instantiated objects
62