web-dev-qa-db-ja.com

それ以外の場合-コードロジックの繰り返し

上司から、特定のロジックを備えたプロジェクトが提供されました。ナビゲーターが商品にたどり着くまで、多くの場合を案内するWebページを開発する必要があります。

これは、サイト内のナビゲーションのパススキームです。

Path Scheme

重要!

製品ページでは、ナビゲーターは希望するフィルターを選択できます。

  • Aの場合、彼/彼女[〜#〜] [〜#〜]はB(そしてもちろんC)またはCを通過して到達する必要がありますプロダクト。
  • Bの場合、彼/彼女[〜#〜] [〜#〜]がCを通過して製品に到達する必要があります。
  • Cの場合、彼/彼女は製品に直接到達します。

もちろん、Aから始めた場合、最長の経路をたどり、製品に到達すると、3つのアクティブフィルターがあります。

今まで、私は次のコードを開発しました。

if filter_A
  if filter_B
     filter_C()
     .. else ..
  else
     filter_C
    .. else ..
else
   if filter_B
      filter_C()
     .. else ..
   else
     filter_C()
     .. else ..

私はここで、この状況でより専門的なプログラマーが何をしたかを尋ねます。私はDRY=原則を尊重しませんでした。私はそれが好きではないので、この種のロジックを開発する別の方法を知りたいです。

コードのすべてのセクションを関数に分割することを考えましたが、この場合は良い考えですか?

15
Kevin Cittadini

フィルターがパラメーターをとるかどうかはまだ言っていません。例えば、 filter_Aはカテゴリフィルタである可能性があるため、「適用する必要があるかどうかfilter_A」、「適用する必要があるfilter_Aで、カテゴリフィールド= fooCategory "のすべてのレコードを返します。

あなたが説明したものを正確に実装する最も簡単な方法(ただし、以下の回答の後半を必ず読んでください)は他の回答と似ていますが、私は持っていませんすべてのブール値チェック。私はインターフェイスを定義します:FilterA, FilterB, FilterC。それからあなたは(私はJavaプログラマーなので、これはJava風の構文になります)のようなものを持つことができます:

class RequestFilters {
    FilterA filterA;
    FilterB filterB;
    FilterC filterC;
}

それから、あなたはこのような何かを持つことができます(- enum singleton pattern from Effective Java を使用):

enum NoOpFilterA implements FilterA {
    INSTANCE;

    public List<Item> applyFilter(List<Item> input) {
       return input;
    }
}

ただし、一部のアイテムを実際にフィルタリングする場合は、代わりに実際に何かを行うFilterA実装のインスタンスを提供できます。あなたのろ過方法は非常に簡単になります

List<Item> filterItems(List<Item> data, RequestFilters filters) {
    List<Item> returnedList = data;
    returnedList = filters.filterA.filter(data);
    returnedList = filters.filterB.filter(data);
    returnedList = filters.filterC.filter(data);
    return returnedList;
}

しかし、私はまだ始まったばかりです。

applyFilterの呼び出しは、実際には3種類のフィルターすべてで非常によく似ていると思います。もしそうなら、私はそれを上記の方法で行うことすらしません。インターフェースを1つだけにし、これを行うことで、よりクリーンなコードを取得できます。

class ChainedFilter implements Filter {
     List<Filter> filterList;

     void addFilter(Filter filter) {
          filterList.add(filter);
     }

     List<Item> applyFilter(List<Item> input) {
         List<Item> returnedList = input;
         for(Filter f : filterList) {
             returnedList = f.applyFilter(returnedList);
         }
         return returnedList;
     }
}

次に、ユーザーがページを移動するときに、必要に応じて必要なフィルターの新しいインスタンスを追加します。これにより、将来その動作が必要になった場合に、同じフィルターの複数のインスタンスを異なる引数で適用できるようになります。また、設計を変更せずに将来フィルターを追加することもできます

さらに、上記のNoOpFilterのようなものを追加することも、特定のフィルターをリストにまったく追加しないこともできます。

20
durron597

この場合、フィルタリングのロジックと、フィルターの実行方法の制御フローを分離することが重要です。フィルターロジックは、互いに独立して実行できる個別の機能に分離する必要があります。

ApplyFilterA();
ApplyFilterB();
ApplyFilterC();

投稿されたサンプルコードには、3つのブール値filter_Afilter_Bfilter_C。ただし、図からfilter_Cは常に実行されるため、無条件に変更できます。

注:制御フロー図が正しいと想定しています。投稿されたサンプルコードと制御フロー図に不一致があります。

別のコードが実行するフィルターを制御します

ApplyFilters(bool filter_A, bool filter_B)
{
    listOfProducts tmp;
    if (filter_A)
        ApplyFilterA();
    if (filter_B)
        ApplyFilterB();
    ApplyFilterC();
}

実行するフィルターの制御とフィルターの機能には明確な違いがあります。これらの2つのロジックを分解します。

3
CurtisHx

私は、あなたが最も単純で明確なアルゴリズムを望んでいると思います。
この場合、フィルターcが常に適用されることを知っているので、ifロジックの外でそれを実行し、関係なく最後に適用します。フローチャートにあるように、cの前の各フィルターはオプションです。これらのフィルターはそれぞれ適用できるかどうかのどちらかです。この場合、入れ子や連鎖をせずに、各フィルターとは別にifを使用します。

if filter_a
  do_filter_a()

if filter_b
  do_filter_b()

do_filter_c()

フィルターの数が可変のフローチャートがある場合、必須のフィルターの前に、代わりに、すべてのフィルターを表示される順序で配列に保存します。次に、オプションのフィルターをループで処理し、必須のフィルターをループの外側の最後に適用します。

optional_filters_array = (a, b, c, d, e, f, g, h, etc)

for current_filter in optional_filters_array
  do_filter(current_filter)

do_required_filter()

または:

optional_filters_array = (a, b, c, d, e, f, g, h, etc)
required_filter = last_filter


for current_filter in optional_filters_array
  do_filter(current_filter)

do_filter(required_filter)

もちろん、フィルター処理サブルーチンを定義する必要があります。

2
igoryonya

FilterA、filterB、filterCが実際に製品のリストを変更すると想定します。そうでない場合、それらが単なるifチェックである場合、すべてのパスが最終的にfilterCにつながるため、filterAとfilterBは無視できます。要件の説明は、各フィルターが製品リストを減らすことを意味するようです。

フィルターが実際に製品のリストを減らすと仮定すると、ここに疑似コードが少しあります...

class filter
    func check(item) returns boolean
endclass

func applyFilter(filter, productList) returns list
    newList is list
    foreach item in productList
        if filter.check(item) then
            add item to newList
        endif
    endfor 
    return newList
endfunc



filterA, filterB, filterC = subclasses of filter for each condition, chosen by the user
products = list of items to be filtered

if filterA then
    products = applyFilter(filterA, products)
endif

if filterB then
    products = applyFilter(filterB, products)
endif

if filterC then
    products = applyFilter(filterC, products)
endif

# use products...

要件では、filterCは自動的に適用されませんが、図では適用されます。少なくともfilterCを適用する必要がある場合は、filterCが選択されているかどうかを確認せずにapplyFilter(filterC、products)を呼び出します。

filterC = instance of filter, always chosen

...

# if filterC then
products = applyFilter(filterC, products)
# endif
1
Kent A.

フィルターをグラフ内のある種のオブジェクトとしてモデル化することは意味があるのでしょうか。少なくとも、それを図で見ると思います。

オブジェクトグラフのようにフィルターの依存関係をモデル化する場合、考えられるフローパスを処理するコードは、複雑なロジックがなくても非常に単純です。また、グラフ(ビジネスロジック)は変更できますが、グラフを解釈するコードは同じままです。

0
enum