Top Banner
From Code to Pattern Understanding classical GoF patterns with C++
92
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: From code to pattern, part one

From Code to PatternUnderstanding classical GoF patterns with C++

Page 2: From code to pattern, part one

Agenda

• Why this necessary

• OO fundamental

• OO in C++

• GoF classical pattern

• Creational

• Structural and behavioral

• Inter-operational

4:5

4:1

4 A

M

2

Page 3: From code to pattern, part one

Why this necessary

• Know what those terms mean on earth

• Know how they work

• Know why they came

• Know how to connect to your daily work

4:5

4:1

4 A

M

3

Page 4: From code to pattern, part one

OO concepts

Class

4:5

4:1

4 A

M

4

Page 5: From code to pattern, part one

OOD principles

• Program to an interface, not an implementation

• Favor object composition over class inheritance

• SOLID• Single responsibility principle

• A class exist only for one reason

• Open/closed principle• Add a new feature through extend exist class instead of modify it

• Liskov substitution principle• In all cases, subclass can substitute its parent without invalidate any

desirable state

• Interface segregation principle• Prefer specific interfaces than general purpose interface

• Dependency inversion principle• Depends on abstract instead of high-level or low-level module

4:5

4:1

4 A

M

5

Page 6: From code to pattern, part one

How OOD works

• Finding Appropriate Objects

• Find the nouns in user case/scenario

• Determining Object Granularity

• Refine the noun if it can do complete unrelated things

• Specifying Object Interfaces

• Define what the object can do

• Specifying Object Implementations

• Define how the object does

4:5

4:1

4 A

M

6

Page 7: From code to pattern, part one

The core of OO

• Type and class• Interface is the message it can handle• Type is interface set• Class is type implementation• A class may have(support) more types

• Messaging• The sender• The receiver• The message• The message body

• Inheritance relationship• “is-a”?• “has-a”?• “implement-a”?

• Abstraction• Bind data and behaviors together• Hide as more as possible information

4:5

4:1

4 A

M

7

Page 8: From code to pattern, part one

The core of OO in C++

• Type and class• Interface is the message it can handle Virtual function in abstract class• Type is interface set Abstract class• Class is type implementation Concrete class• A class may have(support) more types Multiple inheritance class

• Messaging• The sender None or implicit object• The receiver Named object• The message Named member method• The message body Parameters of member function

• Inheritance relationship• “is-a”? Public inheritance from class• “has-a”? Private inheritance or object composition• “implement-a”? Public inheritance from type

• Abstraction• Bind data and behaviors together Member variables and methods• Hide as more as possible information Access modifier

4:5

4:1

4 A

M

8

Page 9: From code to pattern, part one

The simplest case study

• Set-and-call style

// client end codevoid call() {Foo foo;foo.set_property("id", 100);foo.set_property("name", "wooooo");foo.bar();

}

• Some questions

• How does an object get to create?

• How does an object get to destroy?

• How to send a message to an object?

• How does the object handle a message?

• Try to identify the hardcode points

4:5

4:1

4 A

M

9

Page 10: From code to pattern, part one

How can it be flexible?

• Avoid hardcode! But what’s hardcode then?• Data hardcode

• Hardcoded path, name, value etc.

• Should be avoided always and easy to do

• Structure hardcode• Fixed class implementation

• Derivation relationship

• Use a class name directly

• Use a method name directly

• Add an extra layer• Structure layer

• Interactive layer

• We get flexibility with the cost of complexity!

• Be sensitive with tradeoff

4:5

4:1

4 A

M

10

Page 11: From code to pattern, part one

Design pattern, the definition

• Wikipedia: “… is general reusable solution to a commonly occurring problem in software design”

• GoF: “… names, abstracts, and identifies the key aspects of a common design structure that make it useful for creating a reusable object-oriented design”

• Essential of DP• Specific name• Scenario• Solution

• We are going to:• Show how simple codes are evolved and refactored to meet more

requirements and • Show how GoF classify and name it

• NOTE• The sample code does not care the delete of object. Just as object

creation, there are patterns to handle object lifecycle, such as RAII.

4:5

4:1

4 A

M

11

Page 12: From code to pattern, part one

Objects creation

4:5

4:1

4 A

M

12

Page 13: From code to pattern, part one

How to get an object? I

• You know the class name• Fully type information

available only at compile-time

• Stack based object• Pros

• Straight

• Easy to use

• Map to language structure directly

• Cons • Compile-time limitation

• Scope based Lifecycle

• Hardcoded type

• Strict couple of server end code and client end code

// server end codestruct Foo {void bar() { cout << “bar called\n"; }

};struct Foo2 {void bar() { cout << “bar2 called\n"; }

};void bang(Foo* foo, Foo2* foo2) {foo->bar();foo2->bar();

}

// client end codevoid call() {Foo foo;Foo2 foo2bang(&foo, &foo2);

}

4:5

4:1

4 A

M

13

Page 14: From code to pattern, part one

How to get an object? II

• Heap based object

• Pros

• Long life across scope

• Easy to use still

• Map to language structure directly

• Cons

• Hardcoded type name

• Still tightly coupling

// server end codestruct Foo {void bar() { cout << "bar called\n"; }

};struct Foo2 {void bar() { cout << "bar2 called\n"; }

};void bang(Foo* foo, Foo2* foo2) {foo->bar();foo2->bar();

}

// client end codeFoo* get_foo() { return new Foo; }Foo2* get_foo2() { return new Foo2; }void call() {Foo* foo = get_foo();Foo2* foo2 = get_foo2();bang(foo, foo2);

}

4:5

4:1

4 A

M

14

Page 15: From code to pattern, part one

How to get an object? III

• First try of refactor

• Abstract the interface

• Server end code cares about the interface that Foo exposes instead of Foo object

• Pros

• Decouple server end code so that it’ll not depends on the Foo definition

• Cons

• New derivation layer

• For any new implementation of IFoo interface, client end code must be modified

