420 likes | 552 Views
Design Patterns. Structural Patterns. Adapter. Convert the interface of a class into another interface clients expect Adapter lets classes work together that couldn't otherwise because of incompatible interfaces Use the Adapter pattern when:
E N D
Design Patterns Structural Patterns
Adapter • Convert the interface of a class into another interface clients expect • Adapter lets classes work together that couldn't otherwise because of incompatible interfaces • Use the Adapter pattern when: • you want to use an existing class and its interface does not match the one you need • you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing everyone. An object adapter can adapt the interface of its parent class
Bridge • Decouple an abstraction from its implementation so that the two can vary independently • Use the Bridge pattern when: • you want run-time binding of the implementation • you want to share an implementation among multiple objects
Composite • Compose objects into tree structures to represent whole-part hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly • Use this pattern whenever you have "composites that contain components, each of which could be a composite".
Decorator • Attach additional responsibilities to an object dynamically • Decorators provide a flexible alternative to subclassing for extending functionality
Problems • Several classes with a similar operation (method), but different behavior. • We want to use many combinations of these behaviors
Example - Automated Hospital • People come to the reception with problems • They describe their problems • A special doctoRobot is created that is specialized to treat their special situations.
Problems with solution-1 • Sometimes we don’t have multiple inheritance. • Even if we have, if is problematic, and bad design. • 2n possible classes to create before compilation.
Code Example Widget* aWidget = new BorderDecorator( new HorScrollDecorator( new VerScrollDecorator( new TextWidget( 80, 24 )))); aWidget->draw(); Stream* aStream = new CompressingStream( new ASCII7Stream( new FileStream( "fileName.dat" ))); aStream->putString( "Hello world" );
Benefits • Flexible • Don’t have to foresee all combinations • Little objects Possible problems • Performance • Decorators are not necessarily always cummutative(surgeon and Anastasiolic)
Facade • 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 • Create a class that is the interface to the subsystem • Clients interface with the Facade class to deal with the subsystem • It hides the implementation of the subsystem from clients • It promotes weak coupling between the subsystems and its clients • It does not prevent clients from using subsystems class, should it?
Flyweight • Use sharing to support large numbers of fine-grained objects efficiently • The pattern can be used when: • The program uses a large number of objects and • Storage cost are high because of the sheer quantity of objects and • The program does not use object identity (==)
Proxy • Provide a surrogate or placeholder for another object to control access to it. • The proxy has the same interface as the original object • Virtual Proxy: • Creates/accesses expensive objects on demand • You may wish to delay creating an expensive object until it is really accessed • It may be too expensive to keep entire state of the object in memory at one time
Protection Proxy • Provides different objects different level of access to original object • Cache Proxy (Server Proxy) • Multiple local clients can share results from expensive operations: remote accesses or long computations • Firewall Proxy • Protect local clients from outside world
Reference Counting • Don’t bother to make a copy of something until you really need one:Be Lazy ?! • Use someone else’s copy as long as you can get away with it. Class String { … }; String s1 = “Hello”; String s2=s1; cout << s1; cout << s1 + s2; S2.convertToUpperCase();
Reference Counting • Copying the string contents in every • Assignment, • Argument passing, and • Function returning value • Can be both • Expensive, and • Unnecessary!
The basic String class class String { public: String(const char* value= “”); String(const String& rhs); String& operator=(const String& rhs); … private: char *date; };
The common implementation String& String::operator=(const String& rhs) { if(this == &rhs) return * this; delete [] data; data = new char[strlen(rhs.data) + 1]; strcpy(data,rhs.data); return *this; }
Using the String class String a,b,c,d,e; a=b=c=d=e=“Hello” String A Hello\0 String B Hello\0 String C Hello\0 String D Hello\0 String E Hello\0
The Idea Behind Reference Counting String A Reference String B String C 5 Hello\0 String D String E
Implement The Reference Counting class String{ private: struct StringRef{ int refCount; char *data; StringRef(const char *initValue); ~StringRef(); }; StringRef *value; public: String(const char* value= “”); String(const String& rhs); String& operator=(const String& rhs); … };
StringRef class implementation String::StringRef::StringRef(const char* initVal) : refCount(1) { data = new char[strlen(initVal) + 1]; strcpy(data,initVal); } String::StringRef::~StringRef() { delete []data; }
String class implementation String::String(const char *initVal) : value(new StringRef(initVal)) {} String::String(const String& rhs) : value(rhs.value) { ++value->refCount; } String::~String() { if(--value->refCount == 0) delete value; }
String class implementation String& String::operator=(const String& rhs) { if (value == rhs.value) return *this; if (--vaule->refCount == 0) delete value; value = rhs.value; ++value->refCount; return *this; }
Using the String class String s1(“More Effective c++”); String s2(“More Effective c++”); String s3=s2; String s4; S4=s3; StringRef s1 More Effective c++\0 1 s2 s3 More Effective c++\0 3 s4
Copy on Write Class String { … public: char operator[](int index) const; char& operator[](int index); … }; char String::operator[](int index) const { return value->data[index]; }
Copy on Write char& String::operator[](int index) { if(value->refCount > 1){ --value->refCount; value = new StringRef(value->data); } return value->data[index]; } … String s1; const String s2; … cout << s1 << s2; s1[5] = ‘x’;
Distinguishing Reads from Writes • Reading a reference counting is cheap • Writing require splitting off a new copy String s = “H. Simpson”; cout << s[5]; S[5] = ‘I’;
Distinguishing Reads from Writes via operator[] class String{ public: class CharProxy{ public: CharProxy(String& str,int index); CharProxy& operator=(const CharProxy& rhs); CharProxy& opeartor=(char c); operator char() const; private: String& theString; int charIndex; }; const CharProxy operator[](int index) const; CharProxy operator[](int index); … friend class CharProxy; private: struct StringRef{…}; StringRef *value; };
Distinguishing Reads from Writes via operator[] const String::CharProxy String::operator[](int index) const { return CharProxy(const_cast<String&>(*this),index); } String::CharProxy String::operator[](int index) { return CharProxy(*this,index); } String::CharProxy::CharProxy(String& str,int index) : theString(str),charIndex(index){} String::CharProxy::operator char() const { return theString.value->data[charIndex]; }
Distinguishing Reads from Writes via operator[] String::CharProxy& String::CharProxy::operator=(const CharProxy& rhs) { if(theString.value->reCount > 1) theString.value = new StringRef(theString.value->data); theString.value->data[charIndex] = rhs.theString.value->data[charIndex]; return *this; } String::CharProxy& String::CharProxy::operator=(char c) { if(theString.value->reCount > 1) theString.value = new StringRef(theString.value->data); theString.value->data[charIndex] = c; return *this; }
Using the proxy class String s1,s2; … cout << s1[5]; s1[5] = ‘x’; s1[3] = s2[8];