java对象字段值取决于其他字段-最佳实践模式?

moiiocjp  于 2021-06-29  发布在  Java
关注(0)|答案(4)|浏览(300)

我有一个简单的java对象

public class Order {
  private int quantity;
  private long price;
  private long totalPrice;
 }

我只存储数量和价格,根据数量*价格生成totalprice字段。
现在我在order类中有了一个名为populatetotalprice()的方法。但我似乎把一个逻辑放在一个实体类中,这个实体类可能是反模式的?
我的另一个选择是使用helper静态方法。
这种行为的最佳实践是什么?

rfbsl7qr

rfbsl7qr1#

有一个 totalPrice 实体中的字段,其中值是其他字段值的函数,与数据库的非规范化思想密切相关(从您对其他答案所做的一些评论来看,我推测您使用的是这个实体以及您要查询的备份数据库表。)特别是,它违反了3nf:列值应仅依赖于键列值。尽管存储计算值违反了3nf,但有时这样做有很好的性能原因。与数据库一样,处理这类需求有多种方法。
您也许可以将问题转移到数据库并消除 totalPrice java对象中的字段。根据所使用的dbms,一种可能性是将数据库模式定义为具有计算列(计算列不是标准的sql,但有几个系统(如microsoft的sql server)支持它们 SELECT 存储在数据库中并像查询另一个表一样进行查询的语句)。视图可能类似于:

CREATE OR REPLACE VIEW orders_view AS
SELECT quantity, price, quantity * price AS total_price
FROM orders

然后您的代码就可以查询 orders_view 而不是 orders 当需要使用时 totalPrice (或 total_price ).
最后,您可以在执行过程中对实体进行反规范化,并接受结果,主要结果是更新异常:您更新了 price 和/或 quantity 但未能更新 totalPrice 现场。您可以通过更新 totalPrice 在setters for quantity 以及 price (而不是有一个单独的 populateTotalPrice() 方法):

public class Order {
    private int quantity;
    private long price;
    private long totalPrice;

    public int getQuantity() { return quantity; }

    public void setQuantity(int newQuantity) {
        quantity = newQuantity;
        updateTotalPrice();
    }

    public long getPrice() { return price; }

    public void setPrice(long newPrice) {
        price = newPrice;
        updateTotalPrice();
    }

    private void updateTotalPrice() {
        totalPrice = quantity * price;
    }

    public long getTotalPrice() { return totalPrice; }
}

请注意,没有为 totalPrice . 以这种方式安排代码可以确保您不会因为忽略对的调用而遇到问题 populateTotalPrice() . (当然,如果您可以在不经过java对象的情况下更改数据库值,那么最终仍然会出现更新异常。)
在某些框架中,每次从表中加载实体时都可以进行这些计算(例如,hibernate postLoad 使用android room,您可以使用 LiveData 字段)那么您根本不需要在表中存储字段。
关于这个问题的进一步解读:
第三范式与计算值
计算列是否打破3nf(第三范式)?
数据库规范化-谁说得对?

x6492ojm

x6492ojm2#

永远不要使用静态方法!!静态方法是邪恶的!!
以下是一些选项:
使用calculateprice(order…)方法创建orderservice。您可以使用ioc容器和控制反转在需要的地方注入此服务(如spring、guice等)
每次调用order.gettotalprice()时计算总价,而不是存储/设置totalprice。
在order(…)构造函数中传递数量和价格,并在构造函数中计算totalprice。在这种情况下,order类中的所有字段都是final。而你只有能手(没有二传手)。然后类将是不可变的

llmtgqce

llmtgqce3#

是的,您不必在订单中输入总价来创建发票。
在创建发票的过程中计算总价。

f1tvaqid

f1tvaqid4#

java实践是用属性而不是字段来处理对象。大多数属性都基于backing字段,但在您的情况下,它对属性是有意义的 totalPrice 作为计算属性:

public long getTotalPrice() {
  return price * quantity;
}

相关问题