-1

I am building a repository that will request data from an API. Originally, I had multiple methods in my repository such as GetOrders(), GetOrderItems() etc.

The majority of these functions had almost the same method body except they:

  • Had a different end point URL
  • Deserialized the JSON into different objects
  • Have a different JSON path variable (eg. SuccessResponse.Body.Orders.Order)

Because the code is almost the same, I want to put this into a class, perhaps something called {Company}ApiClient (is this the right name??) that will be responsibly for running the API call and returning the object.

How would I create this class so that I can have the same code but the class can return a different type depending on what it is bringing back from the API? I am assuming I need to use generics but from what I can see, the type is specified at the class level.

Any advice would be great!

1

You could create an abstract class that defines default behavior for common requests and uses generic type. In java:

public abstract class AbstractRepository<T> {
    ...
    //When constructed pass additional details if needed
    public AbstractRepository(Class<T> type, String baseURL, Client client) {
        this.type = type;
        this.baseURL = baseURL;
        this.client = client;
    }

    public T findById(final String id) {
        //This is where you actually make the call and if you need to 
        //provide the object type information to whatever means you are 
        //using to deserialize the return
        return client.get(id, ...);
    }

    public List<T> findAll() {
        return client.get(...);
    }

    public T update(final T obj) {
        return client.update(obj, ...);
    }

    public List<T> findAllByTerms(SearchTerms searchTerms) {
        return client.get(formatSearchTerms(searchTerms),...);
    }

    ...
}

Then extend this class with your types

public class OrderRepository extends AbstractRepository<Order> {

    //Call super constructor, pass values if needed
    public OrderRepository(Client client) {
        super(Order.class, Order.BASE_URL, client);
    }

    //If you need 'special' searches for Order, add them in
    public Integer getTotalOrderCount() {
        return super.findAll().size();
    }

    public List<Order> findAllByOrderType() {
        SearchTerms searchTerms = new SearchTerms();
        searchTerms.add("type", OrderTypes.OVERSEAS);
        return super.finalAllByTerms(searchTerms);
    }
}

Then if you want one client class where all calls are initiated you could do that a few ways, my naive way would be to contain all your repositories and methods would proxy to them:

public class ApiClient {
    private OrderRepository orderRepository;
    private OrderItemRepository orderItemRepository;

    public ApiClient() {
        //Just an holder
        Client client = new Client("user", "password");
        this.orderRepository = new OrderRepository(client);
        this.orderItemRepository = new OrderItemRepository(client);
    }

    public Order findOrderById(Itneger id) {
        return orderRepository.findById(id);
    }

    public List<Order> findAllOrders() {
        return orderRepository.findAll();
    }

    public OrderItem findOrderItemById(Integer id) {
        return orderItemRepository.findById(id);
    }

    ...
}
  • This looks great. If the abstract class is doing the actual work of calling the API.. What is the purpose of client.get – Lock May 5 '17 at 22:00
  • And if I have an Order endpoint as well as an OrderItem endpoint, i will have separate instances of the abstract repository for these types. If I want to marry the OrderItem results back into the Order object, should this be done in the Order repository class? – Lock May 5 '17 at 22:02
0

Yes you can use generics (in c#)

public class Api<T> where T : new
{
     public T GetById(string id);
}

If your object matches the default deserialisation, Say from Json.Net, then it will create and populate them correctly through reflection.

However. I do not recommend this approach as it has a couple of problems.

1: As already mentioned you need to be able to create and populate the object in a generic way. If you have some weird naming or a construction parameter you have problems.

2: All the clients have the same generic Methods. GetById, GetAll etc. If you want something specific, GetByDateOfBirth again you cant do it.

I think it likely the the default httpClient already has the level of genericism you need. Your api clients as you say need only specify the url, path, method name etc

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.