diff --git a/CMakeLists.txt b/CMakeLists.txt index 495f334..4b60f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,7 @@ set(APP_SOURCES "src/core/datatypes/class/CDestructors.cpp" "src/patterns/structural/Adapter.cpp" "src/patterns/structural/Bridge.cpp" + "src/patterns/structural/Proxy.cpp" ) # Test files diff --git a/docs/uml/patterns_structural_bridge.drawio.svg b/docs/uml/patterns_structural_bridge.drawio.svg index 00b262f..5931500 100644 --- a/docs/uml/patterns_structural_bridge.drawio.svg +++ b/docs/uml/patterns_structural_bridge.drawio.svg @@ -1,4 +1,4 @@ -
Widget
 
+ clickOn(type): type
Client
+ clientCode(const Widget* widget): type
Use
Use
WidgetAbstraction

# _implementation : OsImplementation 
+ WidgetAbstraction(Implementation* impl)
+ clickOn(type): type
<<Interface>>
OsImplementation
+ clickOnImplement(type): type
1
Aggregation
WindowsImplementation
+ clickOnImplement(type): type
WindowsImplementation
+ clickOnImplement(type): type
...
+ clickOnImplement(type): type
1...n
Button
 
 
Label
 
 
Extends
Extends
ButtonWindows
 
+ clickOn(type): type
ButtonLinux
 
+ clickOn(type): type
LabelWindows
 
+ clickOn(type): type
LabelLinux
 
+ clickOn(type): type
Extends
Extends
Extends
Extends
Client
+ clientCode(const Widget* widget): type
ButtonAbstraction

+ ButtonAbstraction(Implementation* impl)
+ clickOn(type): type
LabelAbtraction
+ LabelAbtraction(Implementation* impl)
+ clickOn(type): type
...
+ clickOn(type): type
Extends
Extends
Extends
1...n
clickOn(){
this->_implementation->clickOnImplement()
...
}
bridge
\ No newline at end of file +
Widget
 
+ clickOn(type): type
Client
+ clientCode(const Widget* widget): type
Use
Use
WidgetAbstraction

# _implementation : OsImplementation* 
+ WidgetAbstraction(Implementation* impl)
+ clickOn(type): type
<<Interface>>
OsImplementation
+ clickOnImplement(type): type
1
Aggregation
WindowsImplementation
+ clickOnImplement(type): type
WindowsImplementation
+ clickOnImplement(type): type
...
+ clickOnImplement(type): type
1...n
Button
 
 
Label
 
 
Extends
Extends
ButtonWindows
 
+ clickOn(type): type
ButtonLinux
 
+ clickOn(type): type
LabelWindows
 
+ clickOn(type): type
LabelLinux
 
+ clickOn(type): type
Extends
Extends
Extends
Extends
Client
+ clientCode(const Widget* widget): type
ButtonAbstraction

+ ButtonAbstraction(Implementation* impl)
+ clickOn(type): type
LabelAbtraction
+ LabelAbtraction(Implementation* impl)
+ clickOn(type): type
...
+ clickOn(type): type
Extends
Extends
Extends
1...n
clickOn(){
this->_implementation->clickOnImplement()
...
}
bridge
\ No newline at end of file diff --git a/docs/uml/patterns_structural_proxy.drawio.svg b/docs/uml/patterns_structural_proxy.drawio.svg new file mode 100644 index 0000000..79dd3cf --- /dev/null +++ b/docs/uml/patterns_structural_proxy.drawio.svg @@ -0,0 +1,4 @@ + + + +proxy
Client
+ clientCode(IServer* s): type

<<Interface>>
IServer


+ requestName(): void
+ requestData(): void

+ requestFile(): void

Use

Server


- _id: string


+ Server(string id)

+ request1(): void
+ request2(): void

+ request3(): void

