Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 ָ נן sd Object Based Design uIdentifying the Classes uFeatures Design uConst Correctness uDesign by Contract uSome C++ Tips.

Similar presentations


Presentation on theme: "1 ָ נן sd Object Based Design uIdentifying the Classes uFeatures Design uConst Correctness uDesign by Contract uSome C++ Tips."— Presentation transcript:

1 1 ָ נן sd Object Based Design uIdentifying the Classes uFeatures Design uConst Correctness uDesign by Contract uSome C++ Tips

2 2 ָ נן sd Steps in Object Based Design uFind the classes and name them uCharacterize your classes lDomain: foundation/architectural/business/application lNature: state/source/sink/view/helper/other lKind: mutable/immutable uAssign responsibilities uIdentify collaborators uDeclare protocol uDefine structure uEstablish class invariant uImplement behaviour Good object-based design is first and foremost good modular design.

3 3 ָ נן sd “Find the classes, ” uArguably, the easiest step! uMain heuristic: lUnderline the nouns in the problem description text. uCatches: lMust have a good problem description first Requirement and analysis phases lNot 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 4 ָ נן sd Nouns in Requirements Problem 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 5 ָ נן sd Candidate Classes #1/5 Tangible: 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 6 ָ נן sd Candidate Classes #2/5 Events: 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 7 ָ נן sd Candidate Classes #3/5 Places & 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.

8 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 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 10 ָ נן sd “... and name them.” uUse capitalized identifiers (usually one word) for all class names. l Date, Time, Window, Array uClass names are typically nouns or pairs of nouns: l Student, VersionNumber, PortManager uDo not use verbs! lThe following class names indicate design errors: Parse, Draw, Move lException: classes describing operation in an interactive system. uer-named classes lExamples: LexicalAnalyzer, Mover, Popper, Updater lUsually a design flaw. Especially in the following cases: An er class with many routines An er class with just one routine Many unrelated er classes

11 11 ָ נן sd Item / Group Ambiguity uSource: the class/object confusion arises from the fact that the name of a group is also used for denoting a single item in it: lThe cat in the hat. lKishta is a cat. uSolutions lUse plurals for class names Dates, Cats,... lUse articles for naming objects (the Smalltalk convention): theCat, aWindow,... lBe smart and organized: Capitalized names for classes Meaningful lower case names for objects

12 12 ָ נן sd General Rules for Naming uPronounceable Names lIf you cannot pronounce it, you cannot use it! uFew words: lOne word is best. lSeparate multiple words by Capitalization: BinaryTree Underscore: Binary_Tree Hyphen (COBOL style): Deposit-Slip uDo not invent abbreviations lClarity should be favoured over brevity. lIf you must, omit vowels and/or suffixes msg, buff, ptr,... uNo nested digits. These are easily confused l0/O l1/I l2/Z l5/S

13 13 ָ נן sd “Characterize your classes: Domain.” uThere is a ladder of domains from which classes are taken. uDomains are characterized by the extent to which its classes are applicable: lProblem Space Application Domain One program or application. Business Domain One industry or company. lProgram Space Architectural 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 Medium Reusability High Reusability

14 14 ָ נן sd Program Space Domains uSubdomains of the foundation domain: lFundamental Integer, Boolean, Character, String, Pointer, Class,... lStructural Stack, Queue, List, Binary-Tree, List,... lSemantic (gives units to quantities) Date, Time, MKS, CGS, Point, Line,... uSubdomains of the architectural domain: lMachine-communication Port, Remote-Machine, File, Process, Thread, Keyboard, Mouse-Event,... lDatabase Transaction, Backup, Stream,... lHuman Interface Window, View-Port, Dialogue-Box, Menu-Item, Command, Radio-Button,...

15 15 ָ נן sd Problem Space Domains uSubdomains of the business domain: lAttribute Money, Temperature, Colour, Balance, Address, Rank,... lRole Customer, Patient, Bank, Department,... lRelationship Member-In, Account-Ownership,... uSubdomains of the application domain: lEvent-Stimulus-Recognizer Components that recognize external events lEvent-Activity-Manager Components that handle those events

16 16 ָ נן sd “Characterize your classes: Nature.” uData Manager, Data or State: Classes used to store data. lAlmost all classes. lBasic Principle: All data should be managed by a class. uContainer: Sole purpose is to store objects of other classes. uData Sinks: Classes that consume data lOutput file, Port uData Sources: Classes that generate data lRandomNumber, FibonacciSequence uViewer: Used to display the data of other classes. uHelper, Facilitator, or Algorithm: Used for packaging abstract operations lSorter, Parser,... uOther: all the rest...