// server end codestruct IFoo {virtual void bar() = 0;

};void bang(const vector<IFoo*> vf) {for_each(vf.begin(), vf.end(),mem_fun(&IFoo::bar));

}

// client end codestruct Foo : IFoo {virtual void bar() { cout << "bar called\n"; }

};struct Foo2 : IFoo {virtual void bar() { cout << "bar2 called\n"; }

};IFoo* get_foo() { return new Foo; }IFoo* get_foo2() { return new Foo2; }void call() {vector<IFoo*> vf;vf.push_back(get_foo());vf.push_back(get_foo2());bang(vf);

}

4:5

4:1

4 A

M

15

Page 16: From code to pattern, part one

How to get an object? IV

• Second try of refactor

• Abstract the creation of object

• Decouple server end code and client code completely

• Pros

• Easy to add new IFoo interface implementation without change server end and client end code

• Server end code only define the interfaces and the business logics skeleton

• Cons

• Complex with double derivation layers

// server end codestruct IFoo {virtual void bar() = 0;

};struct IFooCreater {virtual IFoo* create() = 0;

};void bang(IFooCreater* fc) {fc->create() ->bar();

}

struct Foo : IFoo { virtual void bar() { cout << "bar called\n"; }

};struct Foo2 : IFoo {virtual void bar() { cout << "bar2 called\n"; }

};struct FooCreater : IFooCreater {enum FooType {TFoo, TFoo2} type_;FooCreater(FooType t) : type_(t) {}virtual IFoo* create() {if (type_ == TFoo) return new Foo;if (type_ == TFoo2) return new Foo2;assert(0); return 0;

}};void call() {bang(new FooCreater(FooCreater::TFoo));bang(new FooCreater(FooCreater::TFoo2));

}

4:5

4:1

4 A

M

16

Page 17: From code to pattern, part one

The Factory Method

• What’s that? The Factory method!

• GoF: “Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses” 4

:54

:14

AM

17

Page 18: From code to pattern, part one

How to get an object? I

• You have no class name, but an existed object

• Copy constructor?

• Need to know the class of the object

• RTTI?

• Not fully typed information

• There is no built-in support in C++!

• The first try

• Seems useless at all, I know it’s Foo here!

• An incorrect user scenario

// server end codeclass Foo {public: Foo* clone() {

return new Foo(*this);}

};vector<Foo*> foos;void bang(Foo* f) {Foo* baby = f->clone();// change some attributesfoos.push_back(baby);

}

// client end codestatic Foo default_foo;void call() {bang(&default_foo);

}

4:5

4:1

4 A

M

18

Page 19: From code to pattern, part one

How to get an object? II

• The second try

• Server end code should not care about the class of the object

• Pros

• Again, we move the concrete implementation to client end

• Again, we can easily add new

// server end codestruct IFoo {virtual void bar() = 0;virtual IFoo* clone() = 0;

};vector<IFoo*> foos;void bang(IFoo* f) {IFoo* baby = f->clone();// change some attributesfoos.push_back(baby);

}

// client end codeclass Foo : public IFoo {virtual Foo* clone() {return new Foo(*this);

}virtual void bar() {}

};static Foo default_foo;void call() {bang(&default_foo);

}

4:5

4:1

4 A

M

19

Page 20: From code to pattern, part one

The Prototype

• What’s that? The prototype!

• GoF: “Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype”

• Dynamic languages don’t need this pattern as all type information are available on runtime

4:5

4:1

4 A

M

20

Page 21: From code to pattern, part one

How to get the object? I

• In prototype sample, there is a default instance and all other copies are cloned from it.

• And the instance should be global unique

• Should not be constructed by user

• Should be ready before the first use

• Should be cleanup elegance

• Had better never be constructed if it’s not be used

• So

1. Private constructor

2. Runtime library constructed instance (conflict with 1) or runtime construct with condition

3. Resource management

4. Lazy-initialized

4:5

4:1

4 A

M

21

Page 22: From code to pattern, part one

How to get the object? II

• The initial implementation

• some issues

• IFoo implementation is bond to server end and not easy to extend

• Mixed functionalities of class FooSingleton: it implements both IFoo and singleton interface

// server end codestruct IFoo {virtual void bar() = 0;

};struct FooSingleton : public IFoo {static IFoo* get_instance() {if (!instance_)instance_ = new FooSingleton;

return instance_;}void bar() { cout << "bar called\n"; }

private:FooSingleton() {} static IFoo* instance_;

};IFoo* FooSingleton::instance_ = 0;

// client end codevoid call() {IFoo* foo = FooSingleton::get_instance();foo->bar();

}

4:5

4:1

4 A

M

22

Page 23: From code to pattern, part one

How to get the object? III

• The final try• Use another pattern we have

not discussed (the bridge pattern) to avoid the cohesion between interface and implementation

// server end codestruct IFoo {virtual void bar() = 0;

};struct IFooImpl {virtual void bar_impl() = 0;

};

struct FooSingleton : public IFoo {static IFoo* get_instance(IFooImpl* impl) {if (!instance_)instance_ = new FooSingleton(impl);

return instance_;}void bar() { impl_->bar_impl(); }

private:FooSingleton(IFooImpl* i) : impl_(i) {}IFooImpl* impl_;static IFoo* instance_;

};IFoo* FooSingleton::instance_ = 0;

// client end codestruct MyImpl : IFooImpl {void bar_impl() { cout << "bar called\n"; }

};

void call() {IFoo* foo = FooSingleton::get_instance(new MyImpl);foo->bar();

}

4:5

4:1

4 A

M

23

Page 24: From code to pattern, part one

The Singleton

• What’s that? The singleton pattern!

• GoF: “Ensure a class only has one instance, and provide a global point of access to it”

• More discussion on singleton• Thread safety consideration• Dependency among singletons• Double-check idioms• Return reference instead of pointer

4:5

4:1

4 A

M

24

Page 25: From code to pattern, part one

How to get some objects? I