Implementation
int main(int argc, char* argv[ ]){
string id = "admin";
IServer server = new Server(id);
// P1
clientCode(server);
delete server;
}
// P2,P3...
void request*(){
 if(_id != "admin"){
return;
 }
 // do request
}
Client
+ clientCode(IServer* s): type

<<Interface>>
IServer


+ requestName(): void
+ requestData(): void

+ requestFile(): void

Use

Server


- _id: string


+ Server(string id)

+ request1(): void
+ request2(): void

+ request3(): void

Implementation

ServerProxy


- _id: string

- _server:  Server*


- checkAccess(): bool

- logAccess(): bool

+ ServerProxy(string id)

~ ServerProxy();

+ request1(): void
+ request2(): void

+ request3(): void

1
Composition
Implementation
bool checkAccess(){
 if(_id != "admin"){
return false;

 if(_server == nullptr){
 _server = new Server(_id);
}
}

void request*(){
 if(checkAccess()){
_server->request*();
 logAccess();
}
}

~ServerProxy(){
if(_server != nullptr){
delete _server;
}
int main(int argc, char* argv[ ]){
string id = "admin";
IServer server = new ServerProxy(id);
clientCode(server);
delete server;
}
\ No newline at end of file diff --git a/src/patterns/structural/Adapter.cpp b/src/patterns/structural/Adapter.cpp index 9d72a94..9174b22 100644 --- a/src/patterns/structural/Adapter.cpp +++ b/src/patterns/structural/Adapter.cpp @@ -1,6 +1,8 @@ +// Adapters make legacy code work with modern classes. +// UML: docs/uml/patterns_structural_adapter.drawio.svg + #include -// Adapters make legacy code work with modern classes. namespace AdapterPattern { /** diff --git a/src/patterns/structural/Bridge.cpp b/src/patterns/structural/Bridge.cpp index 796d08d..ea974bd 100644 --- a/src/patterns/structural/Bridge.cpp +++ b/src/patterns/structural/Bridge.cpp @@ -1,207 +1,214 @@ -#include +// Bridge lets we split a large class or a set of closely related classes +// into two separate hierarchies—abstraction and implementation +// which can be developed independently of each other. +// UML: docs/uml/patterns_structural_bridge.drawio.svg -namespace Problem +#include +namespace { - class Widget - { - public: - virtual ~Widget() = default; - virtual std::string clickOn() const = 0; - }; - - /* Concrete variations for Button */ - class Button : public Widget + namespace Problem { - public: - virtual ~Button() = default; - - public: - std::string clickOn() const override + class Widget { - return "Click on: Button\n"; - } - }; + public: + virtual ~Widget() = default; + virtual std::string clickOn() const = 0; + }; - class ButtonWindows : public Button - { - public: - std::string clickOn() const override + /* Concrete variations for Button */ + class Button : public Widget { - return "[Linux]" + Button::clickOn(); - } - }; - - class ButtonLinux : public Button - { - public: - std::string clickOn() const override - { - return "[Windows]" + Button::clickOn(); - } - }; + public: + virtual ~Button() = default; - /* Concrete variations for Label */ - class Label : public Widget - { - public: - virtual ~Label() = default; + public: + std::string clickOn() const override + { + return "Click on: Button\n"; + } + }; - public: - std::string clickOn() const override + class ButtonWindows : public Button { - return "Click on: Label\n"; - } - }; - - class LabelWindows : public Label - { - public: - std::string clickOn() const override + public: + std::string clickOn() const override + { + return "[Linux]" + Button::clickOn(); + } + }; + + class ButtonLinux : public Button { - return "[Windows]" + Label::clickOn(); - } - }; - - class LabelLinux : public Label - { - public: - std::string clickOn() const override + public: + std::string clickOn() const override + { + return "[Windows]" + Button::clickOn(); + } + }; + + /* Concrete variations for Label */ + class Label : public Widget { - return "[Linux]" + Label::clickOn(); - } - }; + public: + virtual ~Label() = default; - /* Concrete variations for others widgets like Text,CCombo or new platform macOS etc*/ - // [Problem 1] We have to write the Text/TextLinux ... + public: + std::string clickOn() const override + { + return "Click on: Label\n"; + } + }; - namespace Client - { - void clientCode(const Widget *widget) + class LabelWindows : public Label { - if (widget != nullptr) - std::cout << widget->clickOn(); - } - } + public: + std::string clickOn() const override + { + return "[Windows]" + Label::clickOn(); + } + }; + + class LabelLinux : public Label + { + public: + std::string clickOn() const override + { + return "[Linux]" + Label::clickOn(); + } + }; - void run() - { - // [Problem 2] : Use the Bridge if you need to be able to switch implementations at runtime. how to exmaple for this - // still don't know - Widget *button = new ButtonWindows(); - Client::clientCode(button); - delete button; - } -} + /* Concrete variations for others widgets like Text,CCombo or new platform macOS etc*/ + // [Problem 1] We have to write the Text/TextLinux ... -namespace BridgePattern -{ - /** - * The Implementation defines the interface for all implementation classes. It - * doesn't have to match the Abstraction's interface. In fact, the two - * interfaces can be entirely different. Typically the Implementation interface - * provides only primitive Widgets, while the Abstraction defines higher- - * level Widgets based on those primitives. - */ - class OsImplemetation - { - public: - virtual std::string clickOnImplement() const = 0; - virtual ~OsImplemetation() = default; - }; - - class WindowsImplemetation : public OsImplemetation - { - public: - std::string clickOnImplement() const override + namespace Client { - return "[Windows]"; + void clientCode(const Widget *widget) + { + if (widget != nullptr) + std::cout << widget->clickOn(); + } } - }; - class LinuxImplemetation : public OsImplemetation - { - public: - std::string clickOnImplement() const override + void run() { - return "[Linux]"; + // [Problem 2] : Use the Bridge if you need to be able to switch implementations at runtime. how to exmaple for this + // still don't know + Widget *button = new ButtonWindows(); + Client::clientCode(button); + delete button; } - }; + } - /** - * The Abstraction defines the interface for the "control" part of the two class - * hierarchies. It maintains a reference to an object of the Implementation - * hierarchy and delegates all of the real work to this object. - */ - class WidgetAbstraction + namespace BridgePattern { - protected: - OsImplemetation *_implementation; - - public: - explicit WidgetAbstraction(OsImplemetation *implemetation) : _implementation{implemetation} + /** + * The Implementation defines the interface for all implementation classes. It + * doesn't have to match the Abstraction's interface. In fact, the two + * interfaces can be entirely different. Typically the Implementation interface + * provides only primitive Widgets, while the Abstraction defines higher- + * level Widgets based on those primitives. + */ + class OsImplemetation { - } - virtual ~WidgetAbstraction() = default; - - virtual std::string clickOn() const = 0; - }; + public: + virtual std::string clickOnImplement() const = 0; + virtual ~OsImplemetation() = default; + }; - /** - * We can extend the Abstraction without changing the Implementation classes. - */ - class ButtonAbstraction : public WidgetAbstraction - { - public: - explicit ButtonAbstraction(OsImplemetation *implemetation) : WidgetAbstraction{implemetation} {} - std::string clickOn() const override + class WindowsImplemetation : public OsImplemetation { - return this->_implementation->clickOnImplement() + "Click on: Button\n"; - } - }; - - class LabelAbstraction : public WidgetAbstraction - { - public: - explicit LabelAbstraction(OsImplemetation *implemetation) : WidgetAbstraction{implemetation} {} - std::string clickOn() const override + public: + std::string clickOnImplement() const override + { + return "[Windows]"; + } + }; + + class LinuxImplemetation : public OsImplemetation { - return this->_implementation->clickOnImplement() + "Click on: Label\n"; - } - }; - - namespace Client - { - void clientCode(const WidgetAbstraction *widget) + public: + std::string clickOnImplement() const override + { + return "[Linux]"; + } + }; + + /** + * The Abstraction defines the interface for the "control" part of the two class + * hierarchies. It maintains a reference to an object of the Implementation + * hierarchy and delegates all of the real work to this object. + */ + class WidgetAbstraction { - if (widget != nullptr) - std::cout << widget->clickOn(); + protected: + OsImplemetation *_implementation; + + public: + explicit WidgetAbstraction(OsImplemetation *implemetation) : _implementation{implemetation} + { + } + virtual ~WidgetAbstraction() = default; + + virtual std::string clickOn() const = 0; + }; + + /** + * We can extend the Abstraction without changing the Implementation classes. + */ + class ButtonAbstraction : public WidgetAbstraction + { + public: + explicit ButtonAbstraction(OsImplemetation *implemetation) : WidgetAbstraction{implemetation} {} + std::string clickOn() const override + { + return this->_implementation->clickOnImplement() + "Click on: Button\n"; + } + }; + + class LabelAbstraction : public WidgetAbstraction + { + public: + explicit LabelAbstraction(OsImplemetation *implemetation) : WidgetAbstraction{implemetation} {} + std::string clickOn() const override + { + return this->_implementation->clickOnImplement() + "Click on: Label\n"; + } + }; + + namespace Client + { + void clientCode(const WidgetAbstraction *widget) + { + if (widget != nullptr) + std::cout << widget->clickOn(); + } } - } - void run() - { - // TODO: check memory leak here - OsImplemetation *os = new WindowsImplemetation(); - WidgetAbstraction *widget = new ButtonAbstraction(os); - Client::clientCode(widget); + void run() + { + // TODO: check memory leak here + OsImplemetation *os = new WindowsImplemetation(); + WidgetAbstraction *widget = new ButtonAbstraction(os); + Client::clientCode(widget); - os = new LinuxImplemetation(); - widget = new LabelAbstraction(os); - Client::clientCode(widget); + os = new LinuxImplemetation(); + widget = new LabelAbstraction(os); + Client::clientCode(widget); - delete os; - delete widget; + delete os; + delete widget; + } } -} -struct BridgeAutoruner -{ - BridgeAutoruner() + struct BridgeAutoruner { - std::cout << "\n--- Bridge Pattern Example ---\n"; - Problem::run(); - BridgePattern::run(); - } -}; + BridgeAutoruner() + { + std::cout << "\n--- Bridge Pattern Example ---\n"; + Problem::run(); + BridgePattern::run(); + } + }; -static BridgeAutoruner instance; \ No newline at end of file + static BridgeAutoruner instance; +} \ No newline at end of file diff --git a/src/patterns/structural/Proxy.cpp b/src/patterns/structural/Proxy.cpp new file mode 100644 index 0000000..be12cc4 --- /dev/null +++ b/src/patterns/structural/Proxy.cpp @@ -0,0 +1,257 @@ +// Proxy is a structural design pattern that provides an object that acts as a substitute for a real service object used by a client. +// A proxy receives client requests, does some work (access control, caching, etc.) and then passes the request to a service object. +// UML: docs/uml/patterns_structural_proxy.drawio.svg + +#include +namespace +{ + namespace Problem + { + const std::string admin = "admin"; + + class IServer + { + public: + virtual ~IServer() = default; + virtual void request1() = 0; + virtual void request2() = 0; + virtual void request3() = 0; + }; + + class Server : public IServer + { + private: + std::string _id; + + public: + explicit Server(const std::string &id) : _id{id} + { + // [P1] Heavy or complex construction, so ideally should be lazy-loaded + std::cout << "[Server] Constructor: " << _id << "\n"; + } + + // [P2] Need access control + // [P3] Need to log requests without modifying the Server itself + void request1() override + { + if (_id != admin) + { + std::cout << "[Server] Invalid ID: " << _id << "\n"; + return; + } + std::cout << "[Server] Handling request-1 for: " << _id << "\n"; + } + + void request2() override + { + if (_id != admin) + { + std::cout << "[Server] Invalid ID: " << _id << "\n"; + return; + } + std::cout << "[Server] Handling request-2 for: " << _id << "\n"; + } + + void request3() override + { + if (_id != admin) + { + std::cout << "[Server] Invalid ID: " << _id << "\n"; + return; + } + std::cout << "[Server] Handling request-3 for: " << _id << "\n"; + } + }; + + namespace Client + { + void clientCode(IServer *s) + { + if (s != nullptr) + { + s->request1(); + s->request2(); + s->request3(); + } + } + } + + void run() + { + std::cout << "\n\n"; + + { + std::string connectionId = "admin"; + // [P4] The Server is constructed immediately even if we do not call any requests + IServer *server = new Server(connectionId); + std::cout << "User request\n"; + Client::clientCode(server); + delete server; + } + + { + // [P4] Server is constructed even for invalid ID, wasting resources + std::string invalidId = "xxx"; + Server *server = new Server(invalidId); + Client::clientCode(server); + delete server; + } + } + } + + namespace ProxyPattern + { + const std::string admin = "admin"; + + class IServer + { + public: + virtual ~IServer() = default; + virtual void request1() = 0; + virtual void request2() = 0; + virtual void request3() = 0; + }; + + class Server : public IServer + { + private: + std::string _id; + + public: + explicit Server(const std::string &id) : _id{id} + { + std::cout << "[Server] Constructor: " << _id << "\n"; + } + + void request1() override + { + std::cout << "[Server] Handling request-1 for: " << _id << "\n"; + } + + void request2() override + { + std::cout << "[Server] Handling request-2 for: " << _id << "\n"; + } + + void request3() override + { + std::cout << "[Server] Handling request-3 for: " << _id << "\n"; + } + }; + + class ServerProxy : public IServer + { + private: + std::string _id; + Server *_server; + + bool checkAccess() + { + std::cout << "[Proxy] Checking access before forwarding request.\n"; + if (_id != admin) + { + return false; + } + + // Lazy initialization: construct Server only on first access + if (_server == nullptr) + { + _server = new Server(_id); + } + return true; + } + + void logAccess() const + { + std::cout << "[Proxy] Logging request time: " << _id << " .\n"; + } + + public: + explicit ServerProxy(const std::string &id) : _id{id}, _server{nullptr} + { + std::cout << "[Proxy] Constructor: " << _id << "\n"; + } + + ~ServerProxy() + { + std::cout << "[Proxy] Destructor: " << _id << "\n"; + if (_server != nullptr) + { + delete _server; + } + } + + void request1() override + { + if (checkAccess()) + { + _server->request1(); + logAccess(); + } + } + + void request2() override + { + if (checkAccess()) + { + _server->request2(); + logAccess(); + } + } + + void request3() override + { + if (checkAccess()) + { + _server->request3(); + logAccess(); + } + } + }; + + namespace Client + { + void clientCode(IServer *s) + { + if (s != nullptr) + { + s->request1(); + s->request2(); + s->request3(); + } + } + } + + void run() + { + std::cout << "\n\n"; + + { + std::string connectionId = "admin"; + // Server is not constructed until first request is made + IServer *server = new ServerProxy(connectionId); + std::cout << "User request\n"; + Client::clientCode(server); + delete server; + } + + // Server is not constructed if id is invalid + std::string invalidId = "xxx"; + Server *server = new Server(invalidId); + Client::clientCode(server); + delete server; + } + } + + struct ProxyAutoRunner + { + ProxyAutoRunner() + { + std::cout << "\n--- Proxy Pattern Example ---\n"; + Problem::run(); + ProxyPattern::run(); + } + }; + + static ProxyAutoRunner instance; +}