java防止了很多if并用设计模式替换它

时间:2017-02-08 06:56:37

标签: java-8

我在我的应用程序中使用此代码,我发现它非常难看。 这样做有一种聪明的方法吗?

for (final ApplicationCategories applicationCategorie : applicationCategories) {
          if (applicationCategorie == ApplicationCategories.PROJECTS) {
              // invoke right method
            } else if (applicationCategorie == ApplicationCategories.CALENDAR) {
              // ...
            } else if (applicationCategorie == ApplicationCategories.COMMUNICATION) {

            } else if (applicationCategorie == ApplicationCategories.CONTACTS) {

            } else if (applicationCategorie == ApplicationCategories.DOCUMENTS) {

            } else if (applicationCategorie == ApplicationCategories.WORKINGBOOK) {

            }
        }

我的目标是处理包含在枚举列表中的所有应用程序类别枚举。

3 个答案:

答案 0 :(得分:5)

您可以做的最少的事情是声明处理依赖于<thead> <tr> <th> "text" <sup data-toggle="popover" data-content="sometext" data-placement='top' data-original-title title> <a href='' data-toggle='modal' data-target='someone'> read more help </a></sup> </th> </tr> </thead> enum内部行为的行为的方法。这样,如果要为枚举添加新值,则只需更改相对于枚举的代码。

通过这种方式,您的代码遵循开放封闭原则,因此更容易维护。

ApplicationCategories

只有在您不需要任何外部信息来处理枚举值时,此解决方案才可行。

请记住,您还可以将字段添加到枚举值中。

修改

如果需要,您还可以实施策略设计模式。首先,定义一个策略接口和一些具体的实现。

enum ApplicationCategories {
    PROJECTS,
    CALENDAR,
    // And so on...
    WORKINGBOOK;

    public static void handle(ApplicationCategories category) {
        switch (category) {
            case PROJECTS:
                // Code to handle projects
                break;
            case CALENDAR:
                // Code to handle calendar
                break;
            // And so on
        }
    }
}

然后,您可以修改枚举以使用上述策略。

interface CategoryStrategy {
    void handle(/* Some useful input*/);
}
class ProjectStrategy implements Strategy {
    public void handle(/* Some useful input*/) {
        // Do something related to projects...
    }
}
class CalendarStrategy implements Strategy {
    public void handle(/* Some useful input*/) {
        // Do something related to calendars...
    }
}
//...

显然,上面的代码只是草图。

答案 1 :(得分:4)

您需要的设计模式是Strategy

枚举和违反开放/封闭原则

当您必须为每个定义的值执行不同的操作时使用枚举是一种不好的做法。随着软件的发展,您可能需要在不同的地方传播if链。如果添加新的枚举值,则必须在所有这些位置为该值添加新的if。因为您甚至无法找到所有必须包含新if的地方,这是错误的来源。

这种方法也违反了Open/Closed Principle (OCP)仅创建处理每个枚举值的方法不会使您的代码符合OCP 。它将使代码更有条理,但不会改变有关“if”问题的任何内容。

具有策略模式的Java 7解决方案

使用Java 7或更早版本,您可以定义所有类别将实现的ApplicationCategory接口。此接口将提供一个通用方法,每个类别将实现该方法以执行该类别所需的操作:

public interface ApplicationCategory {
     boolean handle();
}

通常你的方法应该返回一些东西。由于我不知道你的确切目标是什么,我正在使该方法只返回一个布尔值。它将表明该类别是否已被处理,仅作为一个例子。

然后你必须为你拥有的每个类别定义一个实现这种接口的类。例如:

public class CalendarCategory implements ApplicationCategory {
     boolean handle(){
         //the code to handle the Calendar category
         return true;
     }
}

public class CommunicationCategory implements ApplicationCategory {
     boolean handle(){
         //the code to handle the Communication category
         return true;
     }
}

现在你不需要enum类,并且里面的handle方法需要移动到其他地方,这完全取决于你的项目。该handle方法将更改为:

public static void handle(ApplicationCategory category) {
    //Insert here any code that may be executed,
    //regardless of what category it is.
    category.handle();
}

