11// cppcheck-suppress-file [functionStatic]
22
3- // Prototype is a creational design pattern that lets you "copy existing objects" without making your code "dependent on their classes".
4- // Appicability:
5- // (*) when your code shouldn’t depend on the concrete classes of objects that you need to copy.
6- // (**) when you want to reduce the number of subclasses that only differ in the way they initialize their respective objects.
7-
8- // UML: docs/uml/patterns_behavioral_prototype.drawio.svg
9-
3+ // Prototype = create new objects by cloning existing ones.
4+ //
5+ // Flow in this file:
6+ // 1. Define a cloneable interface -> IExtensionPrototype
7+ // 2. Implement concrete prototypes -> Logger / Analytics
8+ // 3. (Optional) Store named presets -> ExtensionPrototypeRegistry
9+ // 4. Client asks registry to clone by id -> never mentions concrete types
10+
11+ #include < memory>
12+ #include < string>
13+ #include < string_view>
1014#include < unordered_map>
1115#include < utility>
16+
17+ #include " ExampleRegistry.h"
1218#include " Logger.h"
19+
1320namespace {
1421namespace prototy {
1522
16- constexpr std::string_view kLoggerEtxId = " logger" ;
23+ constexpr std::string_view kLoggerExtId = " logger" ;
1724constexpr std::string_view kAnalyzeId = " analyze" ;
1825
19- /*
20- * Prototype interface declares the cloning methods.
21- * In most cases, it’s a single `clone` method.
22- */
26+ // / @class Prototype interface: "I know how to copy myself."
27+ // / @brief client code only depends on this class
2328class IExtensionPrototype {
2429 public:
2530 virtual ~IExtensionPrototype () = default ;
26- virtual IExtensionPrototype* clone () = 0;
31+
32+ // / return a new object
33+ virtual std::unique_ptr<IExtensionPrototype> clone () const = 0;
34+
2735 virtual void execute () const = 0;
2836};
2937
30- /*
31- * Concrete Prototype implement an operation for cloning itself
32- * In addition to copying the original object’s data to the clone,
33- * this method may also handle some edge cases of the cloning process related to cloning linked objects,
34- * untangling recursive dependencies, etc.
35- */
38+ // / @class Concrete Prototype
39+ // / @brief each class copies itself
3640class LoggerExtension : public IExtensionPrototype {
37- private:
38- std::string log_level_;
39-
4041 public:
4142 explicit LoggerExtension (std::string level = " DEBUG" )
4243 : log_level_{std::move (level)} {}
4344
44- IExtensionPrototype* clone () override { return new LoggerExtension (*this ); }
45+ std::unique_ptr<IExtensionPrototype> clone () const override {
46+ return std::make_unique<LoggerExtension>(*this );
47+ }
4548
4649 void execute () const override { LOG (" log level: " + log_level_); }
47- };
4850
49- class AnalyticsExtension : public IExtensionPrototype {
5051 private:
51- int sRate_ ;
52+ std::string log_level_;
53+ };
5254
55+ class AnalyticsExtension : public IExtensionPrototype {
5356 public:
54- explicit AnalyticsExtension (int level = 1 ) : sRate_{level} {}
57+ explicit AnalyticsExtension (int sampling_rate = 1 )
58+ : sampling_rate_{sampling_rate} {}
5559
56- IExtensionPrototype* clone () override {
57- return new AnalyticsExtension (*this );
60+ std::unique_ptr< IExtensionPrototype> clone () const override {
61+ return std::make_unique< AnalyticsExtension> (*this );
5862 }
5963
60- void execute () const override { LOG_S (" sampling rate: " << sRate_ ); }
61- };
64+ void execute () const override { LOG_S (" sampling rate: " << sampling_rate_); }
6265
63- /* *
64- * Prototype Registry provides an easy way to access frequently-used prototypes.
65- * It stores a set of pre-built objects that are ready to be copied.
66- * The simplest prototype registry is a name ^ prototype hash map.
67- * However, if you need better search criteria than a simple name, you can build a much more robust version of the registry.
68- */
69- class ExtensionPrototypeRegistry {
7066 private:
71- std::unordered_map<std::string, IExtensionPrototype*> prototypes_;
67+ int sampling_rate_;
68+ };
7269
70+ // / @class Prototype Registry
71+ // / @brief a map of named presets ready to be cloned.
72+ class ExtensionPrototypeRegistry {
7373 public:
74- ~ExtensionPrototypeRegistry () {
75- for (auto it = prototypes_.begin (); it != prototypes_.end ();) {
76- delete it->second ; // free the pointer
77- it = prototypes_.erase (it); // erase and move to next
78- }
79- }
80- void register_extension (const std::string_view& id,
81- IExtensionPrototype* proto) {
74+ void register_extension (std::string_view id,
75+ std::unique_ptr<IExtensionPrototype> proto) {
8276 LOG (id);
83- prototypes_[std::string (id)] = proto;
77+ prototypes_[std::string (id)] = std::move ( proto) ;
8478 }
8579
86- IExtensionPrototype* create (const std::string_view& id) const {
80+ // / clone the preset for `id`. Returns nullptr if the id is unknown.
81+ std::unique_ptr<IExtensionPrototype> create (std::string_view id) const {
8782 auto it = prototypes_.find (std::string (id));
88- if (it ! = prototypes_.end ()) {
89- return it-> second -> clone () ;
83+ if (it = = prototypes_.end ()) {
84+ return nullptr ;
9085 }
91- return nullptr ;
86+ return it-> second -> clone (); // copy the template, leave the original intact
9287 }
88+
89+ private:
90+ std::unordered_map<std::string, std::unique_ptr<IExtensionPrototype>>
91+ prototypes_;
9392};
9493
9594void run () {
96- // Client creates a new object by asking a prototype to clone itself
97- auto client_code = [](const ExtensionPrototypeRegistry* const registry) {
98- IExtensionPrototype* logger_etx = registry->create (prototy::kLoggerEtxId );
99- logger_etx->execute ();
100- IExtensionPrototype* analyx_etx = registry->create (prototy::kAnalyzeId );
101- analyx_etx->execute ();
102-
103- delete logger_etx;
104- delete analyx_etx;
95+ // build presets once, register by name
96+ ExtensionPrototypeRegistry registry;
97+ registry.register_extension (kLoggerExtId ,
98+ std::make_unique<LoggerExtension>(" DEBUG" ));
99+ registry.register_extension (kAnalyzeId ,
100+ std::make_unique<AnalyticsExtension>(1200 ));
101+
102+ // create-by-clone
103+ auto client_code = [](const ExtensionPrototypeRegistry& reg) {
104+ auto logger_ext = reg.create (kLoggerExtId );
105+ auto analytics_ext = reg.create (kAnalyzeId );
106+
107+ if (logger_ext) {
108+ logger_ext->execute ();
109+ }
110+ if (analytics_ext) {
111+ analytics_ext->execute ();
112+ }
105113 };
106114
107- // Create a registry
108- auto * registry = new ExtensionPrototypeRegistry ();
109-
110- // Register extensions
111- registry->register_extension (prototy::kLoggerEtxId ,
112- new LoggerExtension (" DEBUG" ));
113- registry->register_extension (prototy::kAnalyzeId ,
114- new AnalyticsExtension (1200 ));
115-
116115 client_code (registry);
117-
118- delete registry;
119116}
117+
120118} // namespace prototy
121119} // namespace
122120
123- #include " ExampleRegistry.h"
124-
125121class PrototypeExample : public IExample {
126122 public:
127123 std::string group () const override { return " dp/creational" ; }
128124 std::string name () const override { return " Prototype" ; }
129125 std::string description () const override {
130- return " Prototype Pattern Example " ;
126+ return " Clone configured objects via a prototype interface + registry " ;
131127 }
132128 void execute () override { prototy::run (); }
133129};
134130
135- REGISTER_EXAMPLE (PrototypeExample);
131+ REGISTER_EXAMPLE (PrototypeExample);
0 commit comments