diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c2082c..0003950 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,7 @@ set(APP_SOURCES "src/patterns/structural/Proxy.cpp" "src/patterns/structural/Composite.cpp" "src/patterns/structural/Flyweight.cpp" + "src/patterns/structural/Facade.cpp" ) # Test files diff --git a/docs/uml/patterns_structural_facade.drawio.svg b/docs/uml/patterns_structural_facade.drawio.svg new file mode 100644 index 0000000..30924e9 --- /dev/null +++ b/docs/uml/patterns_structural_facade.drawio.svg @@ -0,0 +1,4 @@ + + + +
Use
Use
Use
Client1
+ clientCode(..): type
Client2
+ clientCode(..): type
AuthSubSystem
+ field: type
+ method(type): type
LoggerSubSystem
+ field: type
+ method(type): type
BackendSubSystem
+ field: type
+ method(type): type
ValidatorSubSystem
+ field: type
+ method(type): type
Use
Use
Client1
+ clientCode(..): type
Client2
+ clientCode(..): type
AuthSubSystem
+ field: type
+ method(type): type
LoggerSubSystem
+ field: type
+ method(type): type
BackendSubSystem
+ field: type
+ method(type): type
ValidatorSubSystem
+ field: type
+ method(type): type
RequestFacade
- m_a: AuthSubSystem*
- m_l: LoggerSubSystem*
- m_v: ValidatorSubSystem*
- m_b: BackendSubSystem*
+ RequestFacade(type)
+ method1(type): type
+ method2(type): type
Use
Use
1
1
1
1
MockValidatorSubSystem
+ field: type
+ method(type): type
Extends
1
RequestFacade(const AuthSubSystem *s1 = nullptr
, const ValidatorSubSystem *s2 = nullptr
, const LoggerSubSystem *s3 = nullptr
, const BackendSubSystem *s4 = nullptr) 
{
this->auth_ = s1 ?: new AuthSubSystem; 
this->validator_ = s2 ?: new ValidatorSubSystem;
this->logger_ = s3 ?: new LoggerSubSystem;
this->backend_ = s4 ?: new BackendSubSystem; 
}

method1:
this->auth_->method();
this->validator_->method();
this->logger_->method();
this->backend_->method();

