Reputation: 177
This issue comes up frequently in my projects. Suppose that, as an example, I have two interfaces, one that retrieves information from an API and another one that parses this information.
public interface APIClient {...}
public interface APIParser {...}
Now, I may need to have different APIs, so I will have many implementations for the APICLient
but each of this implementations will need its own APIParser
.
This will lead to this type of structure
public class APIClientA implements APIClient {...}
public class APIParserA implements APIParser {...}
public class APIClientB implements APIClient {...}
public class APIParserB implements APIParser {...}
...
...
So, in general, this would mean that every time I want to add a new type of API, I'll have to create several classes for the same type, and make sure they only communicate with each other and not with the implementation of other types of APIs.
This looks very similar to what Bridge design pattern propose, but this pattern would allow any APIClient to use any APIParser (Am I right ?)
So, is there a better solution? Or maybe this is fine and there is no need to refactor it.
Also, maybe parse is not the right word, sorry my English, but what I meant to say is to analyze a JSON/XML response in order to get concrete information from it. The way I analyze each response depends on the structure provided by each API, so this is why I will need different "Parsers"
EDIT:
I will extend this idea a bit. There is a client class that uses an APIClient to make the request, with a given type of parameters. Once the request is met with a JSON response, the client uses the corresponding APIParser to obtain specific information about the response.
public class ClientClass {
...
json = APIClientTypeA.makeRequest(city, day, ...);
temperature = APIParserTypeA.getTemperature(json);
...
}
The problem here is that the client needs to make sure to use the right implementation of APIClient and APIParser (They must match)
Upvotes: 3
Views: 355
Reputation:
Your requirements seem to be a pretty good match for the Abstract Factory GoF pattern, because once you create a concrete factory and your code only instantiates the client and parser through that factory, it will not be possible to mistakenly get a mismatched set of client/parser instances.
The AbstractFactory
is the object your client uses to instantiate client/parser pairs:
interface AbstractFactory {
APIParser createAPIParser();
APIClient createAPIClient();
}
interface APIParser {}
interface APIClient {}
The concrete factories can provide matching pairs (you might be able to reuse the product instances, but I kept it simple):
class ConcreteFactoryA implements AbstractFactory {
public APIParser createAPIParser() { return new APIParserA(); }
public APIClient createAPIClient() { return new APIClientA(); }
}
class ConcreteFactoryB implements AbstractFactory {
public APIParser createAPIParser() { return new APIParserB(); }
public APIClient createAPIClient() { return new APIClientB(); }
}
For completeness here are the declarations of the concrete products:
class APIParserA implements APIParser {}
class APIParserB implements APIParser {}
class APIClientA implements APIClient {}
class APIClientB implements APIClient {}
Sample client code:
AbstractFactory factory = new ConcreteFactoryA();
APIClient client = factory.createAPIClient();
APIParser parser = factory.createAPIParser();
Upvotes: 2
Reputation: 2386
What you could do is to add a type-parameter to your classes.
// wrapper for your json
public class APIResult<T extends APIClient<T>> {
private final String json;
public APIResult(String json) {
this.json = json;
}
public String getJson() {
return this.json;
}
}
// client always returns a result with itself as a type
public interface APIClient<T extends APIClient<T>> {
APIResult<T> makeRequest();
}
// parser only handles the implementation specific results of one client
public interface APIParser<T extends APIClient<T>> {
String getTemperature(APIResult<T> result);
}
public class APIClientA implements APIClient<APIClientA> {
public APIResult<APIClientA> makeRequest() {
// must return a result with this type
}
}
public class APIParserA implements APIParser<APIClientA> {
public String getTemperature(APIResult<APIClientA> result) {
// only accepts results from one specific client
}
}
Upvotes: 1