【问题标题】:refactoring and removing case statements when circling over an enum structure在枚举结构上循环时重构和删除 case 语句
【发布时间】:2010-11-26 15:20:54
【问题描述】:

在自己的类中声明的枚举结构是业务逻辑类的成员变量。该枚举基本上代表了其他类的状态。

虽然我已经多次重新审视这个问题,但替换或删除这些案例陈述对我来说非常令人沮丧。

几个业务逻辑方法简单地迭代枚举并通过分配相同枚举的另一个值和其他属性来更改该类的状态。

public enum MyEnum{ A,B,C,D }

业务逻辑类有这个枚举成员:

public class BusinessLogic {

    private MyEnum CurrentSelection;
    private int propertyX;
    private int propertyY;

    public void operation1(){
        switch(CurrentSelection){
        case A: {alter propertyX this way; break;}
        case B: {alter propertyY this way; break;}
        case C: {alter propertyX that way; break;}
        case D: {alter propertyY that way; break;}
        }

    }

    public void operation2(){
        switch(CurrentSelection){
        case A: {CurrentSelection=MyEnum.B; break;}
        case B: {CurrentSelection=MyEnum.C; break;}
        ....etc
        }
    }

    public void operation3(){
        switch(CurrentSelection){
        case A: {CurrentSelection=MyEnum.D; break;}
        case B: {CurrentSelection=MyEnum.A; break;}
        ....etc
        }
    }
}

另一个客户端类将实例化业务逻辑类,初始化其属性,然后使用其操作方法。

我已经成功完成(在 SO 的帮助下)将操作方法​​封装到命令模式结构中,这样我就可以在没有任何 case 语句的情况下调用操作。 (here)。

我想我的麻烦是如何在我的业务逻辑类中封装案例语句。我怀疑我需要多态性和适当的数据结构。

重构专家建议每个 case 语句都应该是一个通用接口的实现。但是如果我有 3 个方法迭代一个 4 成员枚举,这意味着我可能需要 3 个接口,每个接口有 4 个实现,给我 12 个类(加上 3 个接口)。那不会是类重载吗?这 3 种方法的逻辑运行良好,但问题在于重复的 switch/case 语句。

有没有办法重构这些 switch 语句,但又避免以多态的名义出现无数其他类?是否可以排除迭代枚举部分?还是只重构案例(逻辑所在)?


作为第一步,我完全删除了这些方法,让它们实现一个简单的接口:

public interface Command {
    void execute();
}

所以,它的操作方法实现了命令界面:

public class Operation1 implements Command {

    private void doOperation1(){
        switch(CurrentSelection){
            ..all the cases here
        }       
    }

    public void execute() {
        doOperation1();
    }

}

正如我所看到的,它会为我的业务逻辑类带来更简洁的效果,但 switch case 的痕迹会保留,对吧?

【问题讨论】:

    标签: java design-patterns refactoring polymorphism switch-statement


    【解决方案1】:

    正如您可能从其他回复中猜到的那样,这是一个常见问题,已被 Martin Fowler 编入重构中。看看重构Replace Conditional with Polymorphism。 Joshua Kerievsky (Replace Conditional with Strategy) 也进行了类似的编码重构。

    【讨论】:

    • 是的,这就是我在原始帖子中所说的:“重构专家建议每个案例语句应该是一个通用接口的实现”。然而,在这一次中,对我来说并不简单。
    【解决方案2】:

    要详细说明我的评论,并接近我认为 rmn 所说的内容,您可以执行以下操作:

    public interface MyInterface {
        operation1(property);
        operation2(property);
        operation3(property);
    }
    
    public enum MyEnum implements MyInterface {
        A {
            operation1(property) {
                //Do Stuff
            }
            operation2(property) {
                //Do Stuff
            }
            operation3(property) {
                //Do Stuff
            }
        },
        B {
            operation1(property) {
                //Do Stuff
            }
            operation2(property) {
                //Do Stuff
            }
            operation3(property) {
                //Do Stuff
            }
        },
        C {
            operation1(property) {
                //Do Stuff
            }
            operation2(property) {
                //Do Stuff
            }
            operation3(property) {
                //Do Stuff
            }
        },
        D {
            operation1(property) {
                //Do Stuff
            }
            operation2(property) {
                //Do Stuff
            }
            operation3(property) {
                //Do Stuff
            }
        } 
    }
    

    这使您拥有一个命令模式 - 即根据所选的不同元素为您工作的东西。它也类似于状态模式,尽管这里的重点是执行而不是数据或数据的状态。

    希望这会有所帮助。

    【讨论】:

    • 这正是我的建议,只需让操作返回 MyInterface,这样您就可以在调用函数时更改状态。 MyInterface operation1 (property) { // 做一些事情,不要改变状态 return this; } and: MyInterface operation2 (property) { //做一些事情并改变状态 return A(); } 然后你可以将它用作: currstate = currstate.operation1() 你就完成了
    • 谢谢,我会尝试将其合并到问题逻辑中。我承认我不能马上看到这有什么帮助,因为操作方法是业务逻辑对象行为的一部分。我仍然需要从该对象委托给枚举结构,对吗? - 如果我的问题有点令人困惑,我们深表歉意。我需要对你的建议进行试验,得出一些结论,然后提出一个更准确的问题。
    • @rmn 我知道这是你的建议,这就是我支持你的原因。我只是认为他可能需要一个更好的例子,并想给出它,这些评论框实际上不允许这样做。
    • @rmn:抱歉在看到你的评论之前写了我的评论。这更有意义。感谢您的详细说明!会继续尝试一下
    • @denchr 是的,它确实分离了一些逻辑,但这是命令模式的重点。您正在根据不同的需要换入和换出特定对象的行为。在某种程度上,工作必须完成,工作代码需要存在于某个地方。您还可以通过其中包含逻辑的枚举委托给一个实例对象 - 即,您可以将该对象从枚举中取出,如果这样会更容易的话。
    【解决方案3】:

    您可以在一个接口中包含所有三个方法,并对该接口有不同的实现(每个枚举一个),并让这个业务类委托调用..

    同样为了改变状态,让操作的返回值是那个通用接口,以便在调用这些方法时,调用类可以为下一个状态返回适当的对象。

    【讨论】:

    • 感谢您的回复。是的,但是那些单独的实现不会仍然带有 switch/case 语句吗?我会清理业务逻辑和客户端类,但是实现这 3 个接口中的每一个的新类仍然会在其中包含 switch 案例。我的推理是否正确,或者您可能不介意进一步详细说明
    • 您可以通过枚举本身访问不同的类-即枚举实现接口或类似的东西,然后执行操作,因此您根本不必打开它.这类似于命令模式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-04
    • 1970-01-01
    • 1970-01-01
    • 2012-04-30
    • 2015-09-23
    • 1970-01-01
    相关资源
    最近更新 更多