• OK, we need some related or dependent objects

• Same class?

• Stack based object array or heap based dynamic object. Works only if the Foo has a default constructor.

// client end codevoid same_class() {Foo foos1[100];Foo* foos2 = new Foo[100];vector<Foo> foos3(100);// cannot return foos1// had better avoid return foos3

}

4:5

4:1

4 A

M

25

//// server end code, none

Page 26: From code to pattern, part one

How to get some objects? II

• Different class but same type?

• Construct directly

• Or use some factories

// server end codestrcut IFoo {virtual void bar() = 0;

};struct D1 : IFoo {virtual void bar() {...}

};struct D2 : IFoo {virtual void bar() {...}

};void bang(const vector<IFoo*>& fs) {for_each(fs.begin(),

fs.end(),mem_fun(&IFoo::bar));

}

// client end codevoid call() {vector<IFoo*> fs;fs.push_back(new D1);fs.push_back(new D2);bang(fs);

}

4:5

4:1

4 A

M

26

Page 27: From code to pattern, part one

How to get some objects? III

• Different type?

• Cannot get a list of object that have no shared type

• Instead, use a struct to warp them and make each object as a member and use them by name

• Then, can you more specific?

• Say, type1 and type2 can be subtyped both dynamically …

struct package {type1 v1;type2 v2;// …

};

void diff_type() {package p;// use(get/set) each member// can return if necessary

}

4:5

4:1

4 A

M

27

//// server end code, none

Page 28: From code to pattern, part one

How to get some objects? IV

• Let’s have a try

• Reconsider the scenario, we are building a C language compiler front end skeleton, and it includes preprocessor and assembler.

• How can we support another language, say C++, while will not affect the server end code?

// server end codestruct front_end {preprocesser* pp_;assembler* as_;void compile(string file) {as_->doit(pp_->doit(file));

}};void do_compile(string file) {front_end* fe = new front_end;fe->pp_ = new preprocesser;fe->as_ = new assembler;fe->compile(file)

}

// client end codevoid call() {do_compile("hello.c");

}

4:5

4:1

4 A

M

28

Page 29: From code to pattern, part one

How to get some objects? V

• Refactor it so that it can easy support more language

• Abstract interface of preprocess and assemble, push the implementation to client end.

// server end codestruct IPreprocesser {virtual string doit(string f) = 0;

};struct IAssemble {virtual string doit(string f) = 0;

};struct IFront_end_creator {virtual IPreprocesser* create_pp() = 0;virtual IAssemble* create_as() = 0;

};

class front_end {IPreprocesser* pp_;IAssemble* as_;

public:front_end(IFront_end_creator* c) {pp_ = c->create_pp();as_ = c->create_as();

}void compile(string file) {as_->doit(pp_->doit(file));

}};

4:5

4:1

4 A

M

29

Page 30: From code to pattern, part one

How to get some objects? VI

• The client code for C (right)

• The client code for C++ (below)

// client end code: C front endstruct CPreprocess : IPreprocesser {virtual string doit(string f) { return f; }

};struct CAssembale : IAssemble {virtual string doit(string f) { return f; }

};struct Cfe : IFront_end_creator {virtual IPreprocesser* create_pp(){return new CPreprocess;

} virtual IAssemble* create_as() {return new CAssembale;

}};

// Client end code: C++ front endstruct CPPPreprocess : IPreprocesser {virtual string doit(string f) { return f; }

};struct CPPAssembale : IAssemble {virtual string doit(string f) { return f; }

};struct CPPfe : IFront_end_creator {virtual IPreprocesser* create_pp() {return new CPPPreprocess;

} virtual IAssemble* create_as() {return new CPPAssembale;

}};

// client end code, how to usevoid call() {front_end cfe(new Cfe);fe.compile("hello.c");front_end cppfe(new CPPfe);fe.compile("hello.cpp");

}

4:5

4:1

4 A

M

30

Page 31: From code to pattern, part one

The Abstract Factory

• A little complex, let’s summary what we did by now• A derivation layer that create some related objects (IFront_end_creater)

• An object that depends on the creator to create objects of another derivation layers (IPreprocesser and IAssemble)

• What’s this? The Abstract Factory!

• GoF: “Provide an interface for creating families of related or dependent objects without specifying their concrete classes”

4:5

4:1

4 A

M

31

Page 32: From code to pattern, part one

How get an object from data? I

• Do you mean object serialization and deserialization?• Almost, but how?• so we have ...• Note, not clear boundary between

server and client.

• This is the simplest case and cannot fit complex scenarios such as the member is a point to some type• C++ has no reflect mechanism so

that we save type information to file directly and then we can construct an object from it directly

• Overriding works only if the concrete object is ready, but we have no yet. Chicken or eggs, eh?

struct record {int id_;string name_;void serialize(ostream& os) {os << id_;os << name_ << endl;

}void deserialize(istream& is) {is >> id_;is >> name_;

}};

void call(){record r;ifstream ifs("/root/data");r.deserialize(ifs);

}

4:5

4:1

4 A

M

32

Page 33: From code to pattern, part one

How get an object from data? II

• Assume we are going to serialize front_end object and we only check the process of deserializing.

• In order to get the class of concrete object, we must save the information as well as object data. So• each class derived from

serializer should have a static global unique ID

• we should serialize the ID before the object data

• The next question is: where does the relationship between ID and class exist?• we need another helper type!

// server end codestruct serializer {virtual void save(ostream& os) = 0;virtual void load(istream& is) = 0;

};struct IPreprocesser : serializer {virtual string doit(string f) = 0;

};struct IAssemble : serializer {virtual string doit(string f) = 0;

};

class front_end : serializer {IPreprocesser* pp_;IAssemble* as_;

public:virtual void save(ostream& os) {pp_.save(os);as_.save(os);

}virtual void load(istream& is) {// ??

}};

4:5

4:1

4 A

M

33

Page 34: From code to pattern, part one

How get an object from data? III

• The help class will help construct correct concrete object according the object ID.

• Server end code first get the object via helper and than load it

// server end codestruct front_end : serializer {IPreprosseser* pp_;IAssemble* as_;ILoader* loader_;virtual void save(ostream& os) {os << pp_->id() << endl;pp_->save(os);os << as_->id() << endl;as_->save(os);

}virtual void load(istream& is) {string pp_id, as_id;is >> pp_id;pp_ = loader_->get_pp(pp_id);pp_->load(is);

is >> as_id;as_ = loader_->get_as(as_id);as_->load(is);

}};

// server end codestruct serializer {virtual string id() const = 0;virtual void save(ostream&) = 0;virtual void load(istream&) = 0;

};struct IPreprosseser : serializer {};struct IAssemble : serializer {};

struct ILoader {virtual IPreprosseser* get_pp(string) = 0;

virtual IAssemble*get_as(string) = 0;

};

4:5

4:1

4 A

M

34

Page 35: From code to pattern, part one

How get an object from data? IV

• Client end provides the implementation of the helper class and specify different ID for different class.

struct Front_end_loader : ILoader {virtual IPreprosseser* get_pp(string id) {if (id == CPreprocess::uuid)return new CPreprocess;

else if (id == CPPPreprocess::uuid)return new CPPPreprocess;

else throw "invalid preprocesser class id";}virtual IAssemble* get_as(string id) {if (id == CAssemble::uuid)return new CAssemble;

else if (id == CPPAssemble::uuid)return new CPPAssemble;

else throw "invalid assemble class id";}

};

// C front end implementation code struct CPreprocess : IPreprocesser {static string uuid;virtual string id() const { return uuid; }virtual void save(ostream& os) const {}virtual void load(istream& is) {}

};string CPreprocess::uuid = "CPreprocess";

struct CAssemble : IAssemble {static string uuid;virtual string id() const { return uuid; }virtual void save(ostream& os) const {}virtual void load(istream& is) {}

};string CAssemble::uuid = "CAssemble";

void call(){const char* file = "C:\\test.dat";Front_end_loader loader;front_end fe(&loader);bk.load(ifstream(file));

}

4:5

4:1

4 A

M

35

Page 36: From code to pattern, part one

The Builder

• We get running codes which satisfy following requirements

• Type based serialization/deserialization

• Easy to add new subtype without need modify the server end code

• Focus on the part of loading. What’s that? The Builder Pattern!

• GoF: “Separate the construction of a complex object from its representation so that the same construction process can create different representations”

4:5

4:1

4 A

M

36

Page 37: From code to pattern, part one

Sidebar: resource management

• We ignore the issue of resource management at all in previous discussion

• There is no default GC support in C++, but we have an elegant alternative, that’s RAII based resource management policy

• Put it simple, use std::shared_ptr or std::unique_ptr always for all places that need a raw pointer

• For other non-pointer resource, such as kernel object handle, locker, database connection, network connection etc., use RAII plus reference counted wrapper if need.

• This is a big and serious topic and we need talk it separately.

4:5

4:1

4 A

M

37

Page 38: From code to pattern, part one

Sending a message

4:5

4:1

4 A

M

38

Page 39: From code to pattern, part one

The object and its behaviors

• Object• Object type. The message set the object accepts.

• Object class. The implementation of object types plus data that represent object status.

• Object Id. The unique identifier of any object.• For C++ language level object, it’s memory address of the object

• That is why objects of zero size class have different address

• Object behavior: how the object handle a message

• Object status decide the behaviors of it• Those behaviors that do not depend on status are static

behaviors and will not be discussed here

• How to change an object?• Set object status directly

• Call a r/w behavior of object

4:5

4:1

4 A

M

39

Page 40: From code to pattern, part one

Send a message to object

• Review C++ syntax of sending a message• Null or implicit caller

• Directly depends on the detailed concrete object or a pointer to a type

• Hardcoded message name, the member function name

• Hardcoded massage property, the argument of the operation

• The message is taken action at once

• In C++, the sender is null (in global function, class static member function) or implicitly (in class member function).

• Depends on the type implementation, C++ runtime will route the message to correct concrete object. • That’s polymorphism.

• C++ runtime only cares about the target object

• What if you want to routine the message by not only the type of target but the type of source?• That’s called double dispatch

• Switch/case? Be a respectful programmer and forget it!

4:5

4:1

4 A

M

40

Page 41: From code to pattern, part one

Double dispatch I

• Target uses C++ override mechanism to distinguish different source class and transfer the message back to source, the first dispatch.

• Source uses C++ overload mechanism to distinguish different target class, the second dispatch.

• We have follow initial implementation

// server end codestruct Source;struct Target { virtual void m(Source*) = 0; };struct T1 : Target {virtual void m(Source* s); };struct T2 : Target { virtual void m(Source* s); };

struct Source {virtual void m(T1*) = 0;virtual void m(T2*) = 0;

};struct S1 : Source {virtual void m(T1* t) { cout << "S1->T1\n"; }virtual void m(T2* t) { cout << "S1->T2\n"; }

};struct S2 : Source {virtual void m(T1* t) { cout << "S2->T1\n"; }virtual void m(T2* t) { cout << "S2->T2\n"; }

};

void T1::m(Source* s) { s->m(this); }void T2::m(Source* s) { s->m(this); }

// client end codevoid call(){Source* s1 = new S1;Source* s2 = new S2;Target* t1 = new T1;Target* t2 = new T2;t1->m(s1);t1->m(s2);t2->m(s1);t2->m(s2);

}

4:5

4:1

4 A

M

41

Page 42: From code to pattern, part one

Double dispatch II• Message m for target is only for exposing

itself so that source can do more. That is, it accepts that object and transfer the control back to the object, so we rename the message as “accept”

• Message m for source is the actually place we can handle different target type. It provides all possible target type overloads and visit them respectively, so we rename the message as “visit”

