English 中文(简体)
• 如何避免els/itch锁,在计算器方案中保留开放/封闭的原则(java)
原标题:How to avoid if-else/switch chains and preserve open/closed principle in Calculator program (java)

<<>strong>TL;DR

在处理计算器功能时,避免使用/发表最佳惯例(例如,目前是增加/减少,在不违反开放/封闭式电子数据交换原则的情况下增加模块功能的余地)。

<<>Background

我正在为Udemy课程分配制定计算器方案。 该任务要求我们修改现有的<代码>计算或类别,以减少假装/itch状的数量。 最初的项目是打字式的,但我已将其转换到 Java,看看我是否能够恢复功能。 下面是<条码>。 采用级计算器功能并打印结果。

public class App {
    public static void main(String[] args) throws Exception {

        Calculator calculator = new Calculator(0);

        calculator
        .execute(OperationType.add, 10)
        .execute(OperationType.add, 20)
        .execute(OperationType.subtract, 15)
        .execute(OperationType.multiply, 3)
        .execute(OperationType.divide, 2)
        .execute(OperationType.multiply, 1000);

        PrintHandler printer = new PrintHandler(calculator);
        printer.printResults();
    }

}

Output:

0.0 added to 10.0
10.0 added to 20.0
30.0 subtracted by 15.0
15.0 multiplied by 3.0
45.0 divided by 2.0
22.5 multiplied by 1000.0
-----------
Total: 22500.0

<<><><><><>>>>><><><>>>>>

虽然该代码有效,但计算器execute 这种方法仍然有大量的开关说明(下文编码),即通往不同类别即时状态的通道。 我试图通过将实际计算(再计算/再计算)推向单独的班级来简化<代码>execute方法,但我基本上只是将这一逻辑围绕而不是绕过需要开关说明。 由于这一模式违反了公开封闭的坚实原则,我不禁要问,我如何能够执行一个遵循坚固原则的解决办法,并且不要求在增加新的业务类型(例如Modulo)时重新开放守则。

public Calculator execute(OperationType operationType, float newValue) throws Exception {

    Operation newOperation;
    // Operation is the interface. SpecifiedOperations is the superclass holding the 
    // nested classes for each operation class, which house the logic for the "calculate" 
    // method. I could also see putting each operation class in its own class

    switch (operationType) {
        case add:
            newOperation = new SpecifiedOperations.AdditionOperation(currentValue, newValue);
            break;
        case subtract:
            newOperation = new SpecifiedOperations.SubtractionOperation(currentValue, newValue);
            break;
        case multiply:
            newOperation = new SpecifiedOperations.MultiplicationOperation(currentValue, newValue);
            break;
        case divide:
            newOperation = new SpecifiedOperations.DivisionOperation(currentValue, newValue);
            break;
        default:
            throw new Exception("Invalid operation type");
    }

    currentValue = newOperation.calculate();
    operations.add(newOperation);

    return this;
}

What I ve Tried

In typescript I was able to somewhat achieve my goal via the code below. By mapping the operation type to the type of class, rather than an instance of it, I m at least removing the need to open any methods and can just modify the map if new operation types come into the equation.

const operationGenerationMapper = new Map([
  [OperationType.addition, AdditionOperation],
  [OperationType.subtraction, SubtractionOperation],
  [OperationType.multiplication, MultiplicationOperation],
  [OperationType.division, DivisionOperation],
]);

export function generateOperation(
  operationType: OperationType,
  currentValue: number,
  newValue: number
): any {
  let newOperation: Operation;

  for (let [key, value] of operationGenerationMapper) {
    if (operationType == key) {
      return new value(currentValue, newValue);
    }
  }

  return null;
}

I found an interesting post titled Java way to create an object based on enum type that seems to be attempting to get at the same concept, but I m not experienced enough with Java to modify the code they discussed/provided to my situation. I also wonder if using generics like this is considered "best practice" or just a way to answer the question provided.

I ve also tried looking into factory patterns (https://www.youtube.com/watch?v=EdFq_JIThqM, https://www.youtube.com/watch?v=QNpwWkdFvgQ), but I haven t quite understood how (or if) those solutions would apply to my scenario.

问题回答

Thanks to trashgod & tgdavies for nudging me in the right direction. I finally wrapped my head around some of the extended capacities that enums are capable of and resolve my question.

This youtube video was helpful for me in understanding how to use fields can be used in enums. A little more research on the original post I referenced as well as what trashgod linked to helped me understand how to create abstract enum methods and how to overwrite them on enum values.

对于那些有好奇的人,我现将最后守则张贴如下。 (土耳其人甚至需要具体的操作班级,因为我可以处理所有问题)

<<>计算>

public class Calculator {

    private ArrayList<Operation> operations = new ArrayList<Operation>();
    private float currentValue;

    public ArrayList<Operation> getOperations(){
        return operations;
    }

    public float getCurrentValue(){
        return currentValue;
    }

    public Calculator(float initialValue) {
        currentValue = initialValue;
    }

    // Because of the enum functionality, the switch statement
    // can be completely removed, simplifying the code as such.
    public Calculator execute(OperationType operationType, float newValue) {
 
        Operation newOperation = new Operation(this.currentValue, operationType, newValue);
        this.currentValue = newOperation.calculate();
        operations.add(newOperation);

        return this;
    }

}

www.un.org/Depts/DGACM/index_spanish.htm Enums Level

public enum OperationType {
    add("added to") {
        public float calculate(float value1, float value2){
            return value1 + value2;
        }
    },
    subtract("subtracted from") {
        public float calculate(float value1, float value2){
            return value1 - value2;
        }
    },
    multiply("multiplied by") {
        public float calculate(float value1, float value2){
            return value1 * value2;
        }
    },
    divide("divided by") {
        public float calculate(float value1, float value2){
            return value1 / value2;
        }
    };

    // This is used for printing purposes down the line.
    // These are populated in the parenthesis next to each enum value
    public final String joinerText;

    // This constructor is necessary to make the above string field work
    OperationType(String joinerText){
      this.joinerText = joinerText;
    }

    // This declares the method for the enum, which each enum value 
    // sets to its own version
    public abstract float calculate(float value1, float value2);

}

http://www.un.org。

public class Operation {
    private float value1;
    private float value2;
    private OperationType operationType;

    public Operation(float value1, OperationType operationType, float value2){
        this.value1 = value1;
        this.value2 = value2;
        this.operationType = operationType;
    }

    public float calculate(){
        return operationType.calculate(this.value1, value2);
    }

    public void printInfo(){
        System.out.println(value1 + " " + operationType.joinerText + " " + value2);
    }
}

印刷结果 我使用了单独的打印机方法。

public class PrintHandler {
    
    private Calculator calculator;

    public PrintHandler(Calculator calculator){
        this.calculator = calculator;
    }

    public void printResults(){

        ArrayList<Operation> operations = calculator.getOperations();

        for(Operation operation : operations){
            operation.printInfo();
        }

        System.out.println("-----------");
        System.out.println("Total: "+calculator.getCurrentValue());
    }
}

最终执行就是这样:

Calculator calculator = new Calculator(0);

calculator
.execute(OperationType.add, 10)
.execute(OperationType.add, 20)
.execute(OperationType.subtract, 15)
.execute(OperationType.multiply, 3)
.execute(OperationType.divide, 2)
.execute(OperationType.multiply, 1000);

PrintHandler printer = new PrintHandler(calculator);
printer.printResults();

这只是我所偏好,但你可以界定一个具有操作方法的功能接口,而不管你想要增加一个新业务类型,你只是把这一接口作为功能来执行。





相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签