Forum Replies Created
-
AuthorPosts
-
::
Very good point. The first example decorator.cpp is from Wikipedia.
Here is a value based implementation:
#include <iostream> #include <memory> #include <string> struct Shape { virtual ~Shape() = default; virtual std::string GetName() const = 0; }; struct Circle : Shape { std::string GetName() const override { return std::string("A circle of radius ") + std::to_string(radius); } float radius = 10.0f; }; struct ColoredShape : Shape { ColoredShape(const std::string& color, std::unique_ptr<Shape> shape) : color(color), shape(std::move(shape)) {} std::string GetName() const override { return shape->GetName() + " which is colored " + color + "."; } std::string color; std::unique_ptr<Shape> shape; }; int main() { std::cout << '\n'; auto circle = std::make_unique<Circle>(); std::cout << circle->GetName() << '\n'; ColoredShape colored_shape("red", std::move(circle)); std::cout << colored_shape.GetName() << '\n'; std::cout << '\n'; }
I essentially replaced the pointer Shape* with a std::unique_ptr<Shape>.
::Without knowing more context, I have several ideas in mind. First, the “Fundamental theorem of software engineering” come into my mind: “We can solve any problem by introducing an extra level of indirection.”
First, I think the Adapter Pattern with Multiple Inheritance is a bad choice. The purpose of the Adapter Pattern is to adapt an interface, and you have to make your decision at compile time. Your concern is to support a generic interface with different implementations. I have two ideas.
- Use the Strategy Pattern. Your JSON class has a pointer to a concrete JSON implementation. This concrete JSON implementation is the strategy that has to implement a JSON interface. Your existing INHOUSE::JSON implementation implements this interface, and also the new JSON implementations implement this interface. Now, you can instantiate your JSON class with a concrete JSON implementation. The classes InhouseJson and BoostJson are probably also adapter, based on the delegation.
class JsonImplementation { virtual JsonData read() = 0; virtual write(JsonData data) = 0; }; class InhouseJson: public JsonImplementation { ... } class BoostJson: public JsonImplementation { ... } class JSON { public: JSON(JSONimplementation j): json(std::make_unique<JSONImlementation>(j)) {} // JSON API private: std::unique_ptr<JSONimplementation> json; };
- If the required API of the JSON class is simple, you should also consider a Facade. This means your Facade describe the simple interface that use under-the-hood the concrete JSON implementation. This has the benefit that you easily can replace the implementation because your customer use the facade. I’m a big fan of the facade for restructuring interface. Assume, your customer should use a different implementation of a JSON library. You can do the following.
- Define a new interface (facade) for your JSON functionality.
- Say to your customer that he should use the new interface.
- Replace the implementation because the customer only depend on the interface.
::If possible, you should use value semantics and not reference semantics. A std::shared_ptr models reference semantics. A std::shared_ptr is better than a pointer, but it models shared ownership. Reference semantics makes your code pretty complicated to reason about. For example, using a std::shared_ptr<Shape> in a concurrent environment is probably a data race. At least, you have to think about critical sections.
A std::unique_ptr on the contrary, models value semantics.
::The crucial idea of the Bridge Pattern is that you can exchange the interface and the implementation during run time using dependency injection. For example, each client call should trigger a log message. Thanks to the Bridget Pattern, you can derive a LoggerAbstraction and a LoggerImplementation for the corresponding interface and plug it together.
auto loggerImplemenation = new LoggerImplementation(); auto loggerAbstraction = new LoggerAbstraction(loggerImplementation); loggerAbstraction.func() // log message from the abstraction and the implementation auto nativeImplementation = new NativeImplementation(); loggerAbstraction.changeImplementation(nativeImplementation); loggerAbstraction.func(); // Log message only form the abstraction
The simplified Bridget Pattern pimpl does not give you this flexibility.
::This is the more advanced topic of template inheritance. I used http://hilite.me/ to lay out your code. Read my post here: Surprise Included: Inheritance and Member Functions of Templates.
::Imagine, your singleton has a pointer to a database. For unit testing, you want to change the database with a mocked database.
#include <iostream> #include <memory> struct DataBase{ virtual ~DataBase() = default; }; struct MockDataBase: DataBase{}; class MySingleton{ private: MySingleton(): myDataBase(std::make_unique<DataBase>()){} ~MySingleton()= default; std::unique_ptr<DataBase> myDataBase; public: MySingleton(const MySingleton&)= delete; MySingleton& operator=(const MySingleton&)= delete; static MySingleton& getInstance(){ static MySingleton sing; return sing; } void changeDataBase(DataBase* newDataBase){ myDataBase.reset(newDataBase); } }; int main(){ std::cout << '\n'; MockDataBase mockDataBase; MySingleton::getInstance().changeDataBase(&mockDataBase); std::cout << '\n'; }
This is not possible with a static member function. To change the member myDataBase, you need a member function.
::Now, I see your point. The member function getType of Window in the program FactoryMethodClassic.cpp was only a stylistic choice. I used it just instead of an enum as an argument for the factory function getNewWindow. This would be more typical. Additionally, this implementation is pretty similar to the Prototype Pattern used in the example factoryMethodUniquePtr.cpp, and simulates virtual behavior.
::There is a big difference.
Compare the following function signatures:
std::unique_ptr<Widget> createWidget1(WindowType win); std::unique_ptr<Widget> createWidget2(std::unique_ptr<Widget>& wid);
- The caller of the factory function createWidget1 has to explicitly specific the Widget he wants to have: auto createWidget(Widget::FancyWidget);
- The caller of the factory function createWidget2 only want to have the same Widget. Which Widget is created depends on the concrete argument. Inside the function, you call a virtual member function such as clone or create on the Widget. Consequentially, your creation process depends not on an explicit value such as an enum, but on virtuality.
std::unique_ptr<FancyWidget> fancyWidget; std::unique_ptr<DefaultWidget> defaultWidget; createWidget2(fancyWidget); createWidget2(defaultWidget);
::I discussed various ways to conditional execute code in these two posts:
- C++ Core Guidelines: To Switch or not to Switch, that is the Question
- C++ Core Guidelines: More about Control Structures
From the performance point of view, CRTP is the most promising. Static polymorphism based on CRTP will be our topic in week 10.
::Classically, you make it inside a class. This is the reason it’s called factory method, not factory function. Furthermore, this factory method gets an object (see factoryMethodUniquePtr.cpp) like in my example or a number (see here) to decide which object should be created.
In the simple use-case, I prefer a free function; in the more advanced, a member function. A function has as serious limitation. It cannot have state. For example, when your factory method returns a std::shared_ptr and want to keep track of the created products, you need an object.
::Clang will not compile this program because val has static storage duration but not automatic storage duration. Static storage duration means that it is initialized prior to program startup. Here is more information about storage duration.
Because of the storage duration, the lambda can directly access val:
#include <functional> #include <iostream> #include <string> const std::string val = "on stack created"; std::function<std::string()> makeLambda() { return []{return val;}; } int main(){ auto bad = makeLambda(); std::cout << bad(); }
-
AuthorPosts
