object-based programming part ii• implies that the part object may belong to only one whole...

41
C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002 Chapter 4 Object-Based Programming Part II

Upload: others

Post on 15-Mar-2021

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

Chapter 4

Object-Based Programming

Part II

Page 2: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Preview

UML, the Unified Modeling Language object composition techniques several new idioms the first design pattern, the Singleton, which is used to constrain

object instantiation namespaces

Page 3: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: UML Class Notations

Class diagrams describe classes and two kinds of static relationships: associations and subtypes.

class Student { public:Student(long); static string university(); void number(long);long number() const;

private:static string university_;long number_;

};

Page 4: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

Associations between classes represent relationships between objects of these classes. They represent responsibilities:

a factory is responsible for creating products.

4: Associations

Page 5: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Aggregations

Aggregation is more than association:• the aggregator is responsible for the aggregatee• the whole/part-of relationship• a tighter coupling and typically implies containment• does not imply lifetime dependencies between the classes

Each student has exactly one name, but the same name may be used to name many students

Page 6: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Compositions

Composition is more than aggregation:• has both, ownership and lifetime dependencies• implies that the part object may belong to only one whole object,

and the lifetime of the composer and composee are identical.

Circle uses the class Point to denote its origin. Not only is a point a part-of a circle, but additionally they have the same lifetime

Page 7: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Reusability through Object Composition

Object composition provides new functionality by composingobjects:

black-box reusability

Page 8: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Reusability through Object Composition

In general, object composition can be implemented using one of two techniques:

using nested objects using references or pointers.

Page 9: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: By Value and by Reference Composition

By value object composition:• a static structure that cannot be updated to satisfy the changing

requirements of the program • efficient access to objects• copying large objects may be inefficient. By reference object composition:• defined dynamically• can be used for polymorphic programmingIf you want to be able to switch between various Associatee

objects at runtime, or to take advantage of lazy evaluation you have to use a pointer rather than a reference

Page 10: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Delegation

Delegation:• a delegator object that is receiving messages, delegates these

requests to a delegatee object• often combined with inheritance is heavily used in design patterns• suitable for capturing a "is-a-role-played-by" relationship

Consider a general windowing system, represented by the class Window, which delegates all its requests to a base class, which contains various implementations of operations on windows. Objects of various classes, such as the object representing the X window implementation, or IBM's PM window implementation, can be substituted for the object of the general base class.

Page 11: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Managing the Creational Process, Part I :Forbidding

Copying Some classes do not support copying of their objects:

class Student {public:explicit Student(long);~Student();long number() const;

private:Student(const Student&); //private copy constructorStudent& operator=(const Student&); // private assign

// both have to be declared to avoid defaults

long number_;};

Page 12: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

To forbid the copying of objects, make the copy constructor and overloaded assignment operator private (or protected).

Forbid Copying

Page 13: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

In order to take over the creational process, make all the constructors either private or protected and provide static functions, called instantiating operations that create and return an object.

Instantiating Operations

Page 14: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Forbidding Constructors: Instantiating Operations

There are two commonly used coordinate systems in a two dimensional space: Cartesian (using x and y coordinates) and Polar (using a radius and an angle). The class Point tries to support both kinds of coordinates:

class Point { public:Point(double x, double y); // CartesianPoint(double r, double a); // err: 2nd declaration...

};

Page 15: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

Point

class Point { public:static Point cartesian(double, double); //instantiatingstatic Point polar(double, double); //instantiating

private:Point(double, double);double x_;double y_;

};Point Point::cartesian(double x, double y) {return Point(x, y);

}Point Point::polar(double radius, double angle) {return Point(radius*cos(angle), radius*sin(angle));

}Point p(1,2); // private constructor

Point p1 = Point::cartesian(5.7, 1.2);

Page 16: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

To allocate all objects on the heap, make all the constructors, including copy constructors, private or protected and provide static functions that allocate objects on the heap and return these objects.

Heap

Page 17: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: General Philosophy of Design Patterns

In many projects you need to: • find the best way to create a complex object, • decide how to encapsulate some actions, • decide how to sequentially access the elements of an aggregate

without knowledge of how the aggregate is implemented, • find a way to provide for the undo-ing of some actions,• make other decisions involving interactions between classes and

