锁定老帖子 主题:什么时候使用assumption?
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-02-16
1。percent。就是百分比。 2。weight。如果percent是10,weight就是0.1,weight=percent/100. 3。amount。如果percent是10,总数是1000,那么amount就是100. amount=total*weight. 在程序中,我们很多时候需要在amount, weight, percent之间来回转换。根据不同模块的需要,把同一个数据点转换成percent或者weight或者amount。 目标是设计一个类Portion,它的接口如下: public class Portion { public double getPercent(); public double getWeight(); public double getAmount(); public double getTotal(); public static Portion fromPercent(double total, double percent); public static Portion fromWeight(double total, double weight); public static Portion fromAmount(double total, double amount); } 另外,程序中还使用这样一个习惯:如果total为0,那么从amount计算percent和weight的时候也是0。(以避免divide by 0) 我的pair想这样实现: public class Portion { private final double total; private final double amount; Portion(double total, double amount) { this.total = total; this.amount = amount; } public double getPercent() { return getWeight()*100; } public double getWeight() { return total==0?0:amount/total; } public double getAmount() { return amount; } public double getTotal() { return total; } public static Portion fromPercent(double total, double percent) { return new Portion(total, total*percent/100); } public static Portion fromWeight(double total, double weight) { return new Portion(total, total*weight); } public static Portion fromAmount(double total, double amount) { return new Portion(total, amount); } } 我对这个设计是不满意的。我提出了几点问题: 1。假设在某一点total不可知,那么怎样用Portion来封装唯一可知的percent/weight?Portion.fromWeight(0, 0.1).getPercent()在这个设计中不会返回我期待的10,而是0。 2。double是有误差的。如果客户写一个 assertEquals(weight, Portion.fromWeight(777, weight).getWeight(), 0) 居然也要失败,不免尴尬。 所以我提议这样做: public class Portion { private final double total; private final double amount; private final double percent; private final double weight; Portion(double total, double amount, double percent, double weight) { this.total = total; this.amount = amount; this.percent = percent; this.weight = weight; } public double getPercent() { return percent; } public double getWeight() { return weight; } public double getAmount() { return amount; } public double getTotal() { return total; } public static Portion fromPercent(double total, double percent) { return new Portion(total, total*percent/100, percent, percent/100); } public static Portion fromWeight(double total, double weight) { return new Portion(total, total*weight, weight*100, weight); } public static Portion fromAmount(double total, double amount) { double weight = total==0?0:amount/total; return new Portion(total, amount, weight*100, weight); } } 这样,虽然double计算有误差,我至少可以保证Portion.fromWeight(total, weight).getWeight()永远等于weight。 但是,pair对这种设计感觉不舒服,他不喜欢把逻辑放在静态工厂方法中。 而且,pair对我提出的几个他的方案的缺陷如此回应: 1。total为0的情况本身就特殊。根据整个系统目前的情况,getWeight()和getPercent()都返回0可以接受。 2。我不care是否有误差,反正我们现在比较double的时候都是用一个tolerance的。 我的pair对整个系统了解的比我多,我相信他说的话。只不过,要我做一个单独的小模块,却总要依赖于外部系统的“对特殊情况A,我们可以容忍;对情况B,我们不在乎”这些assumption,让我深藏于心底的洁癖非常不舒服。 我并不是绝对排斥使用适当的assumption来简化实现复杂度。但是当两个方案的复杂度相近时,我很反感把外界的一些assumption引进来。 那么,你怎么想? 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-02-17
大家都忙着过年,我来给搂主唱个和。
四个量中,只有两个是自变量,其余两个是导出量。凭这一点,我否定楼主的的做法,因为太多的冗余。剩下的问题是选那两个作基本变量。直觉让我选Total和Percent。这个直觉有没有道理呢? 粗看之下,四个量是相关的,任选其中两个的结果应该是等价的。但细细考究的话还是有一些差别的。实数域对于四则运算是封闭的,只有一点例外,那就是0。0是除法(函数)的奇点。所以我们要尽量避免除法而用乘法。结论就是应该选用Total和Weidht作基本量。这样可以保证 assertEquals(weight, Portion.fromWeight(0, weight).getWeight()) assertEquals(weight, Portion.fromWeight(777, weight).getWeight()) assertEquals(percent, Portion.fromPercent(0, percent).getPercent()) assertEquals(percent, Portion.fromPercent(777, percent).getPercent()) 唯一需要做假定的是fromAmount(double total, double amount),如果这个假定还是不能接受,可以让它抛出异常。 至于运算精度,那是另一回事了。既然说的是钱,你要么用assertEquals(A, x, 0.0001)来判是否相等,要么用long或者BigDecimal来实现Portion。 |
|
返回顶楼 | |
发表时间:2007-02-17
pojo 写道 大家都忙着过年,我来给搂主唱个和。
四个量中,只有两个是自变量,其余两个是导出量。凭这一点,我否定楼主的的做法,因为太多的冗余。剩下的问题是选那两个作基本变量。直觉让我选Total和Percent。这个直觉有没有道理呢? 粗看之下,四个量是相关的,任选其中两个的结果应该是等价的。但细细考究的话还是有一些差别的。实数域对于四则运算是封闭的,只有一点例外,那就是0。0是除法(函数)的奇点。所以我们要尽量避免除法而用乘法。结论就是应该选用Total和Weidht作基本量。这样可以保证 assertEquals(weight, Portion.fromWeight(0, weight).getWeight()) assertEquals(weight, Portion.fromWeight(777, weight).getWeight()) assertEquals(percent, Portion.fromPercent(0, percent).getPercent()) assertEquals(percent, Portion.fromPercent(777, percent).getPercent()) 唯一需要做假定的是fromAmount(double total, double amount),如果这个假定还是不能接受,可以让它抛出异常。 至于运算精度,那是另一回事了。既然说的是钱,你要么用assertEquals(A, x, 0.0001)来判是否相等,要么用long或者BigDecimal来实现Portion。 不管冗余与否。那都是内部的实现细节。 问题是你这个方法有两个问题: fromAmount(1, 0).getAmount() != 1 fromAmount(1, 777).getAmount() != 1 强迫客户代码使用精度?觉得这个api比较不自然。fromAmount(1, x).getAmount()不能原样保持原来的amount? 我认为对客户保持一个最友好的接口,least surprise,比内部是否冗余重要的多。毕竟内部的冗余也都是封装好了的。你就当它是四个计算结果的数好了。 而且,用四个数据点还有容易测试的好处。test case还可以这么写: assertEquals(new Portion(0, 1, 0, 0), Portion.fromAmount(0, 1)); |
|
返回顶楼 | |
发表时间:2007-02-17
为Portion定义三个够找函数,是否更好点。 在够找函数里面去计算total;amount;percent;weight;的值
|
|
返回顶楼 | |
发表时间:2007-02-17
fromAmount的签名是
Portion fromAmount(double total, double amount) fromAmount(1, 0).getAmount()的返回值是0,这应该是对的啊,你的输入是0,输出当然也是0。 在fromAmount(1, 777).getAmount()的例子,输入的值就是不合理的,不管在哪一种实现中都会出错的,这是具体实现的问题。 运算精度的问题,不是强迫客户代码使用精度,而是说对于金额而言,精度超过3位(或4位)是没有意义的,它们在实用上是相等的。运算的最后结果最终要进位到小数点后2位。 |
|
返回顶楼 | |
发表时间:2007-02-17
dengyin2000 写道 为Portion定义三个够找函数,是否更好点。 在够找函数里面去计算total;amount;percent;weight;的值
打自己的嘴巴。签名都是一样的 |
|
返回顶楼 | |
发表时间:2007-02-18
pojo 写道 fromAmount的签名是
Portion fromAmount(double total, double amount) fromAmount(1, 0).getAmount()的返回值是0,这应该是对的啊,你的输入是0,输出当然也是0。 在fromAmount(1, 777).getAmount()的例子,输入的值就是不合理的,不管在哪一种实现中都会出错的,这是具体实现的问题。 运算精度的问题,不是强迫客户代码使用精度,而是说对于金额而言,精度超过3位(或4位)是没有意义的,它们在实用上是相等的。运算的最后结果最终要进位到小数点后2位。 1. fromAmount(0, 1).getAmount() != 1 2. fromAmount(777, 1).getAmount() != 1 想想Double,不管double运算是否有误差, double val = 3.14 new Double(val)==val绝对是保证的。 我希望Portion达到同样的效果。 |
|
返回顶楼 | |
发表时间:2007-02-18
我的fromAmount(double total, double amount)和你的一样是有判断的:
public static Portion fromAmount(double total, double amount) { if (total== 0) return new Portion(0, 0); return new Portion(total, weight/total); } 所以fromAmount(777, 1).getAmount() == 1 至于fromAmount(0, 1),我认为最好的办法是抛出异常,因为这是一个不合理的输入,如果让它进入系统,会引起麻烦的。 |
|
返回顶楼 | |
发表时间:2007-02-18
如果需要精确的计算一致性本来就不能用 double, 既然用了double就不应该期望任意运算结果都落在一定的误差范围内.
我觉得: 如果需要很高精度, 就必须把double换成BigDecimal. 如果需要绝对精度, 就要用像IBM的无误差运算库之类的专门解决方案. double精度造成的suprise不是应用程序的问题, 而是 IEEE FP 规范的问题. 如果说项目需求要求在应用程序里实现基于double的精度保障, 那楼主的方案很好, 但是如果没有这方面的明确需求, 就没有必要了. |
|
返回顶楼 | |
发表时间:2007-02-18
complystill 写道 如果需要精确的计算一致性本来就不能用 double, 既然用了double就不应该期望任意运算结果都落在一定的误差范围内.
我觉得: 如果需要很高精度, 就必须把double换成BigDecimal. 如果需要绝对精度, 就要用像IBM的无误差运算库之类的专门解决方案. double精度造成的suprise不是应用程序的问题, 而是 IEEE FP 规范的问题. 如果说项目需求要求在应用程序里实现基于double的精度保障, 那楼主的方案很好, 但是如果没有这方面的明确需求, 就没有必要了. double是系统范围内采用的。但是我并不认为采用了double,就可以容忍val != val,或者new Double(val).doubleValue()!=val的情况出现。 ieee标准并没有造成new Double(val).doubleValue()!=val。同样,我也希望fromAmount(total, val).getAmount==val。觉得这个要求不过分。 |
|
返回顶楼 | |