Who should construct objects in this scenario?
I have the following class:
class PluginLoader
{
public:
PluginLoader(Logger&, PluginFactory&, ConflictResolver&);
//Functions.
private:
//Members and functions
};
Logger
, PluginFactory
and ConflictResolver
classes are all interface classes which will be implemented in the application. I create the single PluginLoader
object at 开发者_JS百科top level in the main program. The Logger
can be known at that level. But, PluginFactory
and ConflictResolver
are only used internally in the PluginLoader
class. So creating their objects at top level seems ugly. What should I do? I can change the constructor if it is needed though I'd like to keep it the way it is. Any comments are welcome. Thanks.
From your question and your comments it looks like that you are confused with two seemingly conflicting design principles.
- You should encapsulate as much as possible
- You shouldn't hardwire the creation of the needed objects in your implementation.
In your case, you have done right(at least from the visible interfaces) by adhering to the second principle. This is a good example of dependency injection. It is nice to relive the PluginLoader from the responsibility of creating the PluginFactory and the ConflictResolver.
Now, you think that you are violating the 1st principle and this is true to some extend. So, go back and revisit your design problem more closely.
What exactly the PluginLoader need? It just needs the reference to some PluginFactory and ConflictResolver object. But it doesn't need to create them by itself. How to solve this problem?
There are two ways I can think of:
- Have another level of abstraction as done in a builder pattern. Here the complex (or apparently complex in your case) building and wiring process is moved to a builder object. The main program is not bothering about how this is done. It just triggers the building process.
- Have another factory that can create the helper objects like PluginFactory and ConflictResolver and let the PluginLoader only know about the interface of this new factory. The main program can create a concrete instance of this factory and pass on to the PluginBuilder.
By using either of the above ways you can eliminate your conflict with the principle 2.
All the best.
I think the solution is to use a Dependency Injection mechanism: you can thus choose the concrete PluginFactory
and ConflictResolver
by changing just a string, maybe loaded from a configuration file.
I developed a dependency injection library in C++ named Wallaroo. In your application you may declare your PluginLoader class using this code:
REGISTERED_CLASS( PluginLoader, void, void )
{
...
private:
Plug< Logger > logger;
Plug< PluginFactory > pluginFactory;
Plug< ConflictResolver > conflictResolver;
};
then, you can decide the concrete classes to use and their wiring in an xml file:
<catalog>
<!-- build the objects of your app -->
<object>
<name>loader</name>
<class>PluginLoader</class>
</object>
<object>
<name>logger</name>
<class>MyConcreteLogger</class>
</object>
<object>
<name>factory</name>
<class>MyConcretePluginFactory</class>
</object>
<object>
<name>resolver</name>
<class>MyConcreteResolver</class>
</object>
<!-- wiring of the objects: -->
<relation>
<source>loader</source>
<dest>logger</dest>
<role>logger</role>
</relation>
<relation>
<source>loader</source>
<dest>factory</dest>
<role>pluginFactory</role>
</relation>
<relation>
<source>loader</source>
<dest>resolver</dest>
<role>conflictResolver</role>
</relation>
</catalog>
or by using this code:
catalog.Create( "loader", "PluginLoader" );
catalog.Create( "logger", "MyConcreteLogger" );
catalog.Create( "factory", "MyConcretePluginFactory" );
catalog.Create( "resolver", "MyConcreteResolver" );
wallaroo_within( catalog )
{
use( "logger" ).as( "logger" ).of( "loader" );
use( "factory" ).as( "pluginFactory" ).of( "loader" );
use( "resolver" ).as( "conflictResolver" ).of( "loader" );
}
I would make the PluginLoader class have pointers to those classes. I would forward declare the classes and I wouldn't define the classes anywhere but in the implementation files so that no one else has visibility. That is better encapsulation, plus doing that cuts down on compile time too.
I wouldn't use a raw pointer, but auto_ptr, scoped_ptr or unique_ptr should work.
If you create those objects in the constructor, or even later when first used (lazy construction) then you don't need to pass anything into the constructor and you can drop those parameters.
Edit:
From other comments I see I may have misunderstood your question. If you want to have customizable objects that implement an interface but can be anything underneath then you have to create them at the top level and pass in the interface reference/pointer, like you are doing.
I suppose that you could use a template to "pass" the class type into your object so that it can create those classes for itself. However, using templates would make the class types be fixed at compile time which may not be what you want.
My impression is that you are exposing the internal stuff in the constructor for possible customization but not the typical case. If this is correct then one thought is to make these parameters optional and create them inside if not provided.
...classes are all interface classes which will be implemented in the application...
...
PluginFactory
andConflictResolver
are only used internally in thePluginLoader
class...
This basically means that even though they are only used internally you have to expose the existance of PluginFactory
and ConflictResolver
as you need to have the client implement them, and then pass custom implementations to PluginLoader
in some way (in this case through constructor).
However the declaration of the named object at the top level (either static or automatic inside main
) can be avoided (not sure why one would want to do that though) by replacing references with pointers (preferably the smart kind, unique_ptr will be just the tool for the job):
class PluginLoader
{
public:
PluginLoader(
std::unique_ptr<Logger> logger,
std::unique_ptr<PluginFactory> plugin_factory,
std::unique_ptr<ConflictResolver> conflict_resolver
)
: logger(std::move(logger))
, plugin_factory(std::move(plugin_factory))
, conflict_resolver(std::move(conflict_resolver))
{
}
private:
std::unique_ptr<Logger> logger,
std::unique_ptr<PluginFactory> plugin_factory,
std::unique_ptr<ConflictResolver> conflict_resolver
};
and create PluginLoader
like so:
PluginLoader plugin_loader(
std::unique_ptr<CustomLogger>(new CustomLogger(/* ... */))
std::unique_ptr<CustomPluginFactory>(new CustomPluginFactory(/* ... */))
std::unique_ptr<CustomConflictResolver>(new CustomConflictResolver(/* ... */))
);
where CustomLogger
, CustomPluginFactory
and CustomConflictResolver
are implementations of Logger
, PluginFactory
and ConflictResolver
respectively.
We have used a similar design for an app that uses a dll we create. The logger is known in the main app and is used in the dll. However, there are objects that are only used internally to the dll. I'm still not clear on why they are even part of the interface for the PluginLoader at all if they are only used internally. Do any parts of the other objects depend on those interfaces externally?
What I'm getting at is why not use factories for both those parameters, PluginFactory, ConflictResolver and not pass them as params at all?
e.g. PluginLoader(Logger&);
Then in your implementation
PluginLoader(Logger& logger){
Factory::PluginFactory::GetInstance().useInstance;// example
Factory::ConflicResolver::GetInstance().useInstance;// exmaple
}
Or maybe you can elaborate?
精彩评论