Lecture 20
Recall turtle and weapon example in last lecture.
Visitor Pattern Ctd.
visitor can be used to add functionality to existing classes, without changing or recompiling the classes themselves eg. Add a visitor to the Book hierarchy
// Compare this example to the turtle/weapon example
class Book { // Enemy
public:
virtual void accept (BookVisitor &v) { // beStruckBy
v.visit(*this);
}
};
class Text: public Book {
public:
void accept (BookVisitor &v) {
v.visit(*this);
}
};
class BookVisitor { // Weapon
public:
virtual void visit(Book &b) = 0; // strike
virtual void visit(Text &t) = 0;
virtual void visit(Comic &c) = 0;
};
Application: Track how many of each type of Book I have
Books: by author
Texts: by topic
Comics: by hero
How to collect this information?
Use a
map<string, int>
Could write a
virtual updateMap
method
OR write a visitor:
// look at cs246/1199/lectures/se/visitor example
struct Catalogue: public BookVisitor {
map<string, int> theCat;
void visit(Book &b) override {
++theCat[b.getAuthor()];
}
void visit(Text &t) override {
++theCat[t.getTopic()];
}
void visit(Comic &c) override {
++theCat[c.getHero()];
}
};
Won't compile - there is a cycle of includes
Book and BookVisitor include each other
whichever is first won't know about the second
Compilation Dependencies
When does a file need to
#include
another file?
Consider: Which of the following classes need to #include "a.h"
class A {...}; // a.h
// inherits from A (need to know the size of A)
#include "a.h"
class B: public A {...};
// is not a pointer here, you need to know the size of A
// so you have to include a.h here
#include "a.h"
class C: {
A a;
};
// has a pointer of type A, but pointers always have same size
// so knowing A exists is good enough for class D
// so not including a.h is fine here
class D: {
A *pa;
};
// function is not implemented yet, only declared, so do not details about A
class E {
A f(A a);
};
// Need details about A
#include "a.h"
class F{
A f(A a) {
a.g()
}
}
The Point: If the code doesn't need an #include
, don't create a needless compilation dependency by including unnecessarily.
When A changes, only A,B,C,F need recompilation In the implementations of D,E:
// d.cc
#include "a.h"
void D::f() {
pa->something(); // Need knowledge of A
}
Do the include in the .cc file instead of the .h file (where possible)
Now consider the XWindow class:
// this is all private data, but we can look at it
// **Do we know what it all means? Do we care?
class XWindow {
Display *d;
Window w;
int s;
GC gc;
unsigned long colours[10];
public:
...
};
**What if I need to add or change a private member? All clients must recompile, would be better to hide these details away.
Solution:
Bridge Pattern
Pimpl Idiom (Pointer to Implementation)
Create a second class XWindowImpl:
// XWindow Impl.h:
#include <Xll/Xlib.h>
struct XWindowImpl {
Display *d;
Window w;
int s;
GC gc;
unsigned long colours[10];
};
// Window.h
class XWindowImpl;
class XWindow {
XWindowImpl *pImpl;
public:
... // no change
};
No need to include Xlib.h
Forward declare the impl class
No compilation dependency on XWindowImpl.h
Clients also don't depend on XImpl.h
// Window.cc
#include "Window.h"
#include "XWindowImpl.h"
XWindow::XWindow(...);
pImpl {
new XWindowImpl {...};
}
XWindow::~xWindow() {
delete pImpl;
}
Other methods
replace fields d, w, s, gc, colours with
pImpl->d, pImpl->w
, etc.
Changing private fields => only recompile Window.cc and relink
Generalization: What if there are several possible window implementations, say XWindow and YWindow
then make Impl struct a superclass
UML
pImpl idiom and hierarchy or implementation is called the Bridge Pattern
Last updated
Was this helpful?