• Meanwhile, the type source provides all possible overloads of derived class of type Target. That means it can walk the class hierarchy of type Target. So we rename is as “Visitor”

• Finally, we get following codes.• What is it?

• It provides the possible that add new operation to an existed class hierarchy.

• The Visitor class is strong bound to Target class and any change of the Target class hierarchy will cause the change of Visitor interface.

// server end codestruct Visitor;struct Target {virtual void accept(Visitor*) = 0;

};struct T1 : Target {virtual void accept(Visitor* s);

};struct T2 : Target {virtual void accept(Visitor* s);

};struct Visitor{virtual void visit(T1*) = 0;virtual void visit(T2*) = 0;

};void T1::accept(Visitor* s) { s->visit(this); }void T2::accept(Visitor* s) { s->visit(this); }

4:5

4:1

4 A

M

42

Page 43: From code to pattern, part one

The Visitor

• What’s this? It’s Visitor pattern!

• GoF: “Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.”

4:5

4:1

4 A

M

43

Page 44: From code to pattern, part one

Send a message to an object

• Review C++ syntax of sending a message• Null or implicit caller

• Directly depends on the detailed concrete object or a pointer to a type

• Hardcoded message name, the member function name

• Hardcoded massage property, the argument of the operation

• The message is taken action at once

• How can it possible to erase those?

4:5

4:1

4 A

M

44

//serve end codestruct Target {void foo() { cout << "Target::foo called\n"; }

};

// client end codevoid call() {Target t;t.foo();

}

Page 45: From code to pattern, part one

Indirect messaging I

• Use an indirect layer to wrapper the message target

• Binding the message name to the wrapped message target

//serve end codestruct Target;struct Wrapper {typedef void (Target::*Action)();Target* t_;Action a_;

Wrapper(Target* t, Action a) : t_(t), a_(a) {}void call() const { (t_->*a_)(); }

};

struct Target {void foo() { cout << "Target::foo called\n"; }

};

// client end codevoid call() {Target t;Wrapper w(&t, &Target::foo);w.call();

}

4:5

4:1

4 A

M

45

Page 46: From code to pattern, part one

Indirect messaging II

• Try to refactor it

• Programming for interface

• Const correctness

• Use template to support multiple concrete classes

// serve end codestruct IWrapper {virtual void call() const= 0;

};

template <typename T>struct Wrapper : IWrapper {typedef void (T::*Action)();T* t_;Action a_;Wrapper(T* t, Action a) : t_(t), a_(a) {}virtual void call() const { (t_->*a_)(); }

};

// client end codestruct Target{void foo() { cout << "Target::foo called\n"; }

} t;

struct Another {void bar() { cout << "Another::foo called\n"; }

} a;

void call() {vector<IWrapper*> vw;vw.push_back(new Wrapper<Target>(&t, &Target::foo));

vw.push_back(new Wrapper<Target>(&a, &Another::foo));

for_each(vw.begin(), vw.end(),mem_fun(&IWrapper::call));

}

4:5

4:1

4 A

M

46

Page 47: From code to pattern, part one

The Command

• What do we get? The Command pattern!

• GoF: “Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations”

• Question

• How to support undo with this pattern?

4:5

4:1

4 A

M

47

Page 48: From code to pattern, part one

Broadcast a message

• With native C++ support, we can only send one message to one object once.

• How can we send a message to some objects?

• Loop on the object array/list/tree? 4:5

4:1

4 A

M

48

Page 49: From code to pattern, part one

Message broadcasting I

• In order to broadcast a message, we need

• The message to be broadcasted

• The target objects and they must can handle the message

• An very native implementation

//serve end codestruct Target1 {void foo() const { }

};struct Target2 {void foo() const { }

};

struct Broadcaster {Target1 t1_;Target1 t2_;void broadcast() {t1_.foo();t2_.foo();

}};

// client end codevoid call() {Broadcaster b;b.broadcast();

}

4:5

4:1

4 A

M

49

Page 50: From code to pattern, part one

Message broadcasting II

• Refactor actions• Abstract the unique interface

• Push code to client

• Dynamic target objects

//serve end codestruct ITarget {virtual void foo() const = 0;

};struct Broadcaster {vector<ITarget*> ts_;void broadcast() {for_each(ts_.begin(), ts_.end(),

mem_fun(&ITarget::foo));}void AddTarget(ITarget* t) {ts_.push_back(t);

}};

// client end codestruct Target1 : ITarget { virtual void foo() const { }

};struct Target2 : ITarget {virtual void foo() const { }

};

void call() {Broadcaster b;b.AddTarget(new Target1);b.AddTarget(new Target2);b.broadcast();

}

4:5

4:1

4 A

M

50

Page 51: From code to pattern, part one

Message broadcasting III

• It’s possible that the broadcaster itself will broadcast something if its state changes

• Provides subscribe & unsubscribe interface

• Callback to all subscribers if something occurs

//serve end codestruct ITarget {virtual void foo() const = 0;

};

struct Broadcaster {vector<ITarget*> ts_;void broadcast() {for_each(ts_.begin(), ts_.end(),

mem_fun(&ITarget::foo));}void subscribe(ITarget* t) {ts_.push_back(t);

}void unsubscribe(ITarget* t) {ts_.remove(t);

}};

4:5

4:1

4 A

M

51

Page 52: From code to pattern, part one

The Observer

• What’s that? The Observer pattern!

• GoF: “Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically” 4

:54

:14

AM

52

Page 53: From code to pattern, part one

Send a message to someone

• OO always requires specific target, sometimes it’s rigorous

• The target is unknown on sending time (distributed environment)

• The sender does not care who will handle it

• There are maybe some policies to control what target will handle the message (load balance)

• The target objects may have different priority to handle such message

• How can it possible?

• All problems in computer science can be solved by another level of indirection. So we need a gateway to get the message at first.

• Meanwhile, we also need routine the message to other if itself cannot handle it, so we need chains them together

4:5

4:1

4 A

M

53

