Last active
December 27, 2015 06:19
-
-
Save hostilefork/7280477 to your computer and use it in GitHub Desktop.
Conceptual example for Proxy/Facade pattern to submit to http://codereview.stackexchange.com/questions/33713/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Proxy/facade pattern idea. | |
// See http://codereview.stackexchange.com/questions/33713/ | |
// | |
#include <iostream> | |
#include <vector> | |
#include <memory> | |
#include <cstdlib> | |
using std::unique_ptr; | |
using std::shared_ptr; | |
using std::make_shared; | |
using std::vector; | |
using std::is_base_of; | |
using std::is_convertible; | |
using std::move; | |
using std::cout; | |
// | |
// Forward definitions | |
// | |
class Foo; | |
// | |
// FooPrivate | |
// | |
// Raw handle we never give directly to client. Structure may live in a | |
// memory mapped file; and adding data members to it would be misleading. | |
// Declared "final" and has no virtual methods. | |
// | |
class FooPrivate final { | |
friend class Foo; | |
template<class> friend class Reference; | |
template<class> friend class Owned; | |
template<class> friend struct std::default_delete; | |
private: | |
FooPrivate (int value) : _value (value) { } | |
virtual ~FooPrivate () { } | |
private: | |
static unique_ptr<FooPrivate> create(int value) { | |
return unique_ptr<FooPrivate> (new FooPrivate (value)); | |
} | |
public: | |
FooPrivate * consume(unique_ptr<FooPrivate> && other) { | |
_value += other->_value; | |
FooPrivate * result (other.release()); | |
_consumed.push_back(result); | |
return result; | |
} | |
void setValue(int value) { _value = value; } | |
int getValue() const { return _value; } | |
private: | |
int _value; | |
vector<FooPrivate *> _consumed; | |
}; | |
// | |
// Context | |
// | |
// Placeholder for context information that we want our wrapper to carry | |
// along with every handle. Shown as a flag here for simplicity. | |
// | |
class Context final { | |
friend class Foo; | |
template<class> friend class Reference; | |
template<class> friend class Owned; | |
private: | |
// Note: can't use make_shared when constructor is private | |
Context (bool valid) : _valid (valid) { } | |
private: | |
bool _valid; | |
}; | |
// | |
// Reference<FooType> | |
// | |
// Proxy/facade pattern; creates subtypes that can add additional accessor | |
// methods built on top of the basic ones offered by Foo. Class parameter | |
// is the accessor which should be publicly derived from Foo. | |
// | |
template<class FooType> | |
class Reference | |
{ | |
friend class Foo; | |
private: | |
Reference (FooPrivate * fooPrivate, shared_ptr<Context> context) { | |
static_assert( | |
is_base_of<Foo, FooType>::value, | |
"Reference parameter must be publicly derived from Foo" | |
); | |
_foo.setInternals(fooPrivate, context); | |
} | |
public: | |
Reference (Foo & foo) : _foo (foo) { } | |
template<class OtherFooType> | |
Reference (Reference<OtherFooType> & other) { | |
static_assert( | |
is_convertible<OtherFooType, FooType>::value, | |
"Cannot construct Reference from an incompatible Foo Type" | |
); | |
_foo.setInternals(other._fooPrivate, other._context); | |
} | |
FooType * operator-> () { return &_foo; } | |
FooType const * operator-> () const { return &_foo; } | |
FooType & getFoo() { return _foo; } | |
FooType const & getFoo() const { return _foo; } | |
private: | |
FooType _foo; | |
}; | |
// | |
// Owned<FooType> | |
// | |
// Combination between unique_ptr and the proxy/facade abilities of | |
// Reference. | |
// | |
template<class FooType> | |
class Owned | |
{ | |
friend class Foo; | |
private: | |
Owned ( | |
unique_ptr<FooPrivate> && fooPrivate, shared_ptr<Context> context | |
) { | |
static_assert( | |
is_base_of<Foo, FooType>::value, | |
"Owned template parameter must be publicly derived from Foo" | |
); | |
_foo.setInternals(fooPrivate.release(), context); | |
} | |
unique_ptr<FooPrivate> extractFooPrivate() | |
{ | |
unique_ptr<FooPrivate> result (_foo._fooPrivate); | |
_foo.setInternals(nullptr, nullptr); | |
return result; | |
} | |
// Disable default copying and assignment operators | |
Owned (Owned<FooType> const & foo); | |
Owned<FooType> operator= (Owned<FooType> const & foo); | |
public: | |
static Owned<FooType> create(int value) { | |
return Owned<FooType> ( | |
FooPrivate::create(value), | |
shared_ptr<Context> (new Context (true)) | |
); | |
} | |
template<class OtherFooType> | |
Owned (Owned<OtherFooType> && other) { | |
static_assert( | |
is_convertible<OtherFooType, FooType>::value, | |
"Cannot move-construct Owned from an incompatible Foo Type" | |
); | |
_foo.setInternals( | |
other.extractFooPrivate().release(), | |
other._foo._context | |
); | |
} | |
template<class OtherFooType> | |
Owned<FooType> operator= (Owned<OtherFooType> && other) { | |
static_assert( | |
is_convertible<OtherFooType, FooType>::value, | |
"Cannot move-assign Owned from an incompatible Foo Type" | |
); | |
return Owned<FooType> (other.extractFooPrivate(), other._context); | |
} | |
FooType * operator-> () { return &_foo; } | |
FooType const * operator-> () const { return &_foo; } | |
FooType & getFoo() { return _foo; } | |
FooType const & getFoo() const { return _foo; } | |
private: | |
FooType _foo; | |
}; | |
// | |
// Foo | |
// | |
// Basic wrapped abstraction of the raw handle with context. User code can | |
// legally use this directly, but Reference<Foo> is encouraged for | |
// consistency with other accessor patterns like Reference<FooDerived>. | |
// | |
class Foo { | |
template<class> friend class Reference; | |
template<class> friend class Owned; | |
template<class> friend struct std::default_delete; | |
private: | |
void setInternals(FooPrivate * fooPrivate, shared_ptr<Context> context) { | |
// Separate function for setting internals, used by Reference | |
// and Owned to make sure relevant members are set (these are | |
// private and not directly accessible by Foo derived classes) | |
_fooPrivate = fooPrivate; | |
_context = context; | |
} | |
protected: | |
Foo () { | |
setInternals(nullptr, nullptr); | |
} | |
private: | |
Foo (FooPrivate * fooPrivate, shared_ptr<Context> context) { | |
setInternals(fooPrivate, context); | |
} | |
FooPrivate const & getFooPrivate() const { | |
if (not _context->_valid) { | |
throw "Attempt to dereference invalid Foo handle."; | |
} | |
return *_fooPrivate; | |
} | |
FooPrivate & getFooPrivate() { | |
if (not _context->_valid) { | |
throw "Attempt to dereference invalid Foo handle."; | |
} | |
return *_fooPrivate; | |
} | |
public: | |
template<class FooType> | |
Reference<FooType> consume(Owned<FooType> && foo) { | |
return Reference<FooType> ( | |
getFooPrivate().consume(move(foo.extractFooPrivate())), | |
shared_ptr<Context> (new Context (false)) | |
); | |
} | |
void setValue(int value) { getFooPrivate().setValue(value); } | |
int getValue() const { return getFooPrivate().getValue(); } | |
private: | |
FooPrivate * _fooPrivate; | |
shared_ptr<Context> _context; | |
}; | |
// | |
// DoublingFoo | |
// | |
// A dumb accessor helper just to show the point. | |
// | |
class DoublingFoo : public Foo { | |
public: | |
int getDoubleValue() const { | |
return Foo::getValue() * 2; | |
} | |
Reference<Foo> consumeHelper(int value) { | |
return consume(Owned<Foo>::create(value * 2)); | |
} | |
}; | |
// | |
// A dumb basic test. | |
// | |
int main() | |
{ | |
auto parent (Owned<DoublingFoo>::create(10)); | |
auto child (Owned<Foo>::create(20)); | |
parent->consumeHelper(30); | |
Reference<Foo> childReference (parent->consume(move(child))); | |
cout << "The value is " << parent->getDoubleValue() << "\n"; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment