Is it an acceptable practice to initialize physical/external resources from a constructor when the resource is needed for the object to do it's work?
For instance, let's say I wanted to create an object that proxies a durable message queue and that the message queue is a physical database table.
Would that make sense to create an idempotent constructor which physically create the table if it doesn't exist yet?
E.g.
IMessageQueue queue = new SQLTableMessageQueue('table_name', dataSource);
Perhaps a static factory method would be more appropriate? In that case, the table creation would occur in the factory method and the constructor would be free of such behavior.
IMessageQueue queue = SQLTableMessageQueue.create('table_name', dataSource);
I'm not too sure what would be an appropriate approach? Another idea I had was to use the Repository Pattern.
2 Answers 2
Usually constructors are not used to initialize external resources, there are few reasons,
- Testability - It would be very hard to create unit tests
- To be in compliance with SRP
You could always pass the message queue to the constructor where you use it.
class QueueProcessor
{
private IMessageQueue _queue;
public QueueProcessor(IMessageQueue queue)
{
_queue = queue;
}
}
Yes, you can use a factory to create the queue
class QueueFactory
{
public IMessageQueue CreateMessageQueue
{
return new SQLTableMessageQueue('table_name', dataSource);
}
}
With this approach you can easily mock the message queue for testing, and also it does comply with the SRP compared to constructing the queue within the constructor.
-
Well, I do not see how your exemple is different than mine? The creation of the database table still would have to occur in the
SQLTableMessageQueue
constructor. Also, abstracting away the concrete queue class behind a factory isin't as simple as this. Different queue types might require different arguments to be constructed.plalx– plalx2015年01月21日 04:50:05 +00:00Commented Jan 21, 2015 at 4:50 -
Difference is in my example, I pass the interface IMessageQueue to the constructor, where you can pass any implementation including a mock object for testing. About the factory, it's the same thing nothing different in my case, if you want to add parameters you can pass it into the factory method.Low Flying Pelican– Low Flying Pelican2015年01月21日 04:55:50 +00:00Commented Jan 21, 2015 at 4:55
-
I see that, but that's out of the question's scope and is irrelevant to my question. I haven't even mentionned anything about processing the queue and about the factory, if the goal is to abstract away the concrete class being used, you must make sure that the factory's interface doesn't leak specific concrete class's implementation details. Otherwise, client code will still have to be modified if the concrete class changes within the factory.plalx– plalx2015年01月21日 04:57:50 +00:00Commented Jan 21, 2015 at 4:57
-
It's in scope of the question, question is, is it a good idea to initialize external resources. And if you summarize the answer, it says : "No it's not a good idea, you should construct it outside and pass into the constructor."Low Flying Pelican– Low Flying Pelican2015年01月21日 05:08:56 +00:00Commented Jan 21, 2015 at 5:08
-
I'm sorry, but you are still misunderstanding the question. My point is, where the SQL table creation code shall go in the stated example. Is it fine to have that resource created when invoking the
SQLTableMessageQueue
constructor or not? If not, what would be more appropriate?plalx– plalx2015年01月21日 13:52:24 +00:00Commented Jan 21, 2015 at 13:52
Google's C++ Style Guide asks you to refrain from doing work in constructors. Their arguments may or may not be applicable to your language. Testing on the Toilet's six-year-old advice #3 on how to write testable code by not doing work in the constructor still holds though.
If you search for it online you'll find tons, /*Programmers*/ is one place to start.
Constructors must usually be side-effect free
- Impossible, unless you're constructing an empty object. Where did you hear that?Timer
you wouldn't start it right away when the constructor is called, but even that point is not very important for the question.this
to another thread before the constructor has finished.