Page 54: From code to pattern, part one

Uncertain handler I

• First try • Use a gateway class as the

intermediate layer

• Return if any handler handles the message

• Some problems• Strong relationship of

gateway class and concrete handlers. Difficult to adjust the order of handler

• Cannot represent the relationship among handlers, they are all at the same level

// server end codestruct IFoo {virtual void bar(int) const = 0;virtual bool is_care(int) const = 0;

};

struct gateway : IFoo {vector<IFoo*> fs_;mutable bool handled_ = false;virtual void bar(int n) const {for (size_t i = 0; i < fs_.size(); ++i) {if (fs_[i]->is_care(n)) {fs_[i].bar(n);break;

}}

}virtual bool is_care(int) const { return false; }void add_handler(IFoo* i) { fs_.push_back(i); }

};

4:5

4:1

4 A

M

54

Page 55: From code to pattern, part one

Uncertain handler I, Cont

• Code at call site// client end codestruct zero_handler : IFoo {virtual void bar(int) const {cout << "zero handler\n"; }

virtual bool is_care(int n) const {return n == 0; }

};struct odd_handler : IFoo {virtual void bar(int) const {cout << "odd handler\n"; }

virtual bool is_care(int n) const {return n && n%2 != 0; }

};struct even_handler : IFoo {virtual void bar(int) const { cout << "even handler\n"; }

virtual bool is_care(int n) const {return n && n%2 == 0; }

};

4:5

4:1

4 A

M

55

// client end codevoid call() {gateway gw;gw.add_handler(new odd_handler);gw.add_handler(new even_handler);gw.add_handler(new zero_handler);gw.bar(7);gw.bar(12);gw.bar(0);

}

Page 56: From code to pattern, part one

Uncertain handler II

• Second try

• Using a link structure

• If one handler does not care this message, it will routine the message to its successor if there is

// server end codestruct IFoo {virtual void bar(int) const = 0;

};

struct IChainFoo : IFoo {IFoo* next_;explicit IChainFoo(IChainFoo* next = 0) :

next_(next) {}virtual bool is_care(int n) const = 0;virtual void bar_impl() const = 0;virtual void bar(int n) const {if (is_care(n))bar_impl();

else if (next_)next_->bar(n);

}};

// client end code// concrete handle implementation ignoredvoid call() {IChainFoo* chain = new odd_handler(new zero_handler(new even_handler()));

chain->bar(7);chain->bar(12);chain->bar(0);

}

4:5

4:1

4 A

M

56

Page 57: From code to pattern, part one

The chain of responsibility

• What’s that of linked message handling? The chain of responsibility pattern!

• GoF: “Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it”

4:5

4:1

4 A

M

57

Page 58: From code to pattern, part one

Load elephant into icebox I

• The very old story, how can you load an elephant into a refrigerator? Three steps!• Open the door• Load the elephant• Close the door

• We have right initial draft• Some comments

• Foo defines the steps by implement bar() interface while providing default implementation (violate SRP)

• Difficult to extend

// server end codestruct IFoo {virtual void bar() = 0;

};struct Foo : IFoo {void open_refrigerator() {cout << "open very big one ...\n";

}void load_elephant() {cout << "load the elephant if it will\n";

}void close_refrigerator() {cout << "close it\n";

}virtual void bar() {open_refrigerator();load_elephant();close_refrigerator();

}};

// client end codevoid call() {IFoo* foo = new Foo;foo->bar();

}

4:5

4:1

4 A

M

58

Page 59: From code to pattern, part one

Load elephant into icebox II

• Refactor it to:• Push the implementation to client

• Decouple the definition and implementation

• So we have

// server end codestruct IFoo {virtual void bar() = 0;

};struct IFooImpl {virtual void open_refrigerator() = 0;virtual void load_elephant() = 0;virtual void close_refrigerator() = 0;

};

struct Foo : IFoo {IFooImpl* impl_;explicit Foo(IFooImpl* i) : impl_(i) {}virtual void bar() {open_refrigerator();load_elephant();close_refrigerator();

}};

// client end codestruct MySolution : IFooImpl {void open_refrigerator() {cout << "open very big one ...\n";

}void load_elephant() {cout << "load the elephant if it will\n";

}void close_refrigerator() {cout << "close it\n";

}};void call() {IFoo* foo = new Foo(new MySolution);foo->bar();

}

4:5

4:1

4 A

M

59

Page 60: From code to pattern, part one

The template method

• What’s that of base class defines skeleton while derived class provides implementation? The template method pattern!

• GoF: “Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure”

• Our example is a little different with the default structure as we push the implementation to client end• Similar to Bridge pattern but with different intent.

4:5

4:1

4 A

M

60

Page 61: From code to pattern, part one

Handling message

4:5

4:1

4 A

M

61

Page 62: From code to pattern, part one

Handle message directly I

• As behaviors of an object are decided by its state, so there is a typical implementation.

• Possible issues

• Difficult to extend. If we want to do something special action for state 4, we must modify the Foo implementation

• Had better avoid switch/case or if/else if/else structure

• Indeed, an actually bug there!

// server end codestruct Foo {int state_;void do_one() { cout << “action one\n”; }void do_two() { cout << “action two\n”; }void do_infinity() { cout << “action infinity\n”; }void set(int s) { state_ = s; }void bar() {switch(state_) {case 1: do_one();case 2: do_two();default: do_infinity();}

}};

// client end codevoid call() {Foo foo;foo.set(2);foo.bar();

}

4:5

4:1

4 A

M

62

Page 63: From code to pattern, part one

Handle message directly II

• Refactor

• Server end codes are written for interface only

• Push actually functional codes to client end

• Avoid switch/case by strong type

// server end codestruct IFoo_action { virtual void do_action() = 0;

};struct Foo {IFoo_action* action_;void set_action(IFoo_action* a) { action_ = a; }void bar() { action_->do_action(); }

};

