An interface method that has different parameters in Java

Look for some guidelines for developing Java code.

I currently have something like this ....

@Service class SomeService { @Autowired private FilterSoldOut filterSoldOut; @Autowired private FilterMinPriceThreshold filterMinPriceThreshold; public List<Product> getProducts() { List<Product> products = //...code to get some products // Returns list of in-stock products products = filterSoldOut.doFilter(products); // Returns list of products above min price products = filterMinPriceThreshold.doFilter(minPrice, products); return products; } } 

What I would like to do is create a filter interface using the doFilter method, and then create some List filters in SomeService that will be automatically added by Spring. Then in the getProducts method I can iterate over the filter list and call doFilter. Thus, in the future I will be able to create new classes that implement the Filter interface and add them to the list through the Spring configuration, and apply a new filter without the need to change the code.

But the problem is that the parameters of the doFilter method can be different. I read about the command pattern and the visitor pattern, but they don't seem to match the score.

Can anyone suggest a good sample to achieve what I described?

Thanks.

+4
source share
4 answers

old

I suggest you set the state of the filter during construction or not earlier than getProducts() .

In your example with two filters, one of them (possibly) checks the database for the presence of the product, and the other compares the price of the product with some given value. This value ( minPrice ) is known before the filter is applied. It may also be said that the filter depends on it or that it is part of the state of the filter. Therefore, I recommend you minPrice inside the filter during construction (or via setter), and then only pass the list of products that you want to filter. Use the same template for other filters.

new proposal (appeared after comments):

You can create one object (AllFiltersState) that contains all the values ​​for all filters. In your controller, set any criteria necessary for this object (minPrice, color, etc.) and pass them to each product filter - doFilter (allFiltersState, products).

0
source

There are many ways to do this. Some of them are complex, some of them are simpler. The easiest way is to use varargs or an array of Object elements. The problem here is that you have to use each objetc object for its correct type in order to use them, and it can be a little more complicated if several types exist in an unknown order.

Another option is to use Map<String,Object> (which you can wrap in your own class, if necessary, something lile FilterParams ), which stores the parameters based on the name, and you can then get them and apply them accordingly.


Edit

Given that the parameters change at runtime, you need someone to "inform" about the current configuration.

Not boilerplate, but I would rather keep it simple without using too many fancy names. How about introducing FilterConfigurator , which has a simple overloaded configure method that gets a specific filter and configures it based on its type ?. This configurator is an informed object that knows the current values ​​for these parameters.

The goal is to relieve Service of the responsibility to configure the filter.

In addition, if you create your Filter class, you can implement one doFilter , which you can call without changes.

There's another idea ... and it includes a FilterFactory , which creates and initializes filters, thereby having a 100% filter configured from scratch. This factory can rely on the same FilterConfigurer or do it on its own.

+1
source

As Cris says, you can use the following function definition:

  public List<Product> doFilter(Object...args) { if (args.length != 2) throw new IllegalArgumentException(); if (! (args[0] instanceof String)) throw new IllegalArgumentException(); if (! (args[2] instanceof Integer)) throw new IllegalArgumentException(); String stringArgument = (String) args[0]; Integer integerArgument = (Integer) args[1]; // your code here return ...; } 

or with the command template:

 public interface Command { } public class FirstCommand implements Command { private String string; // constructor, getters and setters } public class SecondCommand implements Command { private Integer integer; // constructor, getters and setters } // first service function public List<Product> doFilter(Command command) { if (command instanceof FirstCommand) throw new IllegalArgumentException(); FirstCommand firstCommand = (FirstCommand) command; return ...; } // second service function public List<Product> doFilter(Command command) { if (command instanceof SecondCommand) throw new IllegalArgumentException(); SecondCommand secondCommand = (SecondCommand) command; return ...; } 

EDIT:

Ok, I understand your question. And think that you can create various filters for sessions.

 @Service class SomeService { @Autowired(required = false) private List<Filter> filters; public List<Product> getProducts() { List<Product> products = //...code to get some products if (filters != null) { for (Filter filter : filters) products = filter.doFilter(products); } return products; } } 

And then create filters with settings fields:

 public PriceFilter implements Filter { private Integer minPrice; private Integer maxPrice; // getters and setters public List<Product> doFilter(List<Product> products) { // implementation here } } public ContentFilter implements Filter { private String regexp; // getters and setters public List<Product> doFilter(List<Product> products) { // implementation here } } 

The user can then configure these filters for the session and use the getProducts utility function to get the result.

0
source

Having a list of filters outsourced is not very suitable for solving your problem. Each filter depends on different types of parameters that must be passed to the doFilter method. The need for this makes the approach very inflexible. Yes, you can use varargs, but it will just create a mess. That's why it is probably easier to implement the builder to create a whole chain of filters to apply to a collection of products. Adding new filters to the constructor becomes a trivial task. The Builder pattern is very useful when many different parameters are played.

Consider this interface:

 public interface CollectionFilter<T> { public Collection<T> doFilter(Collection<T> collection); } 

A filter chain class that applies all filters to a collection:

 public class CollectionFilterChain<T> { private final List<CollectionFilter<T>> filters; public CollectionFilterChain(List<CollectionFilter<T>> filters) { this.filters = filters; } public Collection<T> doFilter(Collection<T> collection) { for (CollectionFilter<T> filter : filters) { collection = filter.doFilter(collection); } return collection; } } 

Two implementations of CollectionFilter<T> :

 public class InStockFilter<T> implements CollectionFilter<T> { public Collection<T> doFilter(Collection<T> collection) { // filter } } public class MinPriceFilter<T> implements CollectionFilter<T> { private final float minPrice; public MinPriceFilter(float minPrice) { this.minPrice = minPrice; } public Collection<T> doFilter(Collection<T> collection) { // filter } } 

And a creator that allows you to easily create a filter chain:

 public class CollectionFilterChainBuilder<T> { List<CollectionFilter<T>> filters; public CollectionFilterChainBuilder() { filters = new ArrayList<CollectionFilter<T>>(); } public CollectionFilterChainBuilder<T> inStock() { filters.add(new InStockFilter<T>()); return this; } public CollectionFilterChainBuilder<T> minPrice(float price) { filters.add(new MinPriceFilter<T>(price)); return this; } public CollectionFilterChain<T> build() { return new CollectionFilterChain<T>(filters); } } 

Using the builder, it's easy to create a filter chain as follows:

 CollectionFilterChainBuilder<Product> builder = new CollectionFilterChainBuilder(); CollectionFilterChain<Product> filterChain = builder.inStock().minPrice(2.0f).build(); Collection<Product> filteredProducts = filterChain.doFilter(products); 

In more dynamic settings, you can use the builder, for example:

 CollectionFilterChainBuilder<Product> builder = new CollectionFilterChainBuilder(); if (filterInStock) { builder.inStock(); } if (filterMinPrice) { builder.minPrice(minPrice); } // build some more 
0
source

Source: https://habr.com/ru/post/1495343/


All Articles