您不再需要枚举,因为声明为ApplicationCategory的任何变量只接受实现此类接口的对象。 如果您将枚举与策略实施一起使用,则在您添加新的ApplicationCategory实施时,还需要更改枚举类,再次违反OCP

如果您使用策略模式,在这种情况下甚至不再需要枚举

具有函数式编程和策略模式的Java 8解决方案

您可以使用函数式编程和lambda表达式更轻松地实现策略模式,并避免类的扩散只是为了提供单个方法的不同实现(在这种情况下为handle方法)。

由于handle方法没有接收任何参数并且正在重新调整某些内容,因此该描述符合Supplier功能接口。它是识别您定义的方法所适用的功能接口的一种很好的方法,它正在研究java.util.function包。

一旦确定了功能接口的类型,我们就可以以功能的方式创建一个ApplicationCategory类(在Java 7示例中是一个接口),将以前的handle方法定义为Supplier类型的属性。您必须为此handle属性定义一个setter才能启用更改句柄实现。将方法定义为属性,您要在运行时更改此类方法实现,从而为策略模式提供不同但更简单,更简单且更易于维护的实现。

如果您需要在某处使用类别名称,例如在用户界面中显示它,则可以在ApplicationCategory类中定义枚举。但是,枚举值与提供的handle之间没有直接关系。枚举仅用作类别的标记。它就像Person类中的“名称”属性,我们通常只是用来“标记”并打印一个人。

public class ApplicationCategory {
     //insert all categories here
     enum Type {CALENDAR, COMMUNNICATION} 

     /**
      * The Supplier object that will handle this category object.
      * It will supply (return) a boolean to indicate if the category
      * was processed or not.
     */
     private Supplier<Boolean> handler;

     private Type type;

     /**
     * A constructor that will receive a Supplier defining how to 
     * handle the category that is being created.
     */
     public ApplicationCategory(Type type, Supplier<Boolean> handler){
         Objects.requireNonNull(type);
         this.handler = handler;
         setType(type);
     } 

     /**
     * Handle the category by calling the {@link Supplier#get()} method,
     * that in turn returns a boolean.
     */
     public boolean handle(){
         return supplier.get();
     }

     public Type getType(){ return type; }

     public final void setHandler(Supplier<Boolean> handler){
        Objects.requiredNonNull(handler);
        this.handler = handler;
     }
}

如果您在枚举构造函数调用中给出将处理枚举值的行为,如此处提供的其他答案所示,那么您没有如何在运行时更改行为并且它实际上不符合战略模式。除非你真的不需要,否则你可能会以这种方式实现,但请记住它违反了OCP。

如何使用Java 8功能的ApplicationCategory类

要实例化ApplicationCategory,您必须提供Type(枚举值)和处理程序(即Supplier,并且可以作为lambda表达式给出)。请参阅以下示例:

import static ApplicationCategory.CALENDAR;

public class Test {
    public static void main(String args[]){
        new Test();
    }

    public Test(){
        ApplicationCategory cat = new ApplicationCategory(CALENDAR, this::calendarHandler);
        System.out.println("Was " + cat + " handled? " + cat.handle());
    }

    private boolean calendarHandler(){
        //the code required to handle the CALENDAR goes here
        return true;
    }
}

this::calendarHandler指令是method reference,用于将“指针”传递给calendarHandler方法。它不是调用方法(你可以看到由于使用了::而不是。而且缺少括号),它只是定义了在调用handle()方法时实际调用的方法,如System.out.println("Was " + cat + " handled? " + cat.handle());

中所示

通过使用此方法,可以为同一类别的不同实例定义不同的处理程序,或者为类别子集使用相同的处理程序。

答案 2 :(得分:2)

与其他语言不同,Java提供了专门允许以面向安全的方式完成此类事情的工具。

在枚举上声明一个抽象方法,然后为每个枚举常量声明特定的实现。然后,编译器将确保每个常量都有一个实现,并且没有人担心在添加新值时遗漏某个案例:

enum ApplicationCategories {
    PROJECTS {
       void handle() {
           ...
       }
    },
    ...

    public abstract void handle();
}

然后,您只需拨打handle(category)

,而不是调用某些静态category.handle()