If Else-繰り返されるコードロジック


15

私の上司は、特定のロジックを持つプロジェクトをくれました。ナビゲーターが製品に到着するまで多くの場合をナビゲートする必要があるWebページを開発する必要があります。

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

パススキーム

重要!

製品ページで、ナビゲーターは必要なフィルターを選択できます。

  • Aの場合、彼/彼女はB(そしてもちろんC)またはCを通過して製品に到達しなければなりません
  • Bの場合、彼/彼女はCを通過して製品に到達しなければなりません
  • Cの場合、彼/彼女は直接製品に到達します。

もちろん、AIから開始する場合、最長パスをたどり、製品に到達すると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の原則を尊重しませんでした。私はそれが気に入らず、この種のロジックを開発する別の方法を知りたいです。

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



制御フロー図には、すべての制御が通過するfilter_Cことが示されていますが、条件文は、制御フローが迂回できることを示していfilter_Cます。あるfilter_Cオプション?
CurtisHx

@CurtisHxフィルターCは必須です。申し訳ありませんが、コピーペーストを行って間違えました。
ケビンチッタディーニ

2
この質問はどのように言語に依存しませんか?Javaの慣用的なソリューションは、Haskellの慣用的なソリューションとは大きく異なります。プロジェクトの言語を決めていませんか?
200_success

回答:


20

フィルターがパラメーターを受け取るかどうかについては言及していません。たとえばfilter_A、カテゴリフィルタの場合、「適用する必要がある」という質問だけでなく、「カテゴリフィールド=ですべてのレコードfilter_Aを適用filter_Aして返す必要がある」などの問題もありますfooCategory

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

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

次に、このようなものを持つことができます(を使用して 列挙シングルトンパターンから効果的な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上記のようなものを追加することも、コードに簡単なものであれば、特定のフィルターをリストにまったく追加することもできません。


コードを変更せずにロジックを変更するための最も簡単な方法を見つけるため、ありがとうございます。これがあなたの答えを最高にします。このコード設計をできるだけ早く実装します
ケビンチッタディーニ

を持ってFilterいる場合Predicateは、StreamAPI で直接使用できます。多くの言語には、同様の機能構成があります。
ボリス・スパイダー

3
@BoristheSpiderそれは、彼がJava 8を使用している場合のみです。彼はどんな言語を使っているのかさえも言わなかった。他の言語にはそのような構造がありますが、私はそれを行う方法のすべての異なるフレーバーに行きたくありませんでした
-durron597

3
理解された-OPが可能な限りクリーンな実装を提供したい場合、それは探索する手段であることに言及するだけの価値がある。すでに素晴らしい回答を得るために、確かに私の+1があります。
ボリス・スパイダー

3

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

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つのロジックを分離します。


+1これは、受け入れられている答えよりもはるかに単純で、分離されているようです。
winkbrace

2

最も単純で明確なアルゴリズムが必要だと思います。
この場合、フィルター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)

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


1

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

0

グラフ内のある種のオブジェクトになるようにフィルターをモデル化するのは理にかなっているのだろうか。少なくとも、図を見たときにそう思う。

オブジェクトグラフのようにフィルターの依存関係をモデル化する場合、可能なフローパスを処理するコードは、毛深いロジックなしで非常に簡単です。また、グラフ(ビジネスロジック)は変更できますが、グラフを解釈するコードは同じままです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.