1 ָ נן sd object based design uidentifying the classes ufeatures design uconst correctness...

88
1 sd Object Based Design Identifying the Classes Features Design Const Correctness Design by Contract Some C++ Tips

Upload: jordan-watling

Post on 16-Dec-2015

220 views

Category:

Documents


1 download

TRANSCRIPT

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: