Title: Object Oriented Programming
1Object Oriented Programming
- Spring 2008
- Recitation 10
2Design Patterns
3Design Patterns
- When designing a software solution, programmers
often come across the same kind of problems. - Design patterns are a set of techniques which
help to overcome these problems. - These problems are not domain-specific and can
occur in any type of application. - An example for a problem How to implement
operatorltlt while using polymorphism?This is not
answered with a design pattern but its an
example of a problem which is not domain-specific.
4Design Problem
- We will see a couple of design patterns today.
- For each one we will describe
- The problem
- Possible solutions
- The design pattern that we should use
- Code example
5Singleton
6Problem Definition
- You wrote a Logger class
- The class can write information about the
applications actions, errors and statistics to a
log file. - You want the class to be accessible from
everywhere in the application it should have
global access. This is because every class can
write to the log file at any time. - You want that at anytime there will be no more
than one object of that class instantiated.This
is because you dont want two objects to write to
the same log or other logs.
7UML description and class code
// Logger.h include ltstringgtusing namespace
std class Logger public Logger(string
logFilenamelog.txt) log_filename
logFilename OpenLogFile() ClearLog() void
ClearLog() /.../ void WriteToLog(string
message) /.../ private void
OpenLogFile()// string log_filename
8Possible (wrong) solutions
- Solution 1 Define a global object of class
Logger
//Logger.hinclude ltstringgtusing namespace
std class Logger ... extern Logeger
g_logger // declaration of global logger object
//Logger.cppinclude Logger.h Logger g_logger
// definition of global logger object
9Possible (wrong) solutions
- The class usage will look something like
- Why is it a bad solution?
- Making more object of class Logger is not
prevented.The user might make more objects and
the compiler will not prevent it. - There is always at least one object because a
global object is always created at the start of
the program. We want the object to be created
only if it is needed.
g_logger.WriteToLog(Error occored!)
10Possible (wrong) solutions
- Why is it a bad solution?
- We are contaminating the global name domain. If
the same name is defined as a global variable,
there will be ambiguity. This can happen in a
large-scale program. - The order in which global objects are created is
not known in advanced. If another global object
needs our logger (or vice-versa), we will have a
problem.
11Possible (wrong) solutions
- Solution 2 Define all members as static
//Logger.h include ltstringgtusing namespace
std class Logger public static void
ClearLog() /.../ static void
WriteToLog(string message) /.../ private
static void OpenLogFile() // static string
log_filename
//Logger.cpp include ltstinggt include
Logger.h string Loggerlog_filename
log.txt
12Possible (wrong) solutions
- In this solution, it is guaranteed that only one
copy of each member will be created (several
objects can still be created, but they all share
the same static members). - Furthermore, we do not contaminate the global
domain. - Why is it a bad solution?
- Again, object is created even if not needed.
- Again, order of creation is not known.
13The Singleton solution
- The Logger class will have
- A static function called Instance() which will
return a static object of the class. - The constructor will be protected so no new
object would be constructed from outside of the
class. - Also define the copy-ctor as protected.
14Singleton c code
// Logger.h include ltstringgtusing namespace
std class Logger public void ClearLog()
/.../ void WriteToLog(string message)
/.../ // global point of access to the sole
instance static Logger Instance() static
Logger object return object protected Logger(
string logFilenamelog.txt) log_filename
logFilename OpenLogFile() ClearLog() Logger
(const Logger) private void OpenLogFile()//
string log_filename
15Singleton User Code
//main.cpp include Logger.h include
ltstringgt include ltiostreamgtusing namespace
std void main() int num cout gtgt please enter
a number between 1 and 10 gtgt endl cin ltlt
num if (!(numgt1 numlt10)) LoggerInstance()
.WriteToLog(invalid number!) return 0
16Singleton pros and cons
- Pros
- Does not contaminate the global domain.
- The singleton object is available when needed, so
if another class needs it, the Instance method
will create it. - Cons
- Hard to use with inheritance.
- Cannot be used in multi-threaded applications.
17Prototype and Prototype-Based Factory
18Problem Definition
- You have a Message interface which is implemented
as an abstract base class. - You inherit three kind of messages
- Fax
- Mail
- Memo
- We want to be able to create a copy of the object
polymorphicly. (i.e. create a copy of an object
to which we have a pointer of type Message)
19Prototype
- We actually discussed the prototype design
pattern last week. - We defined a clone() method which returned a
copy of theobject without knowing theconcrete
type.
20Prototype
- class Message
-
- public
- virtual Message()
- virtual Message clone() const 0
- virtual void set(const string s1, const string
s2) 0 - virtual void print() const 0
class Fax public Message public Fax()
m_number(0) Fax(const string num, const string
image) m_number(atol(num.c_str())),
m_image(image) virtual Message clone() const
return new Fax(this) virtual void set(const
string num, const string image) m_numberatol(num
.c_str()) m_imageimage virtual void print()
const cout ltlt " Fax num" ltlt m_number ltlt "
Image" ltlt m_image ltlt "\n" private long
m_number string m_image
21Problem Definition
- We want to display a menu to the user, in which
he will select the type of message to create.
22Possible (wrong) solution
- Use a switch-case block, and switch over the
returned value from the menu.
include "message.h" class NewMessageDialog ...
int show() // display dialog and return selected
index Message run() Message pm int i
show() // get message type index switch(i) case
0 // Fax pm new Fax break case 1 //
Mail pm new Mail break case 2 //
etc. return pm
23Possible (wrong) solution
- Why is it a bad solution?
- It is time-consuming
- Iterating over all options takes O(n) time on
worse case - It isnt modular
- Adding and removing options is dangerous and
error-prone. - It isnt dynamic
- All possibilities have to be determined when
writing the code, so new options cannot be added
at run-time and options cannot be removed.
24Prototype-based Factorysolution
- We will add a new pure virtual method called
Get_Type(), which will return the name of the
class as a string.
class Message public virtual Message()
virtual Message clone() const 0 virtual
string get_type() const 0 virtual void
set(const string s1, const string s2)
0 virtual void print() const 0
25Prototype-Based Factory Code
class Mail public Message public Mail()
Mail(const string addr, const string text)
m_address(addr), m_text(text) virtual Message
clone() const return new Mail(this) virtual
string get_type() const return "Mail" virtual
void set(const string addr, const string
text) m_addressaddr m_texttext virtual void
print() const cout ltlt " Mail address"
ltlt m_address ltlt ", text" ltlt m_text ltlt
"\n" private string m_address string
m_text
26Solution Continued
- Create a new (singleton) class called Factory
which will hold a map of prototypes, which are of
type Message, and their string representations. - The menu will then call the factory to create a
new object.
27Factory class code
- include ltmapgt
- include ltstringgt
- using namespace std
- include "Message.h"
- // Factory class - Creates Message Objects by
their type name. - // - Singleton
- class Factory
-
- public
- void add(const Message m) m_prototypesm-gtget_ty
pe() m - Message create_object(const string type)
- return m_prototypestype-gtclone()
- static Factory instance() // singleton access
method - static Factory factory
- return factory
28- protected
- Factory()
- Factory(const Factory )
- Factory()
- for(mapltconst string, const Message gtiterator
i - m_prototypes.begin() i ! m_prototypes.end()
i) - delete i-gtsecond
-
- private
- mapltconst string, const Message gt m_prototypes
29- When a user selects an option from the menu, the
factory will clone() the correct object from the
map.
class NewMessageDialog public void
add_button(string name) string show() Message
run() string sel show() return
Factoryinstance().create_object(sel)
- A global function will register and fill the map
void register_prototypes() Factoryinstance().a
dd_proto("Fax", new Fax) Factoryinstance().add_
proto("Mail", new Mail) Factoryinstance().add_p
roto("Memo", new Memo)
30How To Use
- int main()
- register_prototypes()
- NewMessageDialog dialog
- Message p_new_msg dialog.run()
- // use the new message...
- delete p_new_msg
- return 0