objects.The software design pattern consists of four parts: the name of the

pattern, the problem, the solution, and finally the consequences of using the pattern.

Page 18: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: General Philosophy of Design Patterns (cont.)

You use patterns to find solutions to complex problems; solutions that have been tried and approved by a large community of experts, who have also catalogued these patterns:

• the King's gambit• recurring solutions to design problems that you see over and over• help you to design how objects communicate• provide a level of indirection that keeps classes from having to

know about each other’s internals• help you write more reusable programs.

Page 19: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: General Philosophy of Design Patterns (cont.)

The most useful patterns are generative, which means that they tell you how to create something.

There are:• Idioms - describe techniques for expressing low-level, language

dependent ideas• Patterns - medium-scale language-independent abstractions.• Software frameworks - consist of "sockets", into which the user

can plug in specific code to provide the required structure and behavior. For example, certain graphics applications, such as those available in Java, are frameworks.

Page 20: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Singleton Design Pattern

The Singleton Design Pattern:• used to constrain object instantiation to ensure that the client

cannot create more than one object of a particular class. • in Java, the class java.lang.Runtime is a singleton class

with a static method getRuntime(), responsible for returning a single instance of this class

• an example of a creational object pattern• to implement use a variant of the Instantiating Operations Idiom• can be used to ensure that there is exactly one window manager or

print spooler, a single-point entry to a database, a single telephone line for an active modem, or a single keyboard for a PC.

Page 21: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Singleton Design Pattern

Page 22: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

Singleton

class Singleton {public:static Singleton* instance();void op1(); // anything that is useful for this class

protected:Singleton();Singleton(const Singleton&);Singleton& operator=(const Singleton&);

private:static Singleton* onlyInstance_;

};Singleton* Singleton:: onlyInstance_ = 0;Singleton* Singleton::instance() {if(onlyInstance_ == 0)

onlyInstance_ = new Singleton;return onlyInstance_;

}

Page 23: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Singleton Design Pattern

1. The static method instance() uses lazy evaluation. You could throw an exception if one tries to create more than one instance.

2. The constructors and the copy constructor are private/protected:Singleton* s = new Singleton();

3. Private operations, such as a constructor, are defined for a specific application; for example, for a singleton printer spooler.

4. The client should access operations through instance() :Singleton::instance()->op1();

5. Remember to avoid a memory leak:delete Singleton::instance();

6. It is easy to extend the Singleton to allow for at most n instances

Page 24: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Declaring Namespaces

Namespace is similar to a class (defines a scope and anything that can be globally defined can also be defined in a namespace). But:

• there is no syntax to specify the accessibility of members• they are open

namespace Company {class Employee {...

};class NegativeSalaryException {...

};} // no semicolon here

Page 25: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Global namespace

• All declarations that are not explicitly placed in named namespaces are placed in the global namespace.

• Outside of the namespace, you use the qualification operator ::to refer to members of this namespace; for example:

Company::Employee e; // Use "::"

• Inside of the namespace, in definitions of its members, you can refer to a name Id in the global namespace, using ::Id:

::remove()

Page 26: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Namespace (cont.)

A namespace may contain only the declaration of a member; the corresponding definition may be provided outside of the namespace:

namespace Company {class Employee; // class declaration

...}

class Company::Employee { // class definition

...};

Page 27: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Namespace (cont.)

A namespace definition is open: you can add members to the existing namespace by writing a second namespace with the same name.

For example, assuming the definition of Company, you can add an additional member to this namespace:

namespace Company { // extend existing namespace

class Volunteer {...

};}

Page 28: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Namespace Aliases

A name given to a namespace has no structure, and so it is useful to have long names.

To simplify the use of long names, you can introduce one or more local aliases:

namespace Company_From_Nowhere...

namespace Company = Company_From_Nowhere; // alias

Company::Employee e; // using alias

Page 29: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Using Directive

Using the fully qualified names of features of the namespace can be considered a part of the documentation, but is cumbersome.

