I have a class that has a fixed set of filters and is responsible for fetching data from an search API (lets say "PERSON;CAR;BUILDING"). Now I want to wrap this search class inside an "Extended" search (by using something like an adapter pattern), and add an additional endpoint which fetches also ANIMALS (another endpoint). For the Extended class I have the Combined filter "PERSON;CAR;BUILDING;ANIMALS" where the first three still work for the one API and the last one (ANIMALS) works for the second API.
My question is, is there a nice approach to make these filters somehow compatible/dependant on each other. The best solution would be to extend the first filter with the items for "Combined" filter. I have been playing around with sealed interfaces/classes but so far I could not find a nice way to do that. My solution for now is to use enums (copy the values from all filters) and make a mapper from the combined filter to the real search filter. The issues with this approach are that I am duplicating all filter values and in case the search filter gets extended it could easily be forgotten to also extend the combined filter.
Edit: added code
enum class SearchFilter { PERSON, CAR, BUILDING }
class GeneralSearch {
fun search (filter: SearchFilter)
}
class AnimalSearch {
fun search ()
}
Combine both api-s:
enum class CombinedSearchFilter { PERSON, CAR, BUILDING, ANIMALS }
class CombinedSearch (
generalSearch: GeneralSearch,
animalSearch: AnimalSearch
) {
fun search (filter: CombinedSearchFilter)
}
The issue is that the CombinedSearchFilter is duplicating the values from SearchFilter without any constraint.
You can make
SearchFilter,CombinedSearchandAnimalSearchsealed interfaces. Each filter (the enum values) would then implement one or more of those interfaces. You can makeSearchFilterandAnimalSearchinherit fromCombinedSearchas well.or put the individual filters in other objects if you want code completion:
Because of this type hierarchy,
CombinedSearch.searchwould be able to take all 4 ofPerson,Car,BuildingandAnimal.The downside of this is that if you happen to have another combination of APIs, you would need to modify the declarations of all the involved filter types. For example, if there were a third API with a
FooFiltertype:And you want a
CombinedFilter2that combinesFooFilters andSearchFilters, you'd need to add:If you want code-completion on
CombinedFilter, then I can't think of any way unless you get rid ofsealed.Represent each filter as anonymous object properties, and wrap
objects around them. The combined filters will have two properties, each initialised to theobjectcontaining the constituent filters.Another option is to keep using enums, but
CombinedSearchwould take aEither<AnimalFilter, SearchFilter>.Example usage:
The downside of this is that it's more inconvenient on users' side - there is whole lot more "ceremony".