method2:
this->auth_->method();
\ No newline at end of file diff --git a/src/patterns/structural/Composite.cpp b/src/patterns/structural/Composite.cpp index ce2942e..602a7e6 100644 --- a/src/patterns/structural/Composite.cpp +++ b/src/patterns/structural/Composite.cpp @@ -4,7 +4,7 @@ // Appicability: // (*) when you have to implement a tree-like object structure. // (**) when you want the client code to treat both simple and complex elements uniformly. -// UML: docs/uml/patterns_structural_Composite.drawio.svg +// UML: docs/uml/patterns_structural_facade.drawio.svg #include #include diff --git a/src/patterns/structural/Facade.cpp b/src/patterns/structural/Facade.cpp new file mode 100644 index 0000000..d5a0742 --- /dev/null +++ b/src/patterns/structural/Facade.cpp @@ -0,0 +1,274 @@ +// cppcheck-suppress-file [functionStatic] + +// Facade is a structural design pattern that provides a simplified interface +// to a library, a framework, or any other complex set of classes. +// Appicability: +// (*) when you need to have a limited but straightforward interface to a complex subsystem. +// (**) when you want to structure a subsystem into layers. +// UML: none + +#include +namespace +{ + namespace Problem + { + + class AuthSubSystem + { + public: + virtual ~AuthSubSystem() = default; + void login() const + { + std::cout << "AuthSubSystem: login\n"; + } + }; + + class ValidatorSubSystem + { + public: + virtual ~ValidatorSubSystem() = default; + virtual void check() const + { + std::cout << "ValidatorSubSystem: check input\n"; + } + }; + + class LoggerSubSystem + { + public: + virtual ~LoggerSubSystem() = default; + void write() const + { + std::cout << "LoggerSubSystem: write log\n"; + } + }; + + class BackendSubSystem + { + public: + virtual ~BackendSubSystem() = default; + void send() const + { + std::cout << "BackendSubSystem: send request\n"; + } + }; + + namespace Client1 + { + // The client must manually interact with each subsystem. + // This creates unnecessary complexity. + void clientCode(const ValidatorSubSystem &s1, const AuthSubSystem &s2, const LoggerSubSystem &s3, const BackendSubSystem &s4) + { + s1.check(); + s2.login(); + s3.write(); + s4.send(); + + // In real code, the client must also + // - know call ordering + // - combine subsystem operations + // - manage lifecycle and error handling + } + } + + namespace Client2 + { + // The client must manually interact with each subsystem. + // This creates unnecessary complexity. + void clientCode(const ValidatorSubSystem &s1) + { + s1.check(); + } + } + + void run() + { + std::cout << "\n\nProblem\n"; + + ValidatorSubSystem v; + AuthSubSystem a; + LoggerSubSystem l; + BackendSubSystem b; + + Client1::clientCode(v, a, l, b); + Client2::clientCode(v); + } + } + + namespace Facade + { + + class AuthSubSystem + { + public: + virtual ~AuthSubSystem() = default; + void login() const + { + std::cout << "AuthSubSystem: login\n"; + } + }; + + class ValidatorSubSystem + { + public: + virtual ~ValidatorSubSystem() = default; + void check() const + { + std::cout << "ValidatorSubSystem: check input\n"; + } + }; + + class LoggerSubSystem + { + public: + virtual ~LoggerSubSystem() = default; + void write() const + { + std::cout << "LoggerSubSystem: write log\n"; + } + }; + + class BackendSubSystem + { + public: + virtual ~BackendSubSystem() = default; + virtual void send() const + { + std::cout << "BackendSubSystem: send request\n"; + } + }; + + // ======================= + // Mock subclasses (for testing) + // ======================= + class MockBackendSubSystem : public BackendSubSystem + { + public: + void send() const override + { + std::cout << "[MockBackend] fake-send\n"; + } + }; + + /** + * The Facade class provides a simple interface to the complex logic of one or + * several subsystems. The Facade delegates the client requests to the + * appropriate objects within the subsystem. The Facade is also responsible for + * managing their lifecycle. All of this shields the client from the undesired + * complexity of the subsystem. + */ + class RequestFacade + { + protected: + const AuthSubSystem *auth_; + const ValidatorSubSystem *validator_; + const LoggerSubSystem *logger_; + const BackendSubSystem *backend_; + + /** + * Depending on your application's needs, you can provide the Facade with + * existing subsystem objects or force the Facade to create them on its own. + */ + public: + /** + * In this case we will delegate the memory ownership to Facade Class + */ + explicit RequestFacade(const AuthSubSystem *s1 = nullptr, + const ValidatorSubSystem *s2 = nullptr, + const LoggerSubSystem *s3 = nullptr, + const BackendSubSystem *s4 = nullptr) + { + this->auth_ = s1 ?: new AuthSubSystem; + this->validator_ = s2 ?: new ValidatorSubSystem; + this->logger_ = s3 ?: new LoggerSubSystem; + this->backend_ = s4 ?: new BackendSubSystem; + } + + ~RequestFacade() + { + delete auth_; + delete validator_; + delete logger_; + delete backend_; + } + + /** + * The Facade's methods are convenient shortcuts to the sophisticated + * functionality of the subsystems. However, clients get only to a fraction of + * a subsystem's capabilities. + */ + void sendRequest() const + { + validator_->check(); + auth_->login(); + logger_->write(); + backend_->send(); + } + + void validate() const + { + validator_->check(); + } + }; + + namespace Client1 + { + /** + * The client code works with complex subsystems through a simple interface + * provided by the Facade. When a facade manages the lifecycle of the subsystem, + * the client might not even know about the existence of the subsystem. This + * approach lets you keep the complexity under control. + */ + void clientCode(const RequestFacade &facade) + { + facade.sendRequest(); + } + } + + namespace Client2 + { + /** + * The client code works with complex subsystems through a simple interface + * provided by the Facade. When a facade manages the lifecycle of the subsystem, + * the client might not even know about the existence of the subsystem. This + * approach lets you keep the complexity under control. + */ + void clientCode(const RequestFacade &facade) + { + facade.validate(); + } + } + + void run() + { + std::cout << "\n\nFacade\n"; + { + RequestFacade *facade = new RequestFacade(); + Client1::clientCode(*facade); + Client2::clientCode(*facade); + delete facade; + } + + { + // injected subsystems for mocktest + std::cout << "\n"; + const MockBackendSubSystem *b = new MockBackendSubSystem(); + RequestFacade *facade = new RequestFacade(nullptr, nullptr, nullptr, b); + Client1::clientCode(*facade); + delete facade; + } + } + } +} + +struct FacadeAutoRuner +{ + FacadeAutoRuner() + { + std::cout << "\n--- Facade Pattern Example ---\n"; + Problem::run(); + Facade::run(); + } +}; + +static FacadeAutoRuner instance; \ No newline at end of file