17 17 ָ נן sd “Characterize your classes: Kind.” uData Managers are of two kinds: lMutable: class whose objects may change their state during their life time. Long life-time of objects. lImmutable 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. uImmutable classes are sometimes easier to design. New instances are dynamically created if necessary Examples: lInteger, Date, String, Point, Rectangle lFile-Name lButton, Transaction, Playing-Card lDeposit, Permission-Request

18 18 ָ נן sd “Set responsibilities.” uSo far, all that our program has is: uThe 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 19 ָ נן sd Responsibility uEach class must have a cohesive set of responsibilities. uThere must be a clear separation of responsibilities among classes. uResponsibilities must be: lShort. lDescribe what not how. lInclude an active verb. uCommon errors: lToo much responsibility. lNo responsibility at all - false class. lUnrelated responsibilities. lShared responsibility.

20 20 ָ נן sd “Identify collaborators.” uCollaborators (at first iteration no need to determine exact collaboration): lProviders: 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. lUsers: The users of a class C, are all classes to which C is a provider. uSet of collaborators should be small.

21 21 ָ נן sd Encumbrance uEncumbrance of a class C: The number of direct and indirect class providers to C. uExample: Encumbrance tends to be correlated with position in the domain ladder. Deviations should be thoroughly checked. They may contain design flaws. Turtle 6 Direction 1 Pen 4 Colour 1 Position 1 Real 0 Integer 0 use s

22 22 ָ נן sd CRC Cards 4 in 6 in Collaborators Class Name Responsibilities uCRC = Class + Responsibility + Collaboration. uInvented by Beck (89). uUsed by a group of individuals in the simulation process. uBack of card is used for the implementation details.

23 23 ָ נן sd “Declare protocol.” uStart from responsibility description. uKinds of features lMutators lAccessors lConstructors lDestructors lConvertors uDesign decisions: lNumber of features lKinds of arguments Input/Output Options/Real arguments lNumber of arguments lNames of features

24 24 ָ נן sd External vs. Member Functions #include "complex.h" Complex conjugate(Complex c) {... } class Complex { public: Complex conjugate(void) {... } };

25 25 ָ נן sd Kinds of Features: Mutators uNicknames: modifiers, commands, operations, procedures, etc. uNaming: lverbs: insert, pop, activate, move, draw, scale, deposit, indent lverbs with adverb: quick_format, left_rotate, deep_copy lverbs with noun: add_color, insert_line, move_to_parent, set_submitter, trace_rays, start_heating, display_headers,... Do not append class name! stack_pop lOperators names: Utilize, not abuse familiarity with ordinary operator names. uSub-classification: lHigh-level: Can be implemented by using only features in the class protocol. lLow-level: Not high level. Essential Atomic uImmutable classes should have no mutators.

26 26 ָ נן sd Kinds of Features: Inspectors uNicknames: examiners, accessors, queries, functions, etc. uRatio: 60%! (only 40% are mutators) lMeasured in the Eiffel library (~700 classes, ~5500 features). lA result of two design techniques we will study later. uNaming: lsubstantive (noun): occurrences, size, area, summary_of_problem lnoun with adjective: current_size, last_int, next_value uNaming Boolean inspectors: ldescriptive adjectives, sometimes prefixed with “is”. ready (not status), full, empty is_sibling, is_last, is_leaf, is_root

27 27 ָ נן sd Implementing Inspectors in C++ uThe const keyword after the function member signature effectively defines this as: const Heater * const this; instead of Heater * const this; uThe abstract (client-visible) state of the object is not allowed to change. uThe compiler isn't allowed to assume a byte-wise const, for the purpose of optimization since a non-const cast could exist. lHowever, 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 28 ָ נן sd No Side-Effects Principle uImplementation Guide-lines: lMutators should be implemented as procedures ( void function members). lInspectors should be implemented as functions. uRule: Do not make a design that mixes the two. uCounter-intuitive to C programmers who like to write: while ((c = getchar()) != EOF) {... }

29 29 ָ נן sd Rationale for No-Side Effects Principle uClear distinction between inspectors and mutators. uExploiting reader’s background knowledge and understanding of mathematical functions. getchar() == '\n' || getchar() == '\t' || getchar() == ' ' is not at all the same as iswhite(getchar())

30 30 ָ נן sd Kinds of Features: Constructors uNicknames: creating routines, new messages. uNaming: lC++: class name lEiffel: make lSmalltalk: new

31 31 ָ נן sd C++ Constructors Caution uConstructors with only one argument, implicitly define a conversion from the argument type to the class type. uTo avoid this, use the new explicit keyword: uThis will prevent the following two kinds of nonsense code: 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 32 ָ נן sd Kinds of Features: Destructors uNicknames: cast operators uNaming: lC++: ~ class-name lEiffel: no destructors lSmalltalk: no destructors uDon't bother with them now: lDestructors exist only in non garbage collecting systems. lTheir purpose is most frequently to aid in memory management. lThis is an implementation detail, which need not be considered at this stage of the design.

33 33 ָ נן sd Kinds of Features: Convertors. uNaming: lC++: operator Type lEiffel: no convertors lSmalltalk: no convertors uThe main purpose of convertors is to make an ADT complete. Their consideration could be put off to later.

34 34 ָ נן sd Number of Features in Eiffel Incremental size of classes in the Eiffel Library [Meyer ‘94]

35 35 ָ נן sd Observations on #Features uRanges: lSmall classes (0-10): 60-80%. lMedium classes (11-20) 15-20%. lLarge classes (20-40) 5-10%. lVery big classes (more than 40): As much as 150 features. Rare. uAverages: l12.2 in base. l6.7 in vision. uRule of thumb: l5-20 is good. l80 is probably too much.

36 36 ָ נן sd Number of Methods (Smalltalk) Object-Oriented Software Metrics [Lorenz & Kidd ‘95] uObserve the variety lAmong projects lAmong classes within project

37 37 ָ נן sd Distribution of Method Numbers in Smalltalk Programs Object-Oriented Software Metrics [Lorenz & Kidd ‘95]

38 38 ָ נן sd How Big Should a Class be? uEach class has an absolute minimum, below which, the class is useless. lAtomic features are necessary. Nevertheless, too low level atomic features would hinder redesign. uHow much should we have beyond this? lMinimalist approach (Stroustrup). lMaximalist approach (Meyer).

39 39 ָ נן sd Minimalist Approach uExtreme: Do not add anything that can be expressed in terms of atomic features. uAdd only the most important gizmos. uRationale: lCannot forecast the future: It is easy to add a feature later. It is difficult to remove a bad feature. lEasier to write and understand. lUnnecessary features may be an extra baggage. More work to implement. May never be necessary. Greater effort to redesign. Higher coupling. lInvest time in thinking, not implementing! uAppropriate for problem space classes.

40 40 ָ נן sd Shopping List Approach uAdd all features: lthat make an ADT complete. lthat may be useful to a potential user. lthat are not duplicate. uRationale: lNot Invented Here is a result of minimalism. lIn 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. uAppropriate for reuse library and program-space classes.

41 41 ָ נן sd Input and Output Arguments uArguments to methods are of three kinds: lInput lOutput lInput and Output uOutput Arguments: not common in good design. lIf there is only one returned value, then the method could be made into a function that returns this value. lTwo or more returned values are less common. They are usually an indication of bad design. uInput/Output Arguments: very rare. lDanger 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). uTypical case: lSeveral inputs l0/1 outputs.

42 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 43 ָ נן sd Reference to Constant Object uIf an argument should not be changed, then this demand must be: lobvious lenforced uReference to constant object: C++ version of input only argument declaration. lCompile-time error occurs whenever print attempts to change s. lNo run-time penalty: Execution speed Memory consumption lInformation may be used (in special cases) by good optimizing compilers.

44 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. lType safety requires you to annotate your code with type information which isn't absolutely necessary. lYou, 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. lIn 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 45 ָ נן sd When to do it? uMaking an existing program const -correct is not easy: l const -correctness tends to spread very quickly. lBottom-up approach is usually better than top-down. uIt 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 46 ָ נן sd const Objects uAll 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 ! Same as: const int top, left,... Same as: const int top, left,... uCan only call const member functions.

47 47 ָ נן sd Initialization of const Objects uDone once in construction time: const Rect r1(20, 20, 100, 100); r1 = r2; // error! const Rect r1(20, 20, 100, 100); r1 = r2; // error! uMembers 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? Can the compiler generate operator= ?

48 48 ָ נן sd const Member Functions u 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 } uMembers cannot be changed. uCannot call other non- const member functions. uSpecifically, assignment should be non- const.

49 49 ָ נן sd Pointers and const -ness u const refers to its left hand type: p1 = &two; p2 = &two; p3 = &two; p4 = &two; p1 = &two; p2 = &two; p3 = &two; p4 = &two; p 12 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 50 ָ נן sd Logical vs. Physical const -ness uDefault behavior is not always sufficient: uDefault 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 51 ָ נן sd Logical vs. Physical const -ness uDefault behavior is not always sufficient: uDefault 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 52 ָ נן sd Casting Away const in an Inspector const 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 this->clen = len; } int List::len() const { int len; if (clen != -1) return clen; //... compute len return const_cast this->clen = len; } But doesn’t this mean lost opportunities for optimizations? An inspector should preserve “logical”, not “physical” state. Who cares!

53 53 ָ נן sd Mutable Keyword uMeaning: data member is mutable even in an inspector. uMore 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?

54 54 ָ נן sd Semantics of const -ness uLogical 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 }; uInfluences accessors: class Person { public: const String& name () const; Person& spouse() const; }; class Person { public: const String& name () const; Person& spouse() const; }; Really?

55 55 ָ נן sd Example: Adding Const Correctness class 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 an access operator[] 2. Define sum function that sums an array's elements 3. Revise Array definition; add a const version of operator[]. Error

56 56 ָ נן sd Problems with Const Correctness uThere is an implicit cast from a type to its const version, but not vice versa. uA 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 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: lI: Bag must cast constness away: Compromise constness of real constant objects deposited in the container. lII: 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 58 ָ נן sd Solving the Const + Containers Problem uThe source of the problem: void put(cstring); lhas 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 uSolution: separate these two meanings: class Bag_of_cstring {// Do not store string s here... void put(cstring); cstring get(void); // No const_cast in body }; class Bag_of_string {// Do not store cstring s here... void put(cstring); string get(void); // Legitimate const_cast in body }; class Bag_of_cstring {// Do not store string s here... void put(cstring); cstring get(void); // No const_cast in body }; class Bag_of_string {// Do not store cstring s 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 59 ָ נן sd Options and Arguments uInput arguments are of two kinds: lOperands lOptions uDiscriminating options from operands: lIf an option is omitted, a reasonable value can be guessed. lOptions change during the evolution of a class. uBasic rule of arguments design: lMethods should take operands as arguments, not options. uUse special methods to set options instead. lExample: w->set_color(black); w->set_alignment(left); w->set_font(courier); w->set_size(14); w->write(“Hello, World!”)

60 60 ָ נן sd Managing Options uEach option must have lA query feature for reading its value. lA command feature for setting its value. Boolean options should usually have two commands, for setting and unsetting. Example: container.compare_objects(); container.compare_references(); uIn 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 61 ָ נן sd Values of Options Objects of the same class may or may not share option values. lClass Option: All objects in a class have the same value of the option: Use a global shared object. Static class variable in C++. lObject 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); };

62 62 ָ נן sd Rationale for Option-Setting Technique uMain idea Trade vertical complexity for horizontal complexity. uPros lRunning time overhead. Most options are used only in very special cases. lElegant 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 uCons lFor each eliminated options, two more methods are added. lMemory overhead Mitigated by global class variables. Negligible for Boolean and other common small flags. lDiversion 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 63 ָ נן sd Number of Arguments in Eiffel uUse few arguments: lAverage number < 1 Number of arguments in Eiffel standard library [Meyer ‘94]

64 64 ָ נן sd Statistics on Number of Arguments

65 65 ָ נן sd “Define structure.” uStart the class declaration with the public, not the private part! uKinds of structure coordinates: lImmutable - 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) lMutable - changed by mutators. Should be private (or protected ). lTransient - 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 66 ָ נן sd C++ Members Classification

67 67 ָ נן sd References vs. Pointers uRequire dereferencing uTransient binding uJunk, if defined but not initialized uMay store a null value More flexible uCheck for null before dereferencing uCan serve as array elements u No dereferencing u Permanent binding u Must be initialized u May never be null More robust u No special checking required in order to use u 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; } References are similar to constant pointers

68 68 ָ נן sd Quiz Identify 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 69 ָ נן sd “Establish class invariant.” uConsider 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. uClass Invariant: lA logical condition which defines the permissible states an instance of the class may be in. lRelation between values of the state coordinates. lMust 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. lUsually can only be approximated by programming language constructs.

70 70 ָ נן sd Examples of Class Invariants uRectangle: A.x £ B.x  A.y £ B.y uGap-method buffer: 0 <= cursor && cursor < start && start <= size A B X y Gap Used 0size-1cursorstart

71 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 72 ָ נן sd “Implement behaviour.” uGeneral Guide-lines: lDefine member function bodies outside the class declaration. Even if they are inline ! lMake methods short: Usually lines is the upper maximum.

73 73 ָ נן sd Implementing Constructors uIf possible, the body of the constructors should be empty. uAll initialization should be done in the header lThis is the only way to initialize: const data members reference data members data members without default constructor base classes lAvoid unnecessary initialization and then copy. This is the only sensible way to initialize data members which are objects.

74 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 75 ָ נן sd Implementing Destructors uDestructors take no arguments. lMake this explicit by writing void between the parenthesis in the function signature. uIn most cases, the sole purpose of the destructors is to deallocate the memory allocated by the constructor. uDestructors 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 76 ָ נן sd Destructors and Exceptions uDestructors of local variables are invoked automatically when a thrown exception passes through the defining block (stack unwinding). uAn attempt to throw an exception in the midst of stack unwinding will causes terminate() to be executed. Therefore: uSimilarly, since exit() calls the destructors of all global variables, we have: Destructors should not throw exceptions! Destructors should not call exit() !

77 77 ָ נן sd Avoid Memory Management Hassles uMemory management for complex and/or nested objects which have dynamic memory allocations can be difficult. uAt 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 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 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__) #include "line.h" #define try__LINE__ "You loose!\n" PASTE(try, __LINE__) EXPAND_PASTE(try, __LINE__) try.c "You loose!\n" try5 try6 "You loose!\n" try5 try6 try.i cpp

80 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()) #defineBOTH_INV PRE_INV;POST_INV #define CONSTRUCTOR POST_INV #define DESTRUCTOR PRE_INV #define MUTATOR BOTH_INV #ifdef TRUST_INSPECTOS #define INSPECTOR #else #define INSPECTORBOTH_INV #endif Unfortunately, this does not work in most C++ compilers. Local variables are not recognized in local class declarations (Stroustrup, p. 553).

81 81 ָ נן sd Using the Invariant Macros class 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 82 ָ נן sd Contract for Features uA feature call involves two parties: lClient of feature lSupplier of feature code uEstablishing a contract between the two parties allows both sides to get more. lThe tough love principle: Demands a lot; gives a lot! uSupplier: can concentrate on doing his duties, and stop worrying about the client errors. uClient: By promising to satisfy the supplier demands can expect to get more:

83 83 ָ נן sd Pre- and Post-Conditions uFormal 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: {P 0 } S 1 {P 1 } S 2 {P 2 }... S n {P n } uExample: { x ³  2  x £ -1 } x := sqr(x -abs(x)/2) { x ³  2  } uWeakest 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 ³  2  x £ -2 ײ  2/3 } uStrongest 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 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 85 ָ נן sd A Class Using the Contract Macros class 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 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); for (;;) { if (abs(x - result * result) <= epsilon) return result; result = (result + x/result)/2; } return result; }

87 87 ָ נן sd Tough Love Principle uThe stronger the pre-condition, the stronger the post- condition can be. lIf 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! lMore generally, if we require FALSE (an impossible condition to meet), then we can promise the world. uConversly, if we promise nothing (TRUE), then we need not require anything! uGood contract design dictates a balance between the pre and post conditions. The overall goal should be: Value for money uClass invariants are particularly nice, and serve as an inductive hypothesis. lWhatever you assume, you should be able to prove.

88 88 ָ נן sd Final Guide-line: Avoid Macros #define BUFF_SIZE 1024 #define NL '\n' #define PI #define TWICE(x) ((x)<<1) #define SQR(x) ((x)*(x)) enum { BUFF_SIZE = 1024 }; enum { CR = '\r', LF = '\n' }; const double Pi = ; inline twice(int x) { return x << 1; } template 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:


Download ppt "1 ָ נן sd Object Based Design uIdentifying the Classes uFeatures Design uConst Correctness uDesign by Contract uSome C++ Tips."

Similar presentations


Ads by Google