그렇지 않은 경우-반복 코드 논리


15

상사는 나에게 특정한 논리를 가진 프로젝트를 주었다. 제품에 도착할 때까지 네비게이터를 이끌어야하는 웹 페이지를 개발해야합니다.

이것은 사이트 내 탐색 경로 체계입니다.

경로 체계

중대한!

제품 페이지에서 네비게이터는 원하는 필터를 선택할 수 있습니다.

  • 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는 필수입니다. 예, 실수로 복사하여 붙여 넣기를했습니다.
Kevin Cittadini

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

그럼 당신은 다음과 같이합니다 (사용 할 수 열거 싱글 톤 패턴 에서 효과적인 자바 ) :

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세 가지 유형의 필터 모두 에서 호출이 실제로 매우 유사하다고 생각합니다 . 이 경우 위에서 설명한 방식으로 수행하지 않을 것입니다. 하나의 인터페이스 만 있으면 더 깨끗한 코드를 얻을 수 있습니다.

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위와 같은 것을 추가하거나 코드에 더 쉬운 모든 목록에 특정 필터를 전혀 추가 할 수 없습니다.


코드를 변경하지 않고도 로직을 변경할 수있는 가장 간단한 방법을 찾아 주셔서 감사합니다. 이것은 귀하의 답변을 최고로 만듭니다. 이 코드 디자인을 최대한 빨리 구현하겠습니다
Kevin Cittadini

당신이 있다면 FilterA와 Predicate당신은에서 직접 사용할 수있는 StreamAPI. 많은 언어들이 비슷한 기능적 구성을 가지고 있습니다.
Boris the Spider

3
@BoristheSpider Java 8을 사용하는 경우에만 해당됩니다. 그는 자신이 어떤 언어를 사용하고 있는지조차 말하지 않았습니다. 다른 언어들도 그런 구조를 가지고 있지만 그 방법에 대한 모든 다른 맛에 들어가고 싶지 않았습니다
durron597

3
이해-OP가 가장 깨끗한 구현을 제공하려는 경우 탐구 할 수있는 방법이라고 언급 할 가치가 있습니다. 이미 훌륭한 답변을 얻으려면 내 +1이 있어야합니다.
Boris the Spider

3

이 경우 필터링 논리와 필터 실행 방식의 제어 흐름을 분리하는 것이 중요합니다. 필터 로직은 서로 독립적으로 실행될 수있는 개별 기능으로 분리되어야합니다.

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

게시 된 샘플 코드에서이 3 부울있어 filter_A, filter_B하고 filter_C. 그러나 다이어그램에서 filter_C항상 실행되므로 무조건으로 변경할 수 있습니다.

참고 : 제어 흐름도가 정확하다고 가정합니다. 게시 된 샘플 코드와 제어 흐름도간에 불일치가 있습니다.

필터가 실행되는 별도의 코드 제어

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

어떤 필터를 실행할지 제어하는 ​​것과 필터가하는 일을 명확하게 구분합니다. 이 두 가지 논리를 분리하십시오.


+1 이것은 허용 된 답변보다 훨씬 간단하고 분리 된 것 같습니다.
winkbrace

2

가장 단순하고 명확한 알고리즘을 원한다고 가정합니다.
이 경우 필터 c가 항상 적용된다는 것을 알고 있으면 if 로직에서 벗어나 결국에 적용합니다. 플로우 차트에서 볼 수 있듯이 c 앞의 각 필터는 선택 사항입니다. 각 필터를 적용 할 수 있는지 여부입니다. 이 경우 중첩 및 연결없이 각 필터와 별도로 ifs를 사용합니다.

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)

cource의 경우 필터 처리 서브 루틴을 정의해야합니다.


1

filterA, filterB 및 filterC가 실제로 제품 목록을 수정한다고 가정하겠습니다. 그렇지 않으면, 만약 그들이 단지 체크라면, 모든 경로는 궁극적으로 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.