Design Patterns - Factory Method Pattern Adding More Power to Count Allocated Objects in C++

preview_player
Показать описание

►Lesson Description: In this lesson I present a few ideas on how you may decide to add more power to the 'Factory Method' pattern.

00:00 Recap of previous lesson
00:55 Wrapping the factory into a singleton
1:40 Our motivation is for book keeping
2:20 Refactoring our function into a class
4:50 First test after refactoring
5:46 Counting object types that were created
7:50 PrintCounts function implementation
9:00 Discussion on counting active objects
10:27 Sketch of using adapter to count objects
12:25 Other idea to use weak pointers to count active objects

NOTE: You will have to think a little bit about solution you would like to implement, and as always, there are tradeoffs depending on how much power you want, how many layers of abstraction you may choose to use (and if this may cost you space or time), and how simple (i.e. readable) you want your code to be.

►Please like and subscribe to help the channel!
Рекомендации по теме
Комментарии
Автор

Thanks for the video! Just as a remark: If you want to count the number of active planes and boats in the factory, you can also use a custom deleter. For example:
std::shared_ptr<Plane>(new Plane(), [](const Plane* ptr) { delete ptr; s_plane--; });
This approach is simpler than the Adapter Pattern but does not improve reusability, separation of concerns, or extensibility compared to using a dedicated class like CountedSharedPtr.

f.libaax
Автор

Thanks for this great video.
I have one question, what's the benefit of creating the Factory class as a singleton here?
Since we are only using static functions and variables of the class, and no object is created or used.

divanshkawatra
Автор

Hey! Really cool and informative video! I made this templated factory singleton class that creates the factories for any base class, and ensures that the requested type to be constructed is derived and concrete, using type_traits (for compile-time validation of types).

# include <iostream>
# include <memory>
# include <type_traits>
# include <string>

class IRenderable {
public:
std::string GetName() { return this->name; }
virtual void Render() = 0;
virtual ~IRenderable() = default;
protected:
std::string name;
};
class Fish: public IRenderable {
public:
Fish(const std::string& _name = "fish") : name{_name} {}
virtual void Render() override { std::cout << "Swimming\n"; }
};
class Bird : public IRenderable {
public:
Bird(const std::string& _name = "bird") : name{_name} {}
virtual void Render() override { std::cout << "Flying\n"; }
};


// compile-time helper concept to ensure that the type is concrete and derived
template <typename T1, typename T2>
concept IsConcreteDerived = std::is_base_of_v<T1, T2> && !std::is_abstract_v<T2>;

// compile-time helper function to check if a constructor exists for the given argument tuple
template <typename T, typename... Args1>
constexpr bool has_constructor() { return std::is_constructible_v<T, Args1...>; }


template <typename BaseType>
class ObjectFactory final {
private:
static inline size_t s_Count = 0;
ObjectFactory() = delete;
ObjectFactory(const ObjectFactory&) = delete;
ObjectFactory& operator=(const ObjectFactory&) = delete;
= delete;
ObjectFactory& operator=(ObjectFactory&&) = delete;
public:
template <typename DerivedType, typename... Args>
requires IsConcreteDerived<BaseType, DerivedType>

static std::shared_ptr<BaseType> Create(Args&&... args) {
if constexpr (has_constructor<DerivedType, Args...>()) { // constructor for provided args
++s_Count;
return
} else if constexpr { // default constructor
++s_Count;
return
} else {
static_assert(!"No matching constructor found for DerivedType");
}
}
static size_t GetCreatedCount() { return s_Count; }
};

int main() {
auto fish = Fish");
auto bird = Bird");
std::cout << "Created Count: " << << '\n'; // outputs 2
}


Could you please make a video on this topic? I would love to hear your persepctive and insights.

Using templates to define classes seems very cool and because it is all happening at compile-time, there is no performance cost. I was also wondering whether there was some way to make a compile-time templated class constructor, where you basically do not have to type class {/*members, functions, etc*/}; anymore, but rather are just able to do something like:
int main() {
InlineClassFactory<int: m_Int, double: m_Double, std::string: m_String, std::function<void(int&)>: CustomUpdate> myInlineClass {
.m_Int = 42,
.m_Float = 3.14f,
.m_String = "Hello",
.CustomUpdate() = [] (int& a) { a++; }
}; // inline class declaration
int A{};
// A should be 1
}

maybe even add some way to achieve the same level of control that we have when declaring classes normally, such as specifying inheritance, static member functions, override, const, and other specifiers right there in the main function. Maybe even defining templated classes using the InlineTemplateClassFactory.

Or am I just too over ambitious (❁´◡`❁)? Do you see any cool use cases for this? It would definitely save some time, .

Anyways, love the content, and the design patterns playlist is really eye-opening!

byte_map
Автор

Hi Mike, great video! Could we have not used the reference count of the shared pointers to tell us how many active objects there are? Or maybe the reference count of the weak ptr associated with the shared ptr ? Thanks!

auto ptr1 = std::make_shared<int[]>(10);
{
auto ptr2 = ptr1;
std::cout << "reference count :" << ptr1.use_count() << '\n'; // prints 2
}
std::cout << "reference count :" << ptr1.use_count() << '\n'; //prints 1

Maximus
Автор

@9:35 can't we delete by overloading the delete operator? something like:

void operator delete(void* memory){ free(memory);}

Praveer
Автор

Hi, Mike!
Awesome tut, I have one question: on 3:23 you created copy-constructor in private section of "factory" class, but what is the difference with explicit restriction on copy constructor on next line:
FactoryGameObjects(const& FactoryGameObjects o) = delete;

heyheyheyhoev
Автор

Is the wrapper the best approach for this problem? Could you store them in a container that gets created with the constructer and push back the shared_ptr when the CreateObject function is called? Then you could use the size function from that container?

dwolrdcojp
Автор

can you explain how to further code the fragment with adapter?

wojciechgrunwald
Автор

11:34, I think that destructor should not have any parameter list!

georgiosdoumas
welcome to shbcf.ru