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 で、Spring によって自動配線される List フィルターを作成することです。次に、getProducts メソッドでフィルター リストを繰り返し処理し、doFilter を呼び出します。このようにして、将来、Filter インターフェースを実装する新しいクラスを作成し、Spring 構成を介してそれらをリストに追加し、コードを変更することなく新しいフィルターを適用することができます。

しかし、問題は doFilter メソッドへのパラメーターが異なる可能性があることです。コマンド パターンとビジター パターンについて読んだことがありますが、それらは法案に適合していないようです。

私が説明したことを達成するための良いパターンを誰かが提案できますか?

ありがとう。

4

4 に答える 4

0

自動配線されるフィルターのリストを持つことは、問題を解決するためのあまり良い方法ではありません。すべてのフィルターは、メソッドに渡す必要があるさまざまなタイプのパラメーターに依存しますdoFilter。そうする必要があると、このアプローチは非常に柔軟性がなくなります。はい、可変引数を使用できますが、混乱を招くだけです。そのため、製品のコレクションに適用されるフィルターのチェーンを構築するビルダーを実装する方がおそらく簡単です。ビルダーに新しいフィルターを追加するのは簡単な作業になります。Builder パターンは、さまざまなパラメーターが多数使用されている場合に非常に役立ちます。

このインターフェースを持つことを検討してください:

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

2 つの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 に答える
0

クリスが言うように、次の関数定義を使用できます。

  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()

2 つのフィルターを使用した例では、そのうちの 1 つは (おそらく) データベースで製品の入手可能性をチェックしており、もう 1 つは製品の価格を事前に設定された値と比較しています。この値 ( minPrice) は、フィルタが適用される前に既知です。フィルターがそれに依存している、またはフィルターの状態の一部であるとも言えます。したがって minPrice、構築時に (またはセッターを介して) フィルターの内側に配置し、フィルター処理する製品のリストのみを渡すことをお勧めします。他のフィルターにも同じパターンを使用します。

新しい提案(コメントの後に思いついた):

すべてのフィルターのすべての値を保持する単一のオブジェクト (AllFiltersState) を作成できます。コントローラーで、このオブジェクトに必要な条件 (minPrice、色など) を設定し、それを製品に沿ってすべてのフィルターに渡します - doFilter(allFiltersState, products)。

于 2013-08-05T16:14:14.643 に答える