【问题标题】:Java Object Fields Value Depend on Other Fields - Best Practice Patterns?Java 对象字段值取决于其他字段 - 最佳实践模式?
【发布时间】:2021-04-04 21:37:00
【问题描述】:

我有一个简单的 java 对象

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

我只存储数量和价格,totalPrice 字段将根据数量 * 价格生成。

现在我在 Order 类中有一个名为 populateTotalPrice() 的方法。但似乎我在一个可能是反模式的实体类中放置了一个逻辑?

我的另一个选择是使用辅助静态方法。

这种行为的最佳实践是什么?

【问题讨论】:

    标签: java oop


    【解决方案1】:

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

    在创建发票的过程中计算总价。

    【讨论】:

      【解决方案2】:

      Java 实践是根据属性而不是字段来处理对象。大多数属性都基于支持字段,但在您的情况下,属性totalPrice 是一个计算属性是有意义的:

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

      【讨论】:

      • 我需要存储总价,以便根据totalPrice查询订单
      • @user1955934 “存储”和“查询”是什么意思?
      【解决方案3】:

      在您的实体中有一个totalPrice 字段,该字段的值是其他字段值的函数,这与数据库的非规范化概念密切相关。 (根据您对其他答案所做的一些 cmet,我推测您正在将此实体与您想要查询的后备数据库表一起使用。)特别是,它违反了 3NF:列值应仅取决于键列值.尽管存储计算值违反了 3NF,但有时这样做有很好的性能原因。与数据库一样,有多种方法可以处理此类需求。

      您或许可以将问题移至数据库并从您的 Java 对象中删除 totalPrice 字段。根据您使用的 DBMS,一种可能性是定义您的数据库模式以具有计算列。 (计算列不是标准 SQL,但有几个系统——例如 Microsoft 的 SQL Server——支持它们。)另一种可能性是定义一个数据库视图(SELECT 语句,它存储在数据库中并像查询它一样被查询是另一个表)。视图可能类似于:

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

      当需要使用totalPrice(或total_price)时,您的代码可以查询orders_view 而不是orders。

      最后,您可以在执行过程中对实体进行非规范化并接受后果,主要是更新异常:您更新了price 和/或quantity,但未能更新totalPrice 字段。您可以通过在 quantity 和 price 的设置器中更新 totalPrice 来降低这种风险(而不是使用单独的 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 字段。)然后您根本不需要将字段存储在表中。

      关于这个问题的一些进一步阅读:

      【讨论】:

        猜你喜欢
        • 2016-06-14
        • 1970-01-01
        • 2016-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-04
        • 2021-07-08
        相关资源
        最近更新 更多