1 ָ נן sd Object Based Design uIdentifying the Classes uFeatures Design uConst Correctness...
-
Upload
jordan-watling -
Category
Documents
-
view
218 -
download
1
Transcript of 1 ָ נן sd Object Based Design uIdentifying the Classes uFeatures Design uConst Correctness...
1ָ�
sdןנ
Object Based Design
Identifying the Classes Features Design Const Correctness Design by Contract Some C++ Tips
2ָ�
sdןנ
Steps in Object Based Design
Find the classes and name them Characterize your classes
Domain: foundation/architectural/business/application Nature: state/source/sink/view/helper/other Kind: mutable/immutable
Assign responsibilities Identify collaborators Declare protocol Define structure Establish class invariant Implement behaviour
Good object-based design is first and foremost good modular design.
Good object-based design is first and foremost good modular design.
3ָ�
sdןנ
“Find the classes, ” Arguably, the easiest step! Main heuristic:
Underline the nouns in the problem description text.
Catches: Must have a good problem description first
Requirement and analysis phases Not all nouns should be classes:
Items outside the problem boundary “Abstract” nouns, which arise from natural language Some nouns are just attributes
• Annual-Interest, Month, Dimension, Gender, ..
Synonyms
4ָ�
sdןנ
Nouns in RequirementsProblem Reporting System Requirements
[G. Booch. OODwA, ‘91, . p. 364]
Software with a reputation of being full of errors has a short life, and thus it behooves every software development organization to keep track of error reports. The problem is compounded when an organization is responsible for multiple software products, each of which may have several different versions released to the field at any time. Errors in a program may be identified through any number of different sources: end users, field-support personnel, the test and integration team, quality-assurance personnel, and developers. Once identified, errors must first be qualified (is it an error in the software, a problem with documentation, or is it simply a misunderstanding by the user?), then assigned to a responsible party, and eventually resolved (fixed, deferred, or rejected as being not reproducible). An analysis of error reports can help software managers properly allocate developmental resources and track the maturity of a product.
Our task is to develop a problem reporting system for a software development company. This system must support multiple software products as well as multiple versions of each product. Because the kinds of and versions of products will change over time, the system must be designed to support changing database requirements.
... Can you distinguish the real candidates and the attributes from the abstract nouns, external entities and synonyms?
5ָ�
sdןנ
Candidate Classes #1/5Tangible: Physical objects, or groups of objects, that are tangible.
engine, invoice, vehicle
Roles: A person who carries out some action or duty or plays a role.
student, manager, committee-member.
Based on Shlaer & Mellor, Ross, Coad & Yourdon
6ָ�
sdןנ
Candidate Classes #2/5Events: things that happen, usually at a given state or time. Look especially for historical event which must be recorded.
withdrawal, purchase, registration, meeting, wedding
Interactions: exam, loan, meeting, intersection, wedding.
7ָ�
sdןנ
Candidate Classes #3/5Places & Locations: Areas set aside for people or things. Physical locations, offices, and other relevant sites.
Organizational units: Groups to which users belong. Formally organized collections of people, resources, facilities, and capabilities having a defined mission, whose existence is largely independent of constituents:
bank, branch, department, committee.
Com puter Science
E lectrical Engineering
Exact Sciences Hum anities
Medicine
University
8ָ�
sdןנ
Candidate Classes #4/5
Other Systems: External systems or devices with which the application interacts.
Pulse monitor
Phone switchboard
Smart fax machine
9ָ�
sdןנ
Candidate Classes #5/5
Concepts: non tangible principles or ideas.Citizenship, authorization, process
Structure: “Kind of” and “Part of” relationships.Capture the relationship between objects as a class.
10ָ�
sdןנ
“... and name them.” Use capitalized identifiers (usually one word) for all class
names. Date, Time, Window, Array
Class names are typically nouns or pairs of nouns: Student, VersionNumber, PortManager
Do not use verbs! The following class names indicate design errors:
Parse, Draw, Move Exception: classes describing operation in an interactive system.
er-named classes Examples:
LexicalAnalyzer, Mover, Popper, Updater Usually a design flaw. Especially in the following cases:
An er class with many routinesAn er class with just one routineMany unrelated er classes
11ָ�
sdןנ
Item / Group Ambiguity
Source: the class/object confusion arises from the fact that the name of a group is also used for denoting a single item in it:
The cat in the hat. Kishta is a cat.
Solutions Use plurals for class names
Dates, Cats, ... Use articles for naming objects (the Smalltalk
convention):theCat, aWindow, ...
Be smart and organized:Capitalized names for classesMeaningful lower case names for objects
12ָ�
sdןנ
General Rules for Naming Pronounceable Names
If you cannot pronounce it, you cannot use it! Few words:
One word is best. Separate multiple words by
Capitalization: BinaryTreeUnderscore: Binary_TreeHyphen (COBOL style): Deposit-Slip
Do not invent abbreviations Clarity should be favoured over brevity. If you must, omit vowels and/or suffixes
msg, buff, ptr, ... No nested digits. These are easily confused
0/O 1/I 2/Z 5/S
13ָ�
sdןנ
“Characterize your classes: Domain.” There is a ladder of domains from which classes are
taken. Domains are characterized by the extent to which its
classes are applicable: Problem Space
Application Domain• One program or application.
Business Domain• One industry or company.
Program SpaceArchitectural Domain
• One implementation architecture.
Foundation Domain• Across all businesses and architectures.
[Meilir Page-Jones, What Every Programmer Should Know about OOD, Chapter 9, Dorest ‘95]
Low Reusability
MediumReusability
High Reusability
14ָ�
sdןנ
Program Space Domains Subdomains of the foundation domain:
Fundamental Integer, Boolean, Character, String, Pointer, Class, ...
StructuralStack, Queue, List, Binary-Tree, List, ...
Semantic (gives units to quantities)Date, Time, MKS, CGS, Point, Line,...
Subdomains of the architectural domain: Machine-communication
Port, Remote-Machine, File, Process, Thread, Keyboard, Mouse-Event, ...
DatabaseTransaction, Backup, Stream, ...
Human InterfaceWindow, View-Port, Dialogue-Box, Menu-Item, Command,
Radio-Button, ...
15ָ�
sdןנ
Problem Space Domains
Subdomains of the business domain: Attribute
Money, Temperature, Colour, Balance, Address, Rank, ...
RoleCustomer, Patient, Bank, Department, ...
RelationshipMember-In, Account-Ownership, ...
Subdomains of the application domain: Event-Stimulus-Recognizer
Components that recognize external events Event-Activity-Manager
Components that handle those events
16ָ�
sdןנ
“Characterize your classes: Nature.” Data Manager, Data or State: Classes used to store data.
Almost all classes. Basic Principle: All data should be managed by a class.
Container: Sole purpose is to store objects of other classes.
Data Sinks: Classes that consume data Output file, Port
Data Sources: Classes that generate data RandomNumber, FibonacciSequence
Viewer: Used to display the data of other classes. Helper, Facilitator, or Algorithm: Used for packaging
abstract operations Sorter, Parser, ...
Other: all the rest...
17ָ�
sdןנ
“Characterize your classes: Kind.”
Data Managers are of two kinds: Mutable: class whose objects may change their state during
their life time.Long life-time of objects.
Immutable Classes: class whose objects’ state can be determined only at construction.
Short life-time of objects.Objects are dynamically created and destroyed to compensate for
lack of state changes.
Immutable classes are sometimes easier to design. New instances are dynamically created if necessary Examples:
Integer, Date, String, Point, Rectangle File-Name Button, Transaction, Playing-Card Deposit, Permission-Request
18ָ�
sdןנ
“Set responsibilities.”
So far, all that our program has is:
The next step is to write “class comments”. This should describe the class responsibility.
class X {};class Y {};...
class X {};class Y {};...
// XXX...class X {};// YYY...class Y {};...
// XXX...class X {};// YYY...class Y {};...
19ָ�
sdןנ
Responsibility
Each class must have a cohesive set of responsibilities. There must be a clear separation of responsibilities
among classes. Responsibilities must be:
Short. Describe what not how. Include an active verb.
Common errors: Too much responsibility. No responsibility at all - false class. Unrelated responsibilities. Shared responsibility.
20ָ�
sdןנ
“Identify collaborators.”
Collaborators (at first iteration no need to determine exact collaboration):
Providers:Composition relationship:
• Classes of instance variables.
Inheritance Relationship (later):• Base classes.
Other relationships:• Arguments to class methods.
• Classes of Internal variables in methods.
• Classes of global variables used in methods.
Users:The users of a class C, are all classes to which C is a
provider.
Set of collaborators should be small.
21ָ�
sdןנ
Encumbrance Encumbrance of a class C: The number of direct and indirect class
providers to C. Example:
Encumbrance tends to be correlated with position in the domain ladder. Deviations should be thoroughly checked. They may contain design flaws.
Turtle6
Direction1
Pen4
Colour1
Position1
Real0
Integer0
uses
22ָ�
sdןנ
CRC Cards
4 in
6 in
CollaboratorsClass Name
Responsibilities
CRC = Class + Responsibility + Collaboration. Invented by Beck (89). Used by a group of individuals in the simulation
process. Back of card is used
for the implementation details.
23ָ�
sdןנ
“Declare protocol.”
Start from responsibility description. Kinds of features
Mutators Accessors Constructors Destructors Convertors
Design decisions: Number of features Kinds of arguments
Input/OutputOptions/Real arguments
Number of arguments Names of features
24ָ�
sdןנ
External vs. Member FunctionsMember Function External Function
CallingSyntax
z.conjugate() conjugate(z)
AccessRights
Class member Class Client(if not friend)
Creator Class Provider Class User
Immunabilityto Change
Lowif class provider is not careful toavoid using data members directly
High(if not friend)
Packaging Strongseen by all class clients
Weakmight be missed by some clients
#include "complex.h"
Complex conjugate(Complex c) {
...
}
class Complex {public:
Complex conjugate(void) {...
}
};
25ָ�
sdןנ
Kinds of Features: Mutators Nicknames: modifiers, commands, operations, procedures, etc. Naming:
verbs: insert, pop, activate, move, draw, scale, deposit, indent verbs with adverb: quick_format, left_rotate, deep_copy verbs with noun: add_color, insert_line, move_to_parent,
set_submitter, trace_rays, start_heating, display_headers, ...Do not append class name!
• stack_pop Operators names:
Utilize, not abuse familiarity with ordinary operator names. Sub-classification:
High-level: Can be implemented by using only features in the class protocol.
Low-level: Not high level.EssentialAtomic
Immutable classes should have no mutators.
26ָ�
sdןנ
Kinds of Features: Inspectors
Nicknames: examiners, accessors, queries, functions, etc. Ratio: 60%! (only 40% are mutators)
Measured in the Eiffel library (~700 classes, ~5500 features). A result of two design techniques we will study later.
Naming: substantive (noun): occurrences, size, area,
summary_of_problem noun with adjective: current_size, last_int, next_value
Naming Boolean inspectors: descriptive adjectives, sometimes prefixed with “is”.
ready (not status), full, empty is_sibling, is_last, is_leaf, is_root
27ָ�
sdןנ
Implementing Inspectors in C++
The const keyword after the function member signature effectively defines this as:
const Heater * const this; instead of
Heater * const this; The abstract (client-visible) state of the object is not
allowed to change. The compiler isn't allowed to assume a byte-wise const,
for the purpose of optimization since a non-const cast could exist.
However, with the new mutable keyword, a byte-wise constness can be assumed.
class Heater {public: //... int temperature(void) const;
};
class Heater {public: //... int temperature(void) const;
};
28ָ�
sdןנ
No Side-Effects Principle
Implementation Guide-lines: Mutators should be implemented as procedures (void
function members). Inspectors should be implemented as functions.
Rule:
Do not make a design that mixes the two. Counter-intuitive to C programmers who like to
write:
while ((c = getchar()) != EOF) { ...
}
while ((c = getchar()) != EOF) { ...
}
29ָ�
sdןנ
Rationale for No-Side Effects Principle
Clear distinction between inspectors and mutators. Exploiting reader’s background knowledge and
understanding of mathematical functions.
getchar() == '\n' ||
getchar() == '\t'
||
getchar() == ' 'is not at all the same as
iswhite(getchar())
30ָ�
sdןנ
Kinds of Features: Constructors
Nicknames: creating routines, new messages. Naming:
C++: class name
Eiffel: make Smalltalk: new
31ָ�
sdןנ
C++ Constructors Caution
Constructors with only one argument, implicitly define a conversion from the argument type to the class type.
To avoid this, use the new explicit keyword:
This will prevent the following two kinds of nonsense code:
Array v = 10;Array v = 10; int sum(Array);...int j = sum(5);
int sum(Array);...int j = sum(5);
class Array {public:
explicit Array(int len);//...
};
class Array {public:
explicit Array(int len);//...
};
32ָ�
sdןנ
Kinds of Features: Destructors
Nicknames: cast operators Naming:
C++: ~class-name Eiffel: no destructors Smalltalk: no destructors
Don't bother with them now: Destructors exist only in non garbage collecting systems. Their purpose is most frequently to aid in memory
management. This is an implementation detail, which need not be
considered at this stage of the design.
33ָ�
sdןנ
Kinds of Features: Convertors.
Naming: C++: operator Type Eiffel: no convertors Smalltalk: no convertors
The main purpose of convertors is to make an ADT complete. Their consideration could be put off to later.
34ָ�
sdןנ
Number of Features in Eiffel
0--5 6--10
11--15
16--20
21--40
41--80
81--142
Base
Vision0%
10%
20%
30%
40%
50%
60%
70%
Cla
sses
# Features149 classes
1823 features
546 classes3666 features
Incremental size of classes in the Eiffel Library [Meyer ‘94]
35ָ�
sdןנ
Observations on #Features Ranges:
Small classes (0-10):60-80%.
Medium classes (11-20)15-20%.
Large classes (20-40)5-10%.
Very big classes (more than 40):As much as 150 features.Rare.
Averages: 12.2 in base. 6.7 in vision.
Rule of thumb: 5-20 is good. 80 is probably too much.
36ָ�
sdןנ
Number of Methods (Smalltalk)
Project Description Classes Methods AVG STD
WindowBuilderPro GUI system forSmalltalk/V
64 922 14.41 22.49
HomSuite Responsibility drivenCASE tool for OOD
32 707 22.09 23.31
Smalltalk/V OS/2 OO DevelopmentEnvironment
139 2728 19.63 20.40
OOMetric Metrics AnalysisTool
79 758 9.59 28.83
Point of Sale Retail Fast food restrauntsales application
42 327 7.79 10.88
Project Description Classes Methods AVG STD
WindowBuilderPro GUI system forSmalltalk/V
64 922 14.41 22.49
HomSuite Responsibility drivenCASE tool for OOD
32 707 22.09 23.31
Smalltalk/V OS/2 OO DevelopmentEnvironment
139 2728 19.63 20.40
OOMetric Metrics AnalysisTool
79 758 9.59 28.83
Point of Sale Retail Fast food restrauntsales application
42 327 7.79 10.88
Object-Oriented Software Metrics [Lorenz & Kidd ‘95]
Observe the variety Among projects Among classes within project
37ָ�
sdןנ
Distribution of Method Numbers in Smalltalk Programs
0--5
6--10
11--15
16--20
21--40
41--80
80--
WindowBuilderProHomSuite
Smalltalk/VOOMetric
Point of Sale
0%10%20%30%40%
50%60%
70%
80%
Object-Oriented Software Metrics [Lorenz & Kidd ‘95]
38ָ�
sdןנ
How Big Should a Class be?
Each class has an absolute minimum, below which, the class is useless.
Atomic features are necessary.Nevertheless, too low level atomic features would hinder
redesign.
How much should we have beyond this? Minimalist approach (Stroustrup). Maximalist approach (Meyer).
39ָ�
sdןנ
Minimalist Approach
Extreme: Do not add anything that can be expressed in terms of atomic features.
Add only the most important gizmos. Rationale:
Cannot forecast the future: It is easy to add a feature later. It is difficult to remove a bad feature.
Easier to write and understand. Unnecessary features may be an extra baggage.
More work to implement.May never be necessary.Greater effort to redesign.Higher coupling.
Invest time in thinking, not implementing!
Appropriate for problem space classes.
40ָ�
sdןנ
Shopping List Approach
Add all features: that make an ADT complete. that may be useful to a potential user. that are not duplicate.
Rationale: Not Invented Here is a result of minimalism. In a well documented and consistent system, the effort of
reading is much less than that of writing. Essential factors are:Feature documentation.Uniform naming convention.Consistent style.Grouping of features by categories.Powerful indexing system.
Appropriate for reuse library and program-space classes.
41ָ�
sdןנ
Input and Output Arguments Arguments to methods are of three kinds:
Input Output Input and Output
Output Arguments: not common in good design. If there is only one returned value, then the method could be made into a
function that returns this value. Two or more returned values are less common. They are usually an
indication of bad design.
Input/Output Arguments: very rare. Danger Signal: An argument which serves as input to a method , and is also
changed by the same method, brings the suspicion:Maybe the method should have been sent to this argument?
(More on multi-methods later).
Typical case: Several inputs 0/1 outputs.
42ָ�
sdןנ
Implementing Input Arguments
Alternatives:Good: Pass by value:
void print(String s) {/*...*/}Better: Pointer to a constant object:
void print(const String* sptr) {/*...*/}
Best: Constant reference:
void print(const String& s) {/*...*/}Bad: Non-const reference:
void print(String& s){/*...*/}Worse: Pointer to a non-const object:
void print(String* sptr) {/*...*/}
A constant String “forgets” all methods that can change it.
43ָ�
sdןנ
Reference to Constant Object
If an argument should not be changed, then this demand must be:
obvious enforced
Reference to constant object: C++ version of input only argument declaration.
Compile-time error occurs whenever print attempts to change s.
No run-time penalty:Execution speedMemory consumption
Information may be used (in special cases) by good optimizing compilers.
44ָ�
sdןנ
Const Correctness
Just another form of type safety used with all program elements: global variables, arguments to functions, functions return values, internal variables, data members, inspectors, etc.
Type safety requires you to annotate your code with type information which isn't absolutely necessary.
You, as a programmer, know a lot of important information about your program. Type safety and const correctness are structured ways to tell the compiler about this information.
In return you get:More robust systems.
• Fewer run-time errors.More efficient programs.
“const correctness” (just like type-checking) can be time consuming and tedious!
A program is const correct if and only if: All immutable elements are declared as such. No immutable element is mutated, or equivalently,
the program compiles without any mutability related warning or error messages.
45ָ�
sdןנ
When to do it?
Making an existing program const-correct is not easy: const-correctness tends to spread very quickly. Bottom-up approach is usually better than top-down.
It is better to start thinking about const-correctness in the design phase.
void print(const String& s){
...int len = length(s); // Beware: length cannot modify s....
}
void print(const String& s){
...int len = length(s); // Beware: length cannot modify s....
}
46ָ�
sdןנ
const Objects
All members are const:
struct Rect { int top, left, width, height;
Rect *next;};// ...const Rect r;r.top = 0; // error!r.next = &r; // error!r.next->top = 0; // OK
struct Rect { int top, left, width, height;
Rect *next;};// ...const Rect r;r.top = 0; // error!r.next = &r; // error!r.next->top = 0; // OK Only the next
pointer is const !Only the next pointer is const !
Same as:
const int top, left,...
Same as:
const int top, left,...
Can only call const member functions.
47ָ�
sdןנ
Initialization of const Objects
Done once in construction time:
const Rect r1(20, 20, 100, 100);r1 = r2; // error!
const Rect r1(20, 20, 100, 100);r1 = r2; // error!
Members must be initialized before constructor body.
class Student {public: Student(int id): id(id) {}private: const int id;};
class Student {public: Student(int id): id(id) {}private: const int id;};
Which id is which?Which id is which?
Can the compiler generate operator=?
Can the compiler generate operator=?
48ָ�
sdןנ
const Member Functions
this points to a const object:
int Rect::diameter() const{
top = 0; // error! r.next = this; // error! r.next->top = 0; // OK}
int Rect::diameter() const{
top = 0; // error! r.next = this; // error! r.next->top = 0; // OK}
Members cannot be changed. Cannot call other non-const member functions. Specifically, assignment should be non-const.
49ָ�
sdןנ
Pointers and const-ness
const refers to its left hand type:
p1 = &two;p2 = &two;p3 = &two;p4 = &two;
p1 = &two;p2 = &two;p3 = &two;p4 = &two;
p
1 2
int one = 1;int two = 2;
const int *p1 = &one;int const *p2 = &one;int * const p3 = &one;const int * const p4 = &one;
int one = 1;int two = 2;
const int *p1 = &one;int const *p2 = &one;int * const p3 = &one;const int * const p4 = &one;
*p1 = two;*p2 = two;*p3 = two;*p4 = two;
*p1 = two;*p2 = two;*p3 = two;*p4 = two;
QUIZ:
Which of these statements compile successfully?
QUIZ:
Which of these statements compile successfully?
50ָ�
sdןנ
Logical vs. Physical const-ness
Default behavior is not always sufficient:
Default behavior may be too strict:
class Database { void get_data() const; int number_of_accesses;};
class Database { void get_data() const; int number_of_accesses;};
class Person { char *name; };
class Person { char *name; };
const Person p;
p.name = "John Doe";
const Person p;
p.name = "John Doe";
51ָ�
sdןנ
Logical vs. Physical const-ness
Default behavior is not always sufficient:
Default behavior may be too strict:
class Database { void get_data() const; int number_of_accesses;};
class Database { void get_data() const; int number_of_accesses;};
class Person { char *name; };
class Person { char *name; };
const Person p;
p.name = "John Doe";
const Person p;
p.name = "John Doe";
52ָ�
sdןנ
Casting Away const in an Inspectorconst member functions are allowed to “cast away” the constness of the this pointer:
class List {public:
int len() const;private:
//...int clen;
};
class List {public:
int len() const;private:
//...int clen;
};
int List::len() const{
int len;
if (clen != -1)return clen;
// ... compute len
return const_cast<List *const>this->clen = len;}
int List::len() const{
int len;
if (clen != -1)return clen;
// ... compute len
return const_cast<List *const>this->clen = len;}
But doesn’t this mean lost opportunities for optimizations?
But doesn’t this mean lost opportunities for optimizations?
An inspector should preserve “logical”, not “physical” state.
Who cares!Who cares!
53ָ�
sdןנ
Mutable Keyword Meaning: data member is mutable even in an inspector. More generally, the data member is mutable even if
the whole object is constant.
class List {public:
int len() const;private:
//...mutable int clen;
};
class List {public:
int len() const;private:
//...mutable int clen;
};int List::len() const{
int len;
if (clen != -1)return clen;
// ... compute len
return clen = len; // No need for const_cast here}
int List::len() const{
int len;
if (clen != -1)return clen;
// ... compute len
return clen = len; // No need for const_cast here}
When I use a word, it means just what I choose it to mean - nothing more and nothing less!
Humpty Dumpty (B. Stroustrup)
When I use a word, it means just what I choose it to mean - nothing more and nothing less!
Humpty Dumpty (B. Stroustrup)
But, is mutable the right word for this?
But, is mutable the right word for this?
54ָ�
sdןנ
Semantics of const-ness
Logical const-ness depends on the semantics:
class Person { String *name; // part of Person Person *spouse; // independent};
class Person { String *name; // part of Person Person *spouse; // independent};
Influences accessors:
class Person {public: const String& name () const; Person& spouse() const;};
class Person {public: const String& name () const; Person& spouse() const;};
Really? Really?
55ָ�
sdןנ
Example: Adding Const Correctnessclass Array { public:
int& operator[](int i) {
return buff[i]; }...
};
class Array { public:
int& operator[](int i) {
return buff[i]; }...
};
class Array { public:
...int operator[](int i) const {
return buff[i]; }
};
class Array { public:
...int operator[](int i) const {
return buff[i]; }
};
int sum(const Array &a) {
int sum = 0;
for (int i = 0; i < a.size(); i++)sum += a[i];
return sum;}
int sum(const Array &a) {
int sum = 0;
for (int i = 0; i < a.size(); i++)sum += a[i];
return sum;}
1. Define Array class with anaccess operator[]
2. Define sum function thatsums an array's elements
3. Revise Array definition;add a const version of operator[].
Error
56ָ�
sdןנ
Problems with Const Correctness There is an implicit cast from a type to its const version, but
not vice versa. A function returning one of its arguments may add an
unsolicited const attribute.Example:extern const char *max(const char *a, const char *b);void f(char *a, char *b){
a = max(a, b); // Compile time error}Solution using overloading:extern const char *max(const char *a, const char *b);extern char *max(char *a, char *b); void f(char *a, char *b){
a = max(a, b); // OK}void g(const char *a, const char *b){
a = max(a, b); // OK}
57ָ�
sdןנ
Containers and Const Correctness
typedef char *string; typedef const char *cstring;class Bag {public:
void put(cstring);// Alternative I: string get(void);// Alternative II:cstring get(void);
};
typedef char *string; typedef const char *cstring;class Bag {public:
void put(cstring);// Alternative I: string get(void);// Alternative II:cstring get(void);
};
Both alternatives are bad: I: Bag must cast constness away:
Compromise constness of real constant objects deposited in the container.
II: Client must cast constness away:Compromise static-type checking -- Client must remember which
objects in the container are constants and which are not.
What was put into the Bag ?
string’s or cstring’s ?
What was put into the Bag ?
string’s or cstring’s ?
58ָ�
sdןנ
Solving the Const + Containers Problem The source of the problem:
void put(cstring); has two distinct meanings:
The function put will not change its argument.The container can store both constant and non-constant strings.
• Implicit cast from string to cstring
Solution: separate these two meanings:
class Bag_of_cstring {// Do not store strings here...void put(cstring);cstring get(void); // No const_cast in body
}; class Bag_of_string {// Do not store cstrings here
...void put(cstring);string get(void); // Legitimate const_cast in body
};
class Bag_of_cstring {// Do not store strings here...void put(cstring);cstring get(void); // No const_cast in body
}; class Bag_of_string {// Do not store cstrings here
...void put(cstring);string get(void); // Legitimate const_cast in body
};
Client never needs to cast away constness. Static const checking is preserved.
59ָ�
sdןנ
Options and Arguments
Input arguments are of two kinds: Operands Options
Discriminating options from operands: If an option is omitted, a reasonable value can be guessed. Options change during the evolution of a class.
Basic rule of arguments design: Methods should take operands as arguments, not options.
Use special methods to set options instead. Example:
w->set_color(black);w->set_alignment(left);w->set_font(courier);w->set_size(14);w->write(“Hello, World!”)
60ָ�
sdןנ
Managing Options Each option must have
A query feature for reading its value. A command feature for setting its value.
Boolean options should usually have two commands, for setting and unsetting.
Example:• container.compare_objects();• container.compare_references();
In C++ use function name overloading:
class Turtle {public:
enum Trail { solid, dashed };void trail(enum Trail); enum Trail trail(void);Point pos(void);void pos(Point);
};
class Turtle {public:
enum Trail { solid, dashed };void trail(enum Trail); enum Trail trail(void);Point pos(void);void pos(Point);
};
61ָ�
sdןנ
Values of Options
Objects of the same class may or may not share option values. Class Option: All objects in a class have the same value of the
option:Use a global shared object.
• Static class variable in C++.
Object Option: Each object must have an option instance variable.
Global default:• Set option in construction.
Non global default:• Pass options to class construction.
Even better (if possible) default parameter to constructor
class Point {public:
Point(Colour c = black);};
class Point {public:
Point(Colour c = black);};
62ָ�
sdןנ
Rationale for Option-Setting Technique Main idea
Trade vertical complexity for horizontal complexity. Pros
Running time overhead.Most options are used only in very special cases.
Elegant protocol.An option in the argument list must be called, and therefore understood by
every user of the method. This brings an unpleasant tradeoff between:• Potentially useful options• Method complexity
Cons For each eliminated options, two more methods are added. Memory overhead
Mitigated by global class variables.Negligible for Boolean and other common small flags.
Diversion from the tradition of long arguments, flags and options list in user commands and procedures in domains such as GUI:
Object oriented is a different state of mind!
63ָ�
sdןנ
Number of Arguments in Eiffel
Use few arguments: Average number < 1
0 1 2 3 4 5--7
Base
Vision0%
10%
20%
30%
40%
50%
60%
Fe
atu
res
# Arguments
Number of arguments in Eiffel standard library [Meyer ‘94]
64ָ�
sdןנ
Statistics on Number of Arguments
Project #Classes #Methods AVG#arguments
MAX#arguments
Eiffel Base 149 1823 0.4 3
Eiffel Vision 546 3666 0.7 7
Project #Classes #Methods AVG#arguments
MAX#arguments
Eiffel Base 149 1823 0.4 3
Eiffel Vision 546 3666 0.7 7
65ָ�
sdןנ
“Define structure.” Start the class declaration with the public, not the
private part! Kinds of structure coordinates:
Immutable - set at construction and never changed after.Examples: birth date, gender, pointer to file cache buffer. Implementation techniques:
• const data members (may be public!)
• References (as opposed to pointers)
Mutable - changed by mutators.Should be private (or protected).
Transient - can be changed even by inspectors! Not part of the abstract state. Meaning does not change, but bit
and byte values may change:• Internally used counters.
• Cache values, e.g., last accessed item, internal representation of complex numbers in Cartesian and polar forms and conversions as per need.
66ָ�
sdןנ
C++ Members Classification
to o th er typ e
Function Mem bers
D ata Mem bers
S tatic
C onvertors(to o ther type)
Inspectors
Exposers
Mutators
Severa l Argum ents
From Sam e C lass(C opy C onstructor)
C onvertors(from other type)
Explic it C onstructors
O ne Argum ent N o Argum entsD efault C onstructor
C onstructors D estructor
Function Mem bers
Transient
Mutableread-write
Im m utableread-only
D ata Mem bers
O rd inary
Mem bers
67ָ�
sdןנ
References vs. Pointers Require dereferencing
Transient binding Junk, if defined but not
initialized May store a null value
More flexible
Check for null before dereferencing
Can serve as array elements
No dereferencing
Permanent binding Must be initialized May never be null
More robust
No special checking required in order to use
Cannot serve as array elements
void swap(int &a, int &b){
int tmp = a;b = a;a = tmp;
}
void swap(int &a, int &b){
int tmp = a;b = a;a = tmp;
}
void swap(int *a, int *b){
int tmp = *a;*b = *a;*a = tmp;
}
void swap(int *a, int *b){
int tmp = *a;*b = *a;*a = tmp;
}
References are similar to constant pointers
68ָ�
sdןנ
QuizIdentify member kinds in the following example and comment on its design.
class Array {public:
explicit Array(int len_): len(len_), buff(new int[len]){}
~Array(void) { delete[] buff; }int size(void) const { return len; }int& operator[](int i) {
return buff[i]; }int operator[](int i) const {
return buff[i];}
private:const int len;int * const buff;Array(const Array &);Array & operator =(const Array &);
};
class Array {public:
explicit Array(int len_): len(len_), buff(new int[len]){}
~Array(void) { delete[] buff; }int size(void) const { return len; }int& operator[](int i) {
return buff[i]; }int operator[](int i) const {
return buff[i];}
private:const int len;int * const buff;Array(const Array &);Array & operator =(const Array &);
};
69ָ�
sdןנ
“Establish class invariant.”
Consider a Date class with a year, month and day structure coordinates. Then, not all points in the Cartesian product of the values of coordinates is a legal date value.
Class Invariant: A logical condition which defines the permissible states an
instance of the class may be in. Relation between values of the state coordinates. Must be true:
When constructors have finished their execution.Before the execution of any feature.After the execution of any mutator.Before the destructor is executed.
Usually can only be approximated by programming language constructs.
70ָ�
sdןנ
Examples of Class Invariants Rectangle: A.x £ B.x A.y £ B.y
Gap-method buffer: 0 <= cursor && cursor < start && start <= size
A
B
X
y
Gap UsedUsed
0 size-1cursor start
71ָ�
sdןנ
Invariants in C++
class String {public:
// ...class Inconsistent {};void invariant(void);
private: int size;
char * const s;};inline String::invariant(void){
if (strlen(s) + 1 != size)throw Inconsistent;
}
class String {public:
// ...class Inconsistent {};void invariant(void);
private: int size;
char * const s;};inline String::invariant(void){
if (strlen(s) + 1 != size)throw Inconsistent;
}
72ָ�
sdןנ
“Implement behaviour.”
General Guide-lines: Define member function bodies outside the class
declaration.
Even if they are inline! Make methods short:
Usually 10-20 lines is the upper maximum.
73ָ�
sdןנ
Implementing Constructors
If possible, the body of the constructors should be empty.
All initialization should be done in the header This is the only way to initialize:
const data members reference data membersdata members without default constructorbase classes
Avoid unnecessary initialization and then copy. This is the only sensible way to initialize data members which are objects.
74ָ�
sdןנ
Constructor with Empty Body
class String {public:
String(const char *);// ...
private: int size;
char * const s;};inline String::String(const char *s_):
size(strlen(s_)+1),s(strcpy(new char[size],s_))
{}
class String {public:
String(const char *);// ...
private: int size;
char * const s;};inline String::String(const char *s_):
size(strlen(s_)+1),s(strcpy(new char[size],s_))
{}
75ָ�
sdןנ
Implementing Destructors Destructors take no arguments.
Make this explicit by writing void between the parenthesis in the function signature.
In most cases, the sole purpose of the destructors is to deallocate the memory allocated by the constructor.
Destructors should almost always be virtual.
class String {public:
virtual ~String(void);// ...
private: int size;char * const s;
};
inline String::~String(void){
delete[] s;}
class String {public:
virtual ~String(void);// ...
private: int size;char * const s;
};
inline String::~String(void){
delete[] s;}
76ָ�
sdןנ
Destructors and Exceptions Destructors of local variables are invoked automatically
when a thrown exception passes through the defining block (stack unwinding).
An attempt to throw an exception in the midst of stack unwinding will causes terminate() to be executed. Therefore:
Similarly, since exit() calls the destructors of all global variables, we have:
Destructors should not throw exceptions!
Destructors should not call exit()!
77ָ�
sdןנ
Avoid Memory Management Hassles
Memory management for complex and/or nested objects which have dynamic memory allocations can be difficult.
At first iteration, implement a correct constructor and destructor and forbid execution of the copy constructor and assignment operator, by making them private. There is no need to define body for these.
class String {private:
// Note: the privacy of the copy constructor and the // assignment operator is part of the interface! It will
save // prospective users of the class from heap disasters. String(const String &);const String &operator = (const String
&);public:
// ...};
class String {private:
// Note: the privacy of the copy constructor and the // assignment operator is part of the interface! It will
save // prospective users of the class from heap disasters. String(const String &);const String &operator = (const String
&);public:
// ...};
78ָ�
sdןנ
Checking Invariants in Ctors/Dtors
inline String::String(const char *s_):size(strlen(s_)+1),s(strcpy(new char[size],s_))
{invariant();
}
inline String::~String(void){ invariant();
delete[] s;}
inline String::String(const char *s_):size(strlen(s_)+1),s(strcpy(new char[size],s_))
{invariant();
}
inline String::~String(void){ invariant();
delete[] s;}
79ָ�
sdןנ
Few Auxiliary Macros
#define PASTE(a,b) a##b#define EXPAND_PASTE(a,b) PASTE(a,b)
#define LINE(x) EXPAND_PASTE(x, __LINE__)
#define PASTE(a,b) a##b#define EXPAND_PASTE(a,b) PASTE(a,b)
#define LINE(x) EXPAND_PASTE(x, __LINE__)
line.h
#include "line.h"#define try__LINE__ "You loose!\n"
PASTE(try, __LINE__)EXPAND_PASTE(try, __LINE__)EXPAND_PASTE(try, __LINE__)
#include "line.h"#define try__LINE__ "You loose!\n"
PASTE(try, __LINE__)EXPAND_PASTE(try, __LINE__)EXPAND_PASTE(try, __LINE__)
try.c
"You loose!\n"try5try6
"You loose!\n"try5try6
try.i
cpp
80ָ�
sdןנ
Smart Macros for Invariant Checking#define PRE_CONDITION(exp) \
struct LINE(pre_class) { \LINE(pre_class) (void){ exp; } \
} LINE(pre_class) #define POST_CONDITION(exp) \
struct LINE(post_class)_{ \~LINE(post_class) (void) { exp; }\
} LINE(post_class)
#define PRE_INV PRE_CONDITION(invariant())#define POST_INV POST_CONDITION(invariant())#define BOTH_INV PRE_INV;POST_INV
#define CONSTRUCTOR POST_INV#define DESTRUCTOR PRE_INV#define MUTATOR BOTH_INV#ifdef TRUST_INSPECTOS#define INSPECTOR #else#define INSPECTOR BOTH_INV#endif
Unfortunately, this does not work in most C++ compilers. Local variables are not recognized in local class declarations (Stroustrup, p. 553).
Unfortunately, this does not work in most C++ compilers. Local variables are not recognized in local class declarations (Stroustrup, p. 553).
81ָ�
sdןנ
Using the Invariant Macrosclass Date {
public:class Inconsistent { };void invariant(void) { ... } //...
private:int day, month, year;
};Date::Date(int day_, int month, int year_):
day(day_), month(month_), year(year_){
CONSTRUCTOR;// Make fix for February 29th etc.
}const Date & Date::operator +=(int days) {
MUTATOR;// ...
}Date::operator int(void) const{
INSPECTOR;// Compute and return number of days from beginning of century
}
82ָ�
sdןנ
Contract for Features
Client Supplier
Pre-Condition Obligation Benefit
Post-Condition Benefit Obligation
A feature call involves two parties: Client of feature Supplier of feature code
Establishing a contract between the two parties allows both sides to get more.
The tough love principle:
Demands a lot; gives a lot! Supplier: can concentrate on doing his
duties, and stop worrying about the client errors. Client: By promising to satisfy the supplier demands
can expect to get more:
83ָ�
sdןנ
Pre- and Post-Conditions Formal notation: Predicates on program execution
{P} S {Q} If P is true before the execution of S then Q will be true (if and) when terminates.
A scheme for proving correctness:
{P0} S1 {P1} S2 {P2} ... Sn {Pn} Example:{ x ³2x £ -1} x := sqr(x -abs(x)/2) {x ³2}
Weakest pre-condition: given a post-condition, what is the weakest pre-condition required to satisfy it.
We say that p is weaker than q if q p.False is always a valid pre-condition.
In the above example, the weakest pre-condition is:
{ x ³2x £ -2 ײ2/3} Strongest post-condition: given a pre-condition, what is the
strongest post-condition that can be inferred from it.True is always a valid post-condition.The post-condition in the above example is the strongest.
84ָ�
sdןנ
Macros for Contracts struct ContractError {
const char *file;const int line;const char *reason;const enum Kind { client, supplier } kind;ContractError(const char *f,int l,const char *r,enum Kind k):
file(f), line(l), kind(k), reason(r) {}};
#define REQUIRE(exp) \struct LINE(require_class) { \
LINE(require_class) (bool exp, const char *desc) { \if (!(exp)) \
throw ContractError(__FILE__,__LINE__,#exp, \ContractError::client); \
} \} LINE(require_var)
#define ENSURE(exp) \
struct LINE(ensure_class) { \~LINE(ensure_class)(void) { \
if (!(exp)) \throw ContractError(__FILE__, __LINE__, \
ContractError::supplier); \} \
} LINE(ensure_var)
85ָ�
sdןנ
A Class Using the Contract Macrosclass Stack {
public:void push(int);void pop(void);int look(void) const;bool full(void) const;bool empty(void) const;
private:// ...
};
void Stack::push(int i){
REQUIRE(!full());ENSURE(!empty());...
}
void Stack::pop(int i){
REQUIRE(!empty());ENSURE(!full());...
}
86ָ�
sdןנ
A Function Using the Contract Macros
const double epsilon = 1E-5;
double sqrt(double x){
extern double abs(double); double result = 1;
REQUIRE(x >= 0);ENSURE(result * result <= x + epsilon);ENSURE(result * result >= x - epsilon);
for (;;) {if (abs(x - result * result) <= epsilon)
return result;result = (result + x/result)/2;
}
return result;}
87ָ�
sdןנ
Tough Love Principle The stronger the pre-condition, the stronger the post-
condition can be. If in the sqrt example, we require that x is a non prime integer
in the range 0..4, then we can guarantee an infinite accuracy of the result!
More generally, if we require FALSE (an impossible condition to meet), then we can promise the world.
Conversly, if we promise nothing (TRUE), then we need not require anything!
Good contract design dictates a balance between the pre and post conditions. The overall goal should be:
Value for money Class invariants are particularly nice, and serve as
an inductive hypothesis. Whatever you assume, you should be able to prove.
88ָ�
sdןנ
Final Guide-line: Avoid Macros
#define BUFF_SIZE 1024
#define NL '\n'
#define PI 3.1415926
#define TWICE(x) ((x)<<1)
#define SQR(x) ((x)*(x))
enum { BUFF_SIZE = 1024 };
enum { CR = '\r', LF = '\n' };
const double Pi = 3.1415926;
inline twice(int x) {
return x << 1;
}
template <class Type>
Type sqr(Type x)
{
return x * x;
}
C macros are a primitive, language construct, which does not respect other constructs such as name space, encapsulation, etc. It should be used only when it is absolutely necessary.
Some techniques for avoiding macros are illustrated below: