如何保证方法调用的一致性?

vkc1a9a2  于 2021-07-07  发布在  Java
关注(0)|答案(1)|浏览(368)

我在玩一个没有例外的编程的想法,使用沃德·坎宁安的特殊价值和无意义的行为模式的组合。
假设我有一个接口分数,用来表示任何数量,可以用一组运算除以2个其他数量来表示:

interface Fraction {

    Fraction add(Fraction f);
    Fraction subtract(Fraction f);
    Fraction multiply(Fraction f);
    Fraction divide(Fraction f);

}

现在,这个接口将有两个主要实现:a)simplefraction,其中分母不是0;b)未分母,分母为0。两者都将由工厂创建:

public final class SimpleFraction implements Fraction {

    private final int numerator, denominator;

    private SimpleFraction(int numerator, int denominator) {

        this.numerator = numerator;
        this.denominator = denominator;

    }

    // interface method implementations

}

public final class UndefinedFraction implements Fraction {

    private final int numerator;

    private UndefinedFraction(int numerator) {

        this.numerator = numerator;

    }

    // interface method implementations

}

undefinedfraction将是一个具有无意义行为的异常值:它的每个操作都应返回一个undefinedfraction。更重要的是,如果它被用作任何其他分数运算的参数,结果也应该是一个未定义的分数:

FractionFactory factory = new FractionFactory();
Fraction simple = factory.fromNumeratorAndDenominator(420, 10);
Fraction undefined = factory.fromNumeratorAndDenominator(42, 0);

assertEquals(simple.add(undefined), undefined);
assertEquals(undefined.add(simple), undefined);
assertEquals(undefined.add(undefined), undefined);

我怎样才能在涉及未定义分形的运算中强制执行这种变换?我正在寻找基于多态性的选项(例如,它们不应该依赖于布尔检查)。

k2fxgqgv

k2fxgqgv1#

问题是关联性。
术语:给定“a/b”,我们称a为lhs(左侧),b为rhs,“/”为运算符,而“divide”为运算。LH和RH都是“操作数”。
问题是:假设我们创建了两种未定义的味道。我们有“red undefined”和“green undefined”,并且我们坚持相同的定义:任何lhs和/或rhs为“red undefined”的操作都应该产生“red undefined”。
同样的规则也适用于“green undefined”:任何lhs和/或rhs为“green undefined”的操作都应为“green undefined”,而不考虑其他操作数的值和操作。
不幸的是,这一定义模棱两可:

Fraction red = factory.newRedUndefined();
Fraction green = factory.newGreenUndefined();
Fraction whatColorIsThis = red.add(green);

是红色的,还是绿色的?谁有优先权?
这个问题应该强调,你的问题没有简单/明显的答案!
回到“a/b”——您所做的(在面向对象语言中这是很难避免的)是定义操作数上可用的操作。它是一个有除法的a。并不是说“divide”这个概念是一个对象,它有一个接受2个参数的方法。换言之,你有:

a.divide(b);

而不是这个:

fractionMathSystem.divide(a, b);

基本设计 a.divide(b) work固有地将“left”关联起来(比如,“a”决定了divide的实现,b正好代表了right),而不将“right”关联起来,因为java就是这样定义的。
问题是,这里不需要左关联性:如果“b”未定义而“a”未定义,则需要“b”重写并控制操作。
但是你不能这样做,因为前面提到的红/绿未定义的问题,没有一个简单的语言建议可以做到这一点。
根本的问题是您已经定义了一个操作(分数除法),它应该根据操作本身而不是任何一个操作数来定义。因此, fractionNumberSystem.divide(a, b) 更有意义;您可以在那里编写“如果任一操作数未定义,则结果未定义”的代码,甚至可以引入一个系统,询问每个操作数是否要负责该操作,并为每个操作数提供一个优先级层次结构,数字系统将选择报告其具有最高优先级的操作数,让它做手术。
幸运的是,你可以 a.divide(b) 归结起来:

public class Fraction {
    public void divide(Fraction rhs) {
        return numberSystem.divide(this, rhs);
    }
}

现在numbersystem.divide可以做任何你想做的事情。“除法运算首先与未定义相关联,无论未定义的哪一边在哪一边”的概念可以在numbersystem的除法方法中编程。或者,你可以让它更“可插拔”,找到解决红/绿问题的方法(当双方都想控制操作时,你如何决定哪一方“获胜”?是红色的。添加(绿色)红色还是绿色?),不管你怎么想。也许通过检查所涉及的分数是否是“operationcontrollingfraction”(您将创建的子类型)的示例,并使operationcontrollingfraction类型 radd 方法(右关联add)以及 int priority() 方法。
注意:提示: add 是个坏名声。 plus 效果更好。 a.add(b) 这意味着 a . a.plus(b) 表示a保持不变,并返回一个新值。对, BigInteger 朋友的名字都不好。它发生了;那些api都很老了,我不认为为了保持一致而延续明显的设计错误是个好主意,因为设计错误已经足够严重了。

相关问题