否则-重复代码逻辑


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 2015年

@CurtisHx过滤器C是必需的。是的,抱歉,我犯了复制粘贴的错误。
凯文·奇塔迪尼

2
这个问题如何与语言无关?Java中的惯用解决方案与Haskell中的惯用解决方案会有很大不同。您尚未确定项目语言吗?
200_success 2015年

Answers:


20

您还没有说过滤器是否带有任何参数。例如,filter_A可能是类别过滤器,所以它不仅是“我需要应用filter_A”的问题,还可能是“我需要应用filter_A并返回类别字段= fooCategory”的所有记录。

完全实现您所描述的内容的最简单方法(但请确保阅读下面的答案的后半部分)与其他答案类似,但是我根本不会进行布尔检查。我将定义接口:FilterA, FilterB, FilterC。然后,您可能会得到类似(我是Java程序员,所以这将是Java风格的语法):

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

然后,您可能会遇到这样的事情(使用来自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三种过滤器的调用实际上都非常相似。如果真是这样,我什至不会以上述方式进行。通过仅具有一个接口,您可以得到更简洁的代码,然后执行以下操作:

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 2015年

如果您拥有Filteras,Predicate则可以直接在StreamAPI中使用它。许多语言具有相似的功能构造。
蜘蛛鲍里斯(Boris)

3
@BoristheSpider只有在他使用Java 8的情况下;他甚至没有说他使用的语言。其他语言确实具有这样的构造,但我不想深入探讨如何做到这一点
durron597

3
理解-值得一提的是,这是探索OP是否要提供最干净的实现的途径。您肯定会对我的+1感到满意。
蜘蛛鲍里斯(Boris)

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

在控制运行哪些过滤器和执行过滤器之间有明显的区别。分开这两个逻辑。


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

当然,您必须定义过滤器处理子例程。


1

我假设filterA,filterB和filterC实际上修改了产品列表。否则,如果它们只是if-check,则filterA和filterB可以忽略,因为所有路径最终都通向filterC。您对要求的描述似乎暗示每个过滤器都会减少产品列表。

因此,假设过滤器实际上减少了产品列表,下面是一些伪代码...

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,则可以调用applyFilter(filterC,products)而不检查是否选择了filterC。

filterC = instance of filter, always chosen

...

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

0

我不知道将过滤器建模为图形中的某种对象是否有意义。至少这就是我在查看图表时所想到的。

如果您像对象图一样对过滤器的依赖关系进行建模,那么处理可能的流路的代码将非常简单,无需任何冗长的逻辑。同样,图(业务逻辑)可以更改,而解释图的代码保持不变。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.