<<>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.