// client end codestruct Action_for_one : IFoo_action {void do_action() { cout << "action one\n"; }

};struct Action_for_two : IFoo_action {void do_action() { cout << "action two\n"; }

};struct Action_for_infinity : IFoo_action {void do_action() { cout << "action infinity\n"; }

};

// client end codevoid call() {Foo foo;foo.set_action(new Action_for_two);foo.bar();

}

4:5

4:1

4 A

M

63

Page 64: From code to pattern, part one

The strategy

• What’s that? The strategy pattern!

• GoF: “Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it” 4

:54

:14

AM

64

Page 65: From code to pattern, part one

Enhance message handling I

• There is an existed implementation of some types and you want to:

• Reuse the implementation

• Enhancement some specific behaviors, such as log, authentication, lock/unlock etc

• The initial try is as right

• Drawbacks

• How about LSP?

• Difficult to extend

// server end codestruct IFoo {virtual void bar() = 0;virtual void baz() = 0;

};struct Foo : IFoo {void bar() { cout << "action bar\n"; }void baz() { cout << "action baz\n"; }

};

// client end codevoid do_log(const char* msg) {cout << msg << "\n";

}void call() {Foo foo;do_log("before call bar");foo.bar();

}

4:5

4:1

4 A

M

65

Page 66: From code to pattern, part one

Enhance message handling II

• What does LSP say:

• Wiki : “if S is a subtype of T, then objects of type T … may be replaced with objects of type S (i.e., objects of type S maybe substituted for objects of type T), without altering any of the desirable properties of that program (correctness, task performed, etc.)”

• Now we can treat the Foo object and our customized MyFoo object in the same way

// server end codestruct IFoo {virtual void bar() = 0;

};struct Foo : IFoo {void bar() { cout << "action bar\n"; }

};

// client end codevoid do_log(const char* msg) { cout << msg << "\n";

}struct MyFoo : Foo {void bar() {do_log("before call bar"); Foo::bar();

}};void call() {vector<IFoo*> vfs;vfo.push_back(new Foo);vfo.push_back(new MyFoo);for_each(vfs.begin(), vfs.end(),

mem_fun(&IFoo::bar));}

4:5

4:1

4 A

M

66

Page 67: From code to pattern, part one

The decorator

• What’s that? The decorator pattern!

• GoF: “Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality” 4

:54

:14

AM

67

Page 68: From code to pattern, part one

Delegated message handling

• Sometime, creating an object has a big overhead or just impossible

• A distributed object maybe resident on another address space, so we need RPC infrastructure as the basis.

• You have the right to use it only

• Creating and destroying such object often is not desirable

4:5

4:1

4 A

M

68

Page 69: From code to pattern, part one

The proxy (TODO)

• What’s that? The proxy pattern!

• GoF: “Provide a surrogate or placeholder for another object to control access to it”• Remote proxy. For distributed OO system

• Virtual proxy. For lazy loading or initialization

• Protected proxy. For access control

• Smart pointer. For resource management or verification

4:5

4:1

4 A

M

69

Page 70: From code to pattern, part one

The state (TODO)

• What’s that? The state pattern!

• GoF: “Allow an object to alter its behavior when its internal state changes. The object will appear to change its class”

4:5

4:1

4 A

M

70

Page 71: From code to pattern, part one

Separated object serialize (I)

• Image a ring buffer implementation• It gets store from client• It manipulates the buffer

• Question: how to serialize the ring buffer object?

• Answer one: serialize the buffer also• Bad solution. Client may have

done this. Duplication!

• Answer two: just serialize front/back status• Not good enough yet. After

deserialized, the object status is not complete.

4:5

4:1

4 A

M

71

// server end codestruct RingBuffer {explicit RingBuffer(char* buff, size_t sz)

: _size(sz), _buff(buff), _front(0), _back(_buff){}void push_back(char value) { … }

private:size_t _size;char* _buff;char* _frontchar* _back;

};

// client end codevoid call() {char buff[1024];RingBuffer rb(buff, 1024);const char* msg = “hello world!”;std::copy(msg, msg + strlen(msg),

std::back_inserter<RingBuffer>(rb));}

Page 72: From code to pattern, part one

Separated object serialize (II)

• Answer three: delegate the serialization task client fully

• Save internal status to a Memo object

• Client can save this object

• Client can restore this object later

• Will

4:5

4:1

4 A

M

72

// server end codestruct RingBuffer {explicit RingBuffer(char* buff, size_t sz) {…}void push_back(char value) { … }struct Memo {size_t _sz;size_t _off;Memo(size_t sz, size_t off) : _sz(sz), _off(off) {}

};Memo* create_memo() const {…}void set_memo(Memo* m) {…}

};

Page 73: From code to pattern, part one

The memento (TODO)

• What’s that? The memento pattern!

• GoF: “Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later” 4

:54

:14

AM

73

Page 74: From code to pattern, part one

Object relationship

4:5

4:1

4 A

M

74

Page 75: From code to pattern, part one

The adapter

• What’s that? The adapter pattern!

• GoF: “Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces” 4

:54

:14

AM

75

Page 76: From code to pattern, part one

Special object including I

• Many times, the object and its owner same type, that is, they implement the same interface and can be used interchangeable

• File system. A folder can includes folders and files and they are all file system objects

• Windows system. A dialog may include some buttons, edit boxes, combos etc. they are all windows indeed.

• Image following client codes

// client end code

void call() {Dialog* dia = new Dialog("type your password");dia->attach(new Button("OK"));dia->attach(new Button("Cancel"));dia->attach(new EditBox("Edit here"));dia->enable();

}

4:5

4:1

4 A

M

76

Page 77: From code to pattern, part one

