4

寻找一些关于在 Java 中设计一些代码的指导。

目前我有这样的事情......

@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; 
    }
}

我想做的是用 doFilter 方法创建一个 Filter 接口,然后在 SomeService 中创建一个 List 过滤器,它是由 Spring 自动装配的。然后在 getProducts 方法中,我可以迭代过滤器列表并调用 doFilter。这样,将来我可以创建实现过滤器接口的新类,并通过 Spring 配置将它们添加到列表中,并应用新过滤器而无需更改代码。

但是,问题在于 doFilter 方法的参数可能不同。我读过命令模式和访问者模式,但它们似乎不太符合要求。

任何人都可以提出一个好的模式来实现我所描述的吗?

谢谢。

4

4 回答 4

1

有很多方法可以做到这一点。有些比较复杂,有些比较简单。最简单的方法是使用一个元素varargs数组。Object这里的问题是,您必须将每个 objetc 转换为正确的类型才能使用它们,如果有多种类型以未知的顺序出现,这可能会有点棘手。

另一种选择是使用 a Map<String,Object>(如果需要,您可以将其包装在您自己的类中,一些 lile FilterParams)基于名称存储参数,然后您可以获取它们并相应地转换它们。


编辑

考虑到参数在运行时会有所不同,您需要有人“充分了解”当前配置。

不是模式,但我宁愿保持简单而不使用太多花哨的名字。如何引入一个FilterConfigurator具有简单重载方法的方法configure,该方法接收特定过滤器并根据其类型对其进行配置?该配置器是了解这些参数的当前值的知情实体。

目标是摆脱Service配置过滤器的责任。

此外,如果您创建了自己的Filter类,您将能够实现一个doFilter您无需更改即可调用的单一类。

还有另一个想法......它涉及FilterFactory创建初始化过滤器,因此从头开始配置过滤器 100%。这家工厂可以完全依赖FilterConfigurer它,也可以自己做。

于 2013-08-05T15:47:49.887 回答
0

正如 Cris 所说,您可以使用下一个函数定义:

  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 ...;
  }

或使用命令模式:

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 ...;
}

编辑:

好的,我明白你的问题。并认为您可以创建各种会话范围的过滤器。

@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; 
    }
}

然后使用设置字段创建过滤器:

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
  }
}

然后用户可以为会话配置此过滤器并使用服务功能getProducts获取结果。

于 2013-08-05T15:45:27.130 回答
0

我建议您在施工时或至少在您之前设置过滤器状态getProducts()

在您使用两个过滤器的示例中,其中一个是(可能)检查数据库以了解产品的可用性,另一个是将产品的价格与某个预设值进行比较。该值 ( minPrice) 在应用过滤器之前是已知的。也可以说过滤器依赖于它,或者它是过滤器状态的一部分。因此,我建议您 minPrice在构建时(或通过设置器)将过滤器放在内部,然后只传递您要过滤的产品列表。对其他过滤器使用相同的模式。

新建议(在评论后提出):

您可以创建一个对象 (AllFiltersState) 来保存所有过滤器的所有值。在您的控制器中设置您在此对象中需要的任何标准(minPrice、颜色等)并将其传递给产品中的每个过滤器 - doFilter(allFiltersState, products)。

于 2013-08-05T16:14:14.643 回答
0

拥有一个自动装配的过滤器列表并不是解决问题的好方法。每个过滤器都依赖于需要传递给doFilter方法的不同类型的参数。需要这样做使得该方法非常不灵活。是的,您可以使用可变参数,但它只会造成混乱。这就是为什么实现构建器来构建过滤器链以应用于产品集合的原因可能更容易。向构建器添加新过滤器成为一项微不足道的任务。当有很多不同的参数在起作用时,建造者模式非常有用。

考虑有这个接口:

public interface CollectionFilter<T> {
    public Collection<T> doFilter(Collection<T> 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;
    }
}

两种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
    }
}

还有一个构建器,可让您以简单的方式构建过滤器链:

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);
    }
}

使用构建器可以轻松创建过滤器链,如下所示:

CollectionFilterChainBuilder<Product> builder = 
    new CollectionFilterChainBuilder();

CollectionFilterChain<Product> filterChain = 
    builder.inStock().minPrice(2.0f).build();

Collection<Product> filteredProducts = 
    filterChain.doFilter(products);

在更动态的设置中,您可以使用如下构建器:

CollectionFilterChainBuilder<Product> builder = new CollectionFilterChainBuilder();

if (filterInStock) {
    builder.inStock();
}

if (filterMinPrice) {
    builder.minPrice(minPrice);
}

// build some more
于 2013-08-05T16:55:24.237 回答