Object builder that requires parameters based on other parameters

Viewed 47

I am working with several external APIs on a business code that would be used by several developers that do not have the deep knowledge required to build meaningful queries to those APIs.

Those API retrieve data. For example, you can retrieve entities either based on their Key (direct access) or based on their Group (lists available entities). But if you choose to query by Key you have to provide an id, and if you query by Group you have to provide a groupName.

The APIs are bigger than that and more complex, with many possible use-cases. The main constraints are that:

  • Some parameters require the presence of other parameters
  • Some parameters put with other parameters produce no data at best, wrong data at worst.

I would love to fix the underlying APIs but they are outside our scope.

I think it might be good to enclose a bit those API and produced a APIService that can request(APIQuery query). The basic thing I could do is to put conditions in the code to check that no developer instantiates the APIQuery with missing/incoherent parameters, however that would only be a runtime error. I would love for the developer to know when building their request that they can/cannot do something.

My two questions are:

  • Is there an extensible builder-like way to defer the responsibility of building itself to the object? Having 1 constructor per valid query is not a good solution, as there are many variables and "unspoken rules" here.
  • Is this even a good idea? Am I trying to over-engineer?
1 Answers

I'll answer your second question first:

Is this even a good idea? Am I trying to over-engineer?

The answer is an uncomfortable "it depends". It depends how bad the pain is, it depends how crucial it is to get this right. It depends on so many factors that we can't really tell.

And to your: is this possible?

Yes, a builder pattern can be extended to return specific builders when certain methods are called, but this can become complicated and mis-uses are possible.

For your specific example I'd make the QueryBuilder simply have two methods:

  • a byGroup method that takes a group value to filter on and returns a GroupQueryBuilder
  • a bykey method that takes a key value to filter on and returns a KeyQueryBuilder.

Those two classes can then have methods that are distinct to their respective queries and possibly extend a shared base class that provides common properties.

And their respective build methods could either return a APIQuery or distinct APIQueryByGroup/APIQueryByKey classes, whichever is more useful for you.

This can become way more complicated if you have multiple axis upon which queries can differ and at a certain point, it'll become very hard to map that onto types.

Related