Objective-c design advice for use of different data sources, swapping between test and live
I'm in the process of designing an application that is part of a larger piece of work, depending on other people to build an API that the app can make use of to retrieve data.
While I was thinking about how to setup this project and design the architecture around it, something occurred to me, and I'm sure many people have been in similar situations.
Since my work is depending on other people to complete their tasks, and a test server, this slows work down at my end.
So the question is: What's the best practice for creating test repositories and classes, implementing them, and not having to depend on altering several places in the code to swap between the test classes and the actual repositories / proper api calls.
Contemplate the following scenario:
GetDataFromApiCommand *getDataCommand = [[GetDataFromApiCommand alloc]init];
getDataCommand.delegate = self;
[getDataCommand getData];
开发者_JS百科Once the data is available via the API, "GetDataFromApiCommand" could use the actual API, but until then a set of mock data could be returned upon the call of [getDataCommand getData]
There might be multiple instances of this, in various places in the code, so replacing all of them wherever they are, is a slow and painful process which inevitably leads to one or two being overlooked.
In strongly typed languages we could use dependency injection and just alter one place. In objective-c a factory pattern could be implemented, but is that the best route to go for this?
GetDataFromApiCommand *getDataCommand = [GetDataFromApiCommandFactory buildGetDataFromApiCommand];
getDataCommand.delegate = self;
[getDataCommand getData];
What is the best practices to achieve this result?
Since this would be most useful, even if you have the actual API available, to run tests, or work off-line, the ApiCommands would not necessarily have to be replaced permanently, but the option to select "Do I want to use TestApiCommand or ApiCommand".
It is more interesting to have the option to switch between: All commands are test and All command use the live API, rather than selecting them one by one, however that would also be useful to do for testing one or two actual API commands, mixing them with test data.
EDIT The way I have chosen to go with this is to use the factory pattern.
I set up the factory as follows:
@implementation ApiCommandFactory
+ (ApiCommand *)newApiCommand
{
// return [[ApiCommand alloc]init];
return [[ApiCommandMock alloc]init];
}
@end
And anywhere I want to use the ApiCommand class:
GetDataFromApiCommand *getDataCommand = [ApiCommandFactory newApiCommand];
When the actual API call is required, the comments can be removed and the mock can be commented out.
Using new in the message name implies that who ever uses the factory to get an object, is responsible for releasing it (since we want to avoid autorelease on the iPhone).
If additional parameters are required, the factory needs to take these into consideration i.e:
[ApiCommandFactory newSecondApiCommand:@"param1"];
This will work quite well with repositories as well.
Using new in the message name implies that who ever uses the factory to get an object, is responsible for releasing it (since we want to avoid autorelease on the iPhone).
You don't have to be obsessive about not autoreleasing. It really only applies if you happen to be creating lots of autoreleased objects within one iteration of the event loop. If this object is intended to live long enough that you would otherwise retain it outside of the factory method, giving back an autoreleased object will cost you only a reference in the autorelease pool.
Anyway, to answer your question, your choice is pretty much exactly what I would do. Consider also creating a Objective-C protocol that matches the API and that your APICommandMock and APICommand must conform to. That will document the API and provide some discipline for both you and the other team. For instance, it'll nail down things like method names and parameter types.
精彩评论