Last active
May 10, 2020 02:25
Demo of runtime-implementation design
This file contains 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
#include <iostream> | |
#include <memory> | |
/* | |
* This code provides an example design pattern to achieve the following: | |
* | |
* We want to implement Container class using two different implementations | |
* We further want to select its implementation during runtime | |
* We also want to be able to do the same for any classes derived from Container | |
* We even want to be able to templatize on Container class | |
*/ | |
/** | |
* interface that all implementations must bind to | |
*/ | |
class ContainerInterface { | |
public: | |
virtual ~ContainerInterface() = default; | |
virtual void identify() const = 0; | |
virtual void push_back(int) = 0; | |
virtual void pop_back() = 0; | |
virtual size_t size() const = 0; | |
virtual int &front() = 0; | |
virtual int &back() = 0; | |
}; | |
/** | |
* one type of implementation | |
*/ | |
class Array : public ContainerInterface { | |
public: | |
Array() = default; | |
void identify() const override { std::cout << "array" << std::endl; } | |
void push_back(int x) override { /* ... */ } | |
void pop_back() override { /* ... */ } | |
size_t size() const override { /* ... */ } | |
int &front() override { /* ... */ } | |
int &back() override { /* ... */ } | |
private: | |
/* ... */ | |
}; | |
/** | |
* another type of implementation | |
*/ | |
class List : public ContainerInterface { | |
public: | |
List() = default; | |
void identify() const override { std::cout << "list" << std::endl; } | |
void push_back(int x) override { /* ... */ } | |
void pop_back() override { /* ... */ } | |
size_t size() const override { /* ... */ } | |
int &front() override { /* ... */ } | |
int &back() override { /* ... */ } | |
private: | |
/* ... */ | |
}; | |
/** | |
* a concrete container taking one implementation | |
*/ | |
class Container : public ContainerInterface { | |
public: | |
explicit Container(std::unique_ptr<ContainerInterface> impl) | |
: impl_{std::move(impl)} {} | |
void identify() const override { impl_->identify(); } | |
void push_back(int x) override { impl_->push_back(x); } | |
void pop_back() override { impl_->pop_back(); } | |
size_t size() const override { return impl_->size(); } | |
int &front() override { return impl_->front(); } | |
int &back() override { return impl_->back(); } | |
private: | |
std::unique_ptr<ContainerInterface> impl_; | |
}; | |
/** | |
* Demonstration of templated private inheritance | |
* Queue is implemented through Container APIs | |
*/ | |
template<typename C = Container> | |
class Queue : private C { | |
public: | |
explicit Queue(std::unique_ptr<ContainerInterface> impl) : | |
Container{std::move(impl)} { | |
C::identify(); | |
} | |
void push(int x) { /* ... */ } | |
void pop() { /* ... */ } | |
const int& top() const { /* ... */ } | |
size_t size() const { return C::size(); } | |
bool empty() const { return C::empty(); } | |
}; | |
/** | |
* Demonstration of public inheritance | |
* AdvancedContainer extends Container APIs | |
* but its implementations should only rely on Container APIs | |
* and should not depend on implementation specific of array or list | |
*/ | |
class AdvancedContainer : public Container { | |
public: | |
explicit AdvancedContainer(std::unique_ptr<ContainerInterface> impl) : | |
Container{std::move(impl)} {} | |
bool empty() const { return size() == 0; } | |
}; | |
int main(int argc, const char** argv) { | |
// two copies of implementations depending on user input, determined during run-time | |
std::unique_ptr<ContainerInterface> impl1, impl2; | |
if (std::strcmp(argv[1], "array") == 0) { | |
impl1 = std::unique_ptr<ContainerInterface>(new Array); | |
impl2 = std::unique_ptr<ContainerInterface>(new Array); | |
} | |
else { | |
impl1 = std::unique_ptr<ContainerInterface>(new List); | |
impl2 = std::unique_ptr<ContainerInterface>(new List); | |
} | |
// both Queue and AdvancedContainer implementations are determined during run-time | |
Queue<Container> queue{std::move(impl1)}; | |
AdvancedContainer advancedContainer{std::move(impl2)}; | |
advancedContainer.identify(); | |
return 0; | |
} | |
/* | |
* run example | |
* $ ./a.out array | |
* array | |
* array | |
* | |
* $ ./a.out list | |
* list | |
* list | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment