面向对象编程 - 避免 Switch/Case 和 If/else (JAVA)

Object Oriented Programming - Avoid Switch/Case and If/else (JAVA)

我遇到一个问题,不允许我使用 switch/case 或 if/else 查询。

我读到了一个配置文件,它是这样的:

650;0;1.5;month
614;0;2.88;year
466;0;2.48;week
716;0;4.6;half-year
718;0;2.6;quarter

我在“;”处分割那些字符串,所以它被保存在一个数组中。我遇到的问题是,我需要在代码中为数组 ar[3] 中给出的每个时间做其他事情,所以如果是一个月,那么当它是一整年时,我需要其他计算。

但是我不能用 Switch/case 或 If/Else 这样做,现在我很困惑。

If (ar[3] = month){
do this;
else if (ar[3] = year) {
do this; 
}

我如何做这个面向对象的?感谢您的帮助:)

继承多态性是你的朋友

看来您需要某种基于 ar[3] 中时间段的继承结构。可以为每种情况编码特殊的 do this 方法。这样你就可以针对每种情况做不同的事情。您只需要一种方法来首先实例化正确的子类型。有多种方法可以解决这个问题。

条件运算符

恕我直言,最直接的方法是条件运算符,?:

所以代码看起来像这样:

MyClass x = ar[3].equals("month") ? new MyClassMonth() :
            (ar[3].equals("year") ? new MyClassYear() :
            (ar[3].equals("week") ? new MyClassWeek() :
            (ar[3].equals("half-year") ? new MyClassHalfyear() :
                                        new MyClassQuarter())));
x.doSomething();

嵌套的条件表达式让你能够select正确的class,继承给你你想要的多态行为。

但是你在评论中提到你也不能使用?:。下一步是什么?

无状态对象图

假设您以一种不依赖任何记忆状态的方式编写 MyClassMonth,即 doSomething() 方法没有副作用。然后你可以创建一个 Map<String, MyClass> 来存储每个子 class 的一个实例,然后在你需要调用时从映射中拉出相关的实例。

您可以像这样初始化地图:

final Map<String, MyClass> themap = new HashMap<>();
{
  themap.add("month", new MyClassMonth());
  themap.add("year", new MyClassYear());
  themap.add("week", new MyClassWeek());
  themap.add("half-year", new MyClassHalfyear());
  themap.add("quarter", new MyClassQuarter());
}

并以 ar 作为参数调用 doSomething()

MyClass x = themap.get(ar[3]);
if (x != null)
  x.doSomething(ar);

其他选项

还有其他方法可以做到这一点。坚持 Map 概念,您可以将 class 文字而不是实例存储在 Map 中,然后以反射方式实例化它们。您还可以在 Map 中保留一个 lambda 并调用它。

枚举

@OldCurmudgeon 建议使用枚举。如果将这些枚举放入 Map 并向枚举添加 lambda,则可以获取枚举并调用 lambda。那会起作用并且具有一定的吸引力,但似乎没有必要。你最好直接调用 lambda。

像这样:

public interface Calculator {
    double calculate(int p1, int p2, double p3);
}

public class YearCalculator implements Calculator {
    public double calculate(int p1, int p2, double p3) {
        double value = 0.0;
        // do year calculations
        return value;
    }
}

public class CalculatorFactory {
    public Calculator getInstance(String type) {
       Calculator calculator = null;
       if (type != null) {
       } else {
           throw new IllegalArgumentException("calculator type cannot be null");
           if ("year".equalsIgnoreCase(type)) {
           } else {
               System.out.println(String.format("No such type: %s", type));
           }
       }
       return calculator;
    }
}

工厂中必须有 if/else 逻辑,但在解析文本时不需要。

您的处理代码:

CalculatorFactory factory = new CalculatorFactory();
// contents is a List of Strings from your input file.
for (String line : contents) {
    String [] tokens = line.split(";");
    Calculator calculator = factory.getInstance(tokens[3]);
    double value = calculator.calculate(Integer.parseInt(tokens[0]), Integer.parseInt(tokens[1]), Double.parseDouble(tokens[2]));
}

根据 Codebender 给出的建议作为替代解决方案:

您需要 5 个 class,每种情况一个,具有通用接口但不同实现。

您的界面可能如下所示:

public interface MyCalculator {

    public double calculate(double a, double b, double c);

}

然后您将需要实现与此类似的 5 个 classes。对于 monthyearweekhalf-yearquarter,您将需要不同的 class 和 calculate 的不同实现:

public class MyMonthCalculator implements MyCalculator {

    @Override
    public double calculate(double a, double b, double c) {
        // Do your calculations here then return
    }

}

然后,在您的解析逻辑之前,您可以将五个 class 添加到 Map

map.put("month", new MyMonthCalculator());
// Repeat for year, week, half-year and quarter

实际执行计算:

double result = map.get(ar[3]).calculate(Double.parseDouble(ar[0]), Double.parseDouble(ar[1]), Double.parseDouble(ar[2]));

您可以使用 enum 作为命令工厂模式并通过 Map 查找实现选择。

// Lookups for teh period.
static final Map<String, Period> lookup = new HashMap<>();

enum Period {

    Month("month") {

                @Override
                void process(int x, int y, double v) {
                    // Processing for "month" records here.
                    System.out.println(this + "-process(" + x + "," + y + "," + v + ")");
                }
            },
    Year("year") {
                @Override
                void process(int x, int y, double v) {
                    // Processing for "year" records here.
                    System.out.println(this + "-process(" + x + "," + y + "," + v + ")");
                }
            },
    Quarter("quarter") {
                @Override
                void process(int x, int y, double v) {
                    // Processing for "quarter" records here.
                    System.out.println(this + "-process(" + x + "," + y + "," + v + ")");
                }
            },
    HalfYear("half-year") {
                @Override
                void process(int x, int y, double v) {
                    // Processing for "half-year" records here.
                    System.out.println(this + "-process(" + x + "," + y + "," + v + ")");
                }
            };

    Period(String inData) {
        // Record me in the map.
        lookup.put(inData, this);
    }

    abstract void process(int x, int y, double v);

    static void process(String data) {
        String[] parts = data.split(";");
        Period p = lookup.get(parts[3]);
        if (p != null) {
            p.process(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), Double.parseDouble(parts[2]));
        }
    }
}

public void test() {
    String[] test = {"650;0;1.5;month",
        "614;0;2.88;year",
        "466;0;2.48;week",
        "716;0;4.6;half-year",
        "718;0;2.6;quarter",};
    for (String s : test) {
        Period.process(s);
    }
}

正确打印:

Month-process(650,0,1.5)
Year-process(614,0,2.88)
HalfYear-process(716,0,4.6)
Quarter-process(718,0,2.6)

请注意,其中有一个 if,但这只是为了避免错误数据的防御 - 它不是查找机制的一部分。

您可以使用选项数组模拟 ifcase。这里唯一的问题是在这样的数组中找到我们元素的索引。我们不能使用 ifcase 但我认为 while 是一个选项。

因此您的代码可以类似于:

String[] options = { "foo", "bar", "baz" };
Runnable[] action = { new Runnable() {
    @Override
    public void run() {
        System.out.println("handling foo");
    }
}, new Runnable() {
    @Override
    public void run() {
        System.out.println("handling bar");
    }
}, new Runnable() {
    @Override
    public void run() {
        System.out.println("handling baz");
    }
} };
String choice = "bar";
int matched = 0;
int i = -1;
while (matched != 1) {
    i++;
    matched = boolToInt(options[i].equals(choice));
}
action[i].run();

我使用这样的方法将 boolean 转换为 integer,其中 1=true, 0=false

public static int boolToInt(Boolean b) {
    return 5 - b.toString().length();
}

您可以提供自己的界面而不是 Runnable。