Special object including II// server end codestruct IWin{std::string name_;virtual void enable() = 0;// more windows properties

};struct Button : IWin{Button(const char* name) { name_ = name; }void enable() { cout << "Button " << name_ << endl; }

};struct EditBox : IWin{EditBox(const char* name) { name_ = name; }void enable() { cout << "EditBox " << name_ << endl; }

};struct Dialog : IWin{Dialog(const char* name) { name_ = name; }void attach(IWin* w) { wins_.push_back(w); }void enable() {for_each(wins_.begin(), wins_.end(),

mem_fun(&IWin::enable));cout << "Dialog \"" << name_ <<"\" enable\n";

}list<IWin*> wins_;

};

• And this is the server end code

• All concrete classes implement the same type

• If one of them to be a container, it contains a list of pointer to that type

• On message handling, before/after its own handling mechanism, it’ll forward the message to all objects it contains

4:5

4:1

4 A

M

77

Page 78: From code to pattern, part one

The composite

• What’s that? The composite pattern!

• GoF: “Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly”

• The pattern is seldom used alone, some other pattern make it more useful• Visitor. Add extra functionality to the class struct• Iterator. Enumerate and manipulate each subobjects• …

4:5

4:1

4 A

M

78

Page 79: From code to pattern, part one

Interface implementation

4:5

4:1

4 A

M

79

Page 80: From code to pattern, part one

Implementation an interface I

• Simple and straight, C++ provides directly support

• There is a very big limitation however

• The implementation is bond to server end code and change the implementation will definitely change server end code

• How can we remove it?

• Add new indirection layer

• Push responsibility to client

// server end codestruct IFoo {virtual void bar() = 0;

};struct Foo : IFoo {void bar() { cout << "IFoo implemented\n"; }

};

// client end codevoid call() {Ifoo* foo = new Foo;foo->bar();

}

4:5

4:1

4 A

M

80

Page 81: From code to pattern, part one

Implementation an interface II

• Distinguish the type for interface and the type for implementation

• Client end will provides the concrete implementation while server end only care about the interface to implementation

• C++ usually calls this as p-impl idiom and use it to eliminate the strong header including relationship

// server end codestruct IFoo {virtual void bar() = 0;

};struct IFooImpl {virtual void bar_impl() = 0;

};struct Foo : public IFoo {IFooImpl* impl_;Foo(IFooImpl* i) : impl_(i) {}void bar() { impl_->bar_impl(); }

};

// client end codestruct MyImpl : public IFooImpl {virtual void bar_impl() { cout << "my IFoo implementation\n"; }

};struct MyImplv2 : public IFooImpl {virtual void bar_impl() {cout << "my IFoo implementation v2\n"; }

};

void call() {vector<IFoo*> vf;vf.push_back(new Foo(new MyImpl));vf.push_back(new Foo(new MyImplv2));for_each(vf.begin(), vf.end(),

mem_fun(&IFoo::bar));}

4:5

4:1

4 A

M

81

Page 82: From code to pattern, part one

The bridge

• What’s that? The bridge pattern!

• GoF: “Decouple an abstraction from its implementation so that the two can vary independently”

4:5

4:1

4 A

M

82

Page 83: From code to pattern, part one

Interface management

4:5

4:1

4 A

M

83

Page 84: From code to pattern, part one

Using a package I

• Usually, we use some classes of a package to do something• Typical, it’s the case that

exporting some APIs for 3rd

part use

• Pitfalls• Sometimes you may not want

expose the details of internal interfaces of server end code to client

• Meanwhile, you don’t want to hide something that hard to use correctly or secrets

• Client end code depends on the internal components of server end code, which make it’s very difficult to change server end implementation

// server end codenamespace domain {struct Foo {void do_foo() { cout << "Foo used\n"; }

};struct Bar {void do_bar() { cout << "Bar used\n"; }

};struct Baz {void do_baz() { cout << "Baz used\n"; }

};}

// client end codevoid call() {domain::Foo f;domain::Bar b;domain::Baz z;f.do_foo();b.do_bar();z.do_baz();

}

4:5

4:1

4 A

M

84

Page 85: From code to pattern, part one

Using a package II

• Refactor it• Add another layer• Provide a new interface and

standard the implementation of such common used tasks

• The original interfaces may or may not available to client codes, depends on how your codes are used by client you expect

• Client end code will not continue care about how the interfaces are implemented. They only use them! Simply and easily.

// server end codenamespace domain {struct Foo {void do_foo() { cout << "Foo used\n"; }

};struct Bar {void do_bar() { cout << "Bar used\n"; }

};struct Baz {void do_baz() { cout << "Baz used\n"; }

};struct FooBarBaz {Foo f_;Bar b_;Baz z_;void do_foobarbaz {f.do_foo();b.do_bar();z.do_baz();

}};}

// client end codevoid call() {domain::FooBarBaz fbz;fbz.do_foobarbaz ();

}

4:5

4:1

4 A

M

85

Page 86: From code to pattern, part one

The facade

• What’s that? The façade pattern!

• GoF: “Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use”

• This is typical design method of package level interface. On the view of client end, the package it used is much like another objects while delegate all messages to its internal constructs.

• Client end should always depend on the package interface instead of its internal implementation. If fine granularity controlling is needed, just export those necessary, wrapped subsystem interfaces.

4:5

4:1

4 A

M

86

Page 87: From code to pattern, part one

The mediator (TODO)

• What’s that? The mediator pattern!

• GoF: “Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently”

4:5

4:1

4 A

M

87

Page 88: From code to pattern, part one

The iterator (TODO)

• What’s that? The iterator pattern!

• GoF: “Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation” 4

:54

:14

AM

88

Page 89: From code to pattern, part one

The rest GoF patterns

4:5

4:1

4 A

M

89

Page 90: From code to pattern, part one

Shared fine-grained object

4:5

4:1

4 A

M

90

Page 91: From code to pattern, part one

The flyweight (TODO)

• GoF: “Use sharing to support large numbers of fine-grained objects efficiently”

4:5

4:1

4 A

M

91

Page 92: From code to pattern, part one

The interpreter (TODO)

• GoF: “Given a language, define a represention for its grammar along with an interpreter that uses the representation to interpret sentences in the language”

4:5

4:1

4 A

M

92