You may use a using directive (same as Java's import):

using namespace Company;Employee s; // means: Company::Employee s;

The using directive does not extend the scope but makes names from the namespace available.

There is no conflict between locally declared names and names declared in the namespace. Names locally declared take precedence over names from the namespace.

Page 30: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Using Declaration

Rather than importing an entire namespace, you can also select specific names from the namespace and add them to the local scope, employing a using declaration: using Company::print(); // add a declaration print(); // means: Company::print();

Since the using declaration adds a declaration to the scope, it may cause conflicts:void goo() {int value_;using ResourceNamespace::value_; // repeated

using Company::print(); print(); // Company::print()

::print(); // global print()

}

Page 31: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Standard Namespaces

Virtually all programs need:#include <string>

The standard library is declared in the standard namespace, called std. Therefore, many programs start by:

using namespace std; // using directive

If you want to use some specific members of this namespace, you can specify only the required ones; for example:

using std::string; // using declaration

Page 32: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Namespaces and Information Hiding

You can split a single namespace into several pieces, some of which are visible to the clients and some of which are not:

• place the "interface" sections of the namespace in the header file• place the implementation details in files that are available only in

a binary form, to be linked with the rest of the program.Maintaining a single resource, represented by an integer variable:#ifndef RESOURCE_H //File: resource.h

#define RESOURCE_H namespace ResourceNamespace {int value();void value(int);

} #endif

Page 33: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Namespaces and Information Hiding (cont.)

The client can now use this interface as follows:#include "resource.h"ResourceNamespace::value(4);

The implementation of the resource is not accessible to the client:#include "resource.h" // File: resource.cppnamespace ResourceNamespace { //extends ResourceNamespaceint value_ = 0; // private variable

}int ResourceNamespace::value() { return value_;

}void ResourceNamespace::value(int k) { value_ = k;

}

Page 34: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Helper Functions

Sometimes, you may also have additional helper functions that are not members of a class but are associated with the class:Matrix plus(const Matrix&, const Matrix&);

The header file should include the declarations of such functions:namespace MyMatrix { // File: mymatrix.hclass Matrix { // class declaration

...};

}

The implementation file contains definitions of all member functions, as well as the helper functions.

Page 35: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

Place a class declaration and all related helper functions in a single namespace.

Namespace

Page 36: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Unnamed Namespaces

Unnamed namespace are used to limit the scope of a declaration to the file containing this declaration:namespace { // unnamedint value() { // must be defined herereturn value_;

}void value(int k) { value_ = k;

} int value_ = 0;

}

Members of unnamed namespaces can be used without a qualification:cout << value();

Page 37: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

To limit the scope of a variable, function, or class to a single file, place it in an unnamed namespace.

Single File Scope

Page 38: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Need for Recompiling and Header Files, Part I

class Student { // File: ch4.student.h…

};

// File: ch4.accountforstudent.h#include "ch4.student.h" // compiles, but…class AccountForStudent {public:…

private:Student* stud_;double balance_;

};

Page 39: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Need for Recompiling and Header Files, Part I

What information is required from the class Student in order to compile the operations of AccountForStudent. If sees the definition of a variable of type Student:

Student s; // the compiler needs to know class' size

However, this information is not required for:- pointers or references to the class; e.g. Student* or Student&- operations with class parameters passed by value or returning objects

of this class; e.g.Student getStudent() or print(Student)

Page 40: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Need for Recompiling and Header Files, Part I

// File: ch4.accountforstudent.h

class Student; // declaration

class AccountForStudent {public:AccountForStudent(long number, double balance);~AccountForStudent();long getNumber() const;double getBalance() const;

private:Student* stud_; double balance_;

};

Page 41: Object-Based Programming Part II• implies that the part object may belong to only one whole object, and the lifetime of the composer and composee are identical. Circleuses the class

C++ Programming with Design Patterns Revealed Tomasz Müldner Copyright: Addison-Wesley Publishing Company, 2002

4: Need for Recompiling and Header Files, Part I

Note that the client of AccountForStudent will have to include both these header files:

#include "ch3.student.h"#include "ch3.accountforstudent.h"...AccountForStudent afs(10, 10.25);...

In C++, the interface and the implementation are not cleanly separated, and any changes in the implementation may require recompilation of the client's code. Later, we'll learn how a bridge design pattern can be used to separate the interface from the implementation.