Reputation: 99
I'm learning design patterns and things around it (like SOLID and Dependency inversion principle in particular) and it looks like I'm loosing something:
Following the DIP rule I should be able to make classes less fragile by not creating an object in the class (composition) but sending the object reference/pointer to the class constructor (aggregation). But this means that I have to make an instance somewhere else: so the more flexible one class with aggregation is, the more fragile the other.
Please explain me where am I wrong.
Upvotes: 3
Views: 367
Reputation: 73570
One important possibility that you've missed is the injection of factories, rather than the class itself. One advantage of this is that it allows your ownership of instances to be cleaner. Note the code is a little uglier since I'm explicitly giving the container ownership of its components. Things can be a little neater if you use shared_ptr
, rather than unique_ptr
, but then ownership is ambiguous.
So starting with code that looks like this:
struct MyContainer {
std::unique_ptr<IFoo> foo;
MyContainer() : foo(new Foo() ) {};
void doit() { foo->doit(); }
}
void useContainer() {
MyContainer container;
container.doit();
}
The non-factory version would look like this
struct MyContainer {
std::unique_ptr<IFoo> foo;
template<typename T>
explicit MyContainer(std::unique_ptr<T> && f) :
foo(std::move(f))
{}
void doit() { foo->doit(); }
}
void useContainer() {
std::unique_ptr<Foo> foo( new Foo());
MyContainer container(std::move(foo));
container.doit();
}
The factory version would look like
struct FooFactory : public IFooFactory {
std::unique_ptr<IFoo> createFoo() override {
return std::make_unique<Foo>();
}
};
struct MyContainer {
std::unique_ptr<IFoo> foo;
MyContainer(IFooFactory & factory) : foo(factory.createFoo()) {};
void doit() { foo->doit(); }
}
void useContainer() {
FooFactory fooFactory;
MyContainer container(fooFactory);
container.doit();
}
IMO being explicit about ownership & lifetime in C++ is important - its a crucial part of C++'s identity - it lies at the heart of RAII and lots of other C++ patterns.
Upvotes: 0
Reputation: 339
Yes, you would need to instantiate the class somewhere. If you follow the DIP correctly, you will end up creating all the instances in one place. I called this place as class composition. Read my blog on more depth understanding on this topic Here
Upvotes: 0
Reputation: 32944
You just need to follow the idea through to its logical conclusion. Yes you have to make the instance somewhere else, but this might not be just in the class that is one level above your class, it keeps needing to be pushed out and out until objects are only created at the very outer layer of your application.
Ideally you create all of your objects in a single place, this is called the composition root (the exception being objects which are created from factories, but the factories are created in the composition root). Exactly where this is depends on the type of app you are building.
this place may end up being 'fragile' but you only have a single place to change things in order to be able to reconfigure your application, and then all other classes are testable and configurable.
See this excellent answer (which is quoted above)
Upvotes: 5