【问题标题】:Difference between Value Object pattern and Data Transfer pattern值对象模式和数据传输模式之间的区别
【发布时间】:2011-10-22 14:19:31
【问题描述】:

在哪些情况下可以在 n 层架构中使用这些设计模式?

【问题讨论】:

    标签: java design-patterns value-objects data-transfer-objects


    【解决方案1】:

    这里有几个很好的答案,但我会添加一个以捕捉关键区别:

    值对象没有标识。也就是说,包含相同值的值对象的两个实例之间的任何比较都应该表明它们是相等的。数据传输对象虽然仅用于保存值,但确实具有标识。比较具有相同值但独立创建的 DTO 的两个实例并不表示它们相等。

    例子:

    DTO dto1 = new DTO(10);
    DTO dto2 = new DTO(10);
    dto1.equals(dto2); //False (if equals is not overridden)
    dto1 == dto2; //False
    
    VO vo1 = VO.getInstance(10);
    VO vo2 = VO.getInstance(10);
    vo1.equals(vo2); //True
    vo1 == vo2; //True
    

    在 Java 中实现值对象模式有点困难,因为 == 运算符总是比较对象身份。一种方法是实现一个对象缓存,它为每个值返回相同的对象。

    public class VO {
      Map<Integer, WeakReference<VO>> cache = new LinkedHashMap<Integer, WeakReference<VO>>(); 
      public static VO getInstance(int value) {
         VO cached = cache.get(value);
         if(cached == null) {
            cached = new VO(value);
            cache.put(value, new WeakReference<VO>(cached));
         }
         return cached.get();
      }
    
      private int value;
      private VO(int value) {
        this.value = value;
      }
    }
    

    【讨论】:

    • “包含的值对象的两个实例之间的任何比较都应表明...” - “包含”?包含什么?
    • @Pang 修正了句子。
    【解决方案2】:

    值对象和数据传输对象是设计模式。

    • 值对象:当需要根据对象的值来衡量对象的相等性时使用。

    现实世界的例子是 java.time.LocalDate

    public class HeroStat {
    
       // Stats for a hero
    
       private final int strength;
       private final int intelligence;
       private final int luck;
    
       // All constructors must be private.
       private HeroStat(int strength, int intelligence, int luck) {
        this.strength = strength;
        this.intelligence = intelligence;
        this.luck = luck;
       }
    
      // Static factory method to create new instances.
      public static HeroStat valueOf(int strength, int intelligence, int luck) {
        return new HeroStat(strength, intelligence, luck);
      }
    
      public int getStrength() {
         return strength;
      }
    
      public int getIntelligence() {
         return intelligence;
      }
    
      public int getLuck() {
         return luck;
      }
    
      /*
       * Recommended to provide a static factory method capable of creating an instance 
           from the formal
       * string representation declared like this. public static HeroStat parse(String 
         string) {}
       */
    
      // toString, hashCode, equals
    
      @Override
      public String toString() {
          return "HeroStat [strength=" + strength + ", intelligence=" + intelligence
        + ", luck=" + luck + "]";
      }
    
      @Override
      public int hashCode() {
         final int prime = 31;
         int result = 1;
         result = prime * result + intelligence;
         result = prime * result + luck;
         result = prime * result + strength;
         return result;
      }
    
      @Override
      public boolean equals(Object obj) {
           if (this == obj) {
              return true;
            }
           if (obj == null) {
              return false;
           }
           if (getClass() != obj.getClass()) {
              return false;
           }
           HeroStat other = (HeroStat) obj;
           if (intelligence != other.intelligence) {
              return false;
           }
           if (luck != other.luck) {
              return false;
           }
           if (strength != other.strength) {
              return false;
           }
              return true;
        }
        // The clone() method should not be public. Just don't override it.
      }
    

    - 数据传输对象 :将具有多个属性的数据从客户端一次传递到服务器,以避免多次调用远程服务器。

      public class CustomerDto {
           private final String id;
           private final String firstName;
           private final String lastName;
    
           /**
            * @param id        customer id
            * @param firstName customer first name
            * @param lastName  customer last name
            */
            public CustomerDto(String id, String firstName, String lastName) {
               this.id = id;
               this.firstName = firstName;
               this.lastName = lastName;
            }
    
            public String getId() {
               return id;
            }
    
            public String getFirstName() {
               return firstName;
            }
    
            public String getLastName() {
               return lastName;
            }
         }
    

    【讨论】:

      【解决方案3】:

      数据传输对象用于在数据访问层 (DAO) 中设置来自数据库的属性值,而使用 VO 模式我们可以在 MVC 的控制器层中设置已经在 DAO 层中设置的值。客户端可以访问 VO 对象而不是 DTO,他/她可以在 jsp 页面中迭代。 你可以说这两个层的关注点是分离的。

      【讨论】:

        【解决方案4】:

        DTO 是一个代表一些数据的类,其中没有逻辑。 DTO 通常用于在单个应用程序内的不同应用程序或不同层之间传输数据。您可以将它们视为愚蠢的信息袋,其唯一目的就是将这些信息传递给接收者。

        另一方面,值对象是您的领域模型的完整成员。它符合与实体相同的规则。值对象和实体之间的唯一区别是值对象没有自己的身份。这意味着具有相同属性集的两个值对象应该被认为是相同的,而两个实体即使它们的属性匹配也是不同的。

        值对象确实包含逻辑,并且通常它们不用于在应用程序边界之间传输数据。阅读更多here

        【讨论】:

          【解决方案5】:

          比较 DTO 对象和值对象就像比较橘子和苹果。

          它们服务于完全不同的情况。 DTO 定义了数据将如何在层之间传输的对象/类结构,而值对象定义了比较值时相等的逻辑。

          让我用例子来解释你,让我们首先尝试理解值对象:-

          值对象是一个对象,其相等性是基于值而不是 而不是身份。

          考虑下面的代码,我们创建了两个货币对象,一个是一卢比硬币,另一个是一卢比纸币。

          Money OneRupeeCoin = new Money();
          OneRupeeCoin.Value = 1;
          OneRupeeCoin.CurrencyType = "INR";
          OneRupeeNote.Material = "Coin";
          
          Money OneRupeeNote = new Money();
          OneRupeeNote.Value = 1;
          OneRupeeCoin.CurrencyType = "INR";
          OneRupeeNote.Material = "Paper";
          

          现在,当您比较上述对象时,下面的比较应该评估为真,因为 1 卢比纸币在现实世界中等于 1 卢比硬币。

          因此,无论您是使用“==”运算符还是使用“Equals”方法,比较结果都应该为真。默认情况下,“==”或“equals”不会评估为真,因此您需要使用运算符覆盖和方法覆盖来获得所需的行为。你可以看到这个link,它解释了如何实现同样的目标。

          if (OneRupeeCoin==OneRupeeNote)
           {
           Console.WriteLine("They should be equal");
           }
          if (OneRupeeCoin.Equals(OneRupeeNote))
           {
           Console.WriteLine("They should be equal ");
           }
          

          通常值对象是不变性的良好候选者;您可以从here 了解更多信息。你可以看到this video,它描述了如何创建不可变对象。

          现在让我们尝试理解 DTO:-

          DTO(Data Transfer objects)是一个用于移动简化的数据容器 层之间的数据传输。

          它们也被称为传输对象。 DTO 仅用于传递数据,不包含任何业务逻辑。它们只有简单的 setter 和 getter。

          例如,考虑以下调用,我们进行了两次调用,一次获取客户数据,另一次获取产品数据。

          DataAccessLayer dal = new DataAccessLayer();
          //Call 1:-  get Customer data
          CustomerBO cust = dal.getCustomer(1001);
          
          //Call 2:-  get Products for the customer
          ProductsBO prod = dal.getProduct(100);
          

          所以我们可以将 Customer 和 Product 类合并为一个类,如下所示。

          class CustomerProductDTO
          {
            // Customer properties
                  public string CustomerName { get; set; }
             // Product properties
                  public string ProductName { get; set; }
                  public double ProductCost { get; set; }
          }
          

          现在只需一个电话,我们就可以同时获取客户和产品数据。数据传输对象用于两种场景,一是改善远程调用,二是扁平化对象层次结构;您可以阅读此article,其中解释了有关数据传输对象的更多信息。

          //Only one call
          CustomerProductDTO cust = dal.getCustomer(1001);
          

          下面是完整的比较表。

          【讨论】:

            【解决方案6】:

            DTO 是您可以在系统的边界处使用的对象。例如,当您有一个 SOAP Web 服务并且想要返回响应时,您将使用 DTO。它比必须通过网络返回的实际 XML 更容易处理。 DTO 通常由工具生成,例如基于 WSDL。 DTO 通常根据服务消费者的需求量身定制,并且会受到性能要求的影响。

            另一方面,值对象存在于系统的核心中。它捕获业务逻辑和可能的格式化规则。它使您的代码更具类型安全性和表现力。它还解决了“原始痴迷”反模式。很好的例子是使用类“SocialSecurityNumber”而不是字符串。或金钱而不是十进制。这些对象应该是不可变的,这样它们看起来更像原语,并且可以在不同的线程之间轻松共享。

            例如在假设的“客户订单”系统中:

            CustomerAndLastFiveOrders 是 DTO(优化以避免多次网络调用)

            客户是实体

            MoneySKU 是 Value 对象

            【讨论】:

              【解决方案7】:

              我建议不要使用数据传输对象。在我看来,它是一种 EJB 1.0 反模式,被那些坚持层纯度的人赋予了价值。

              值对象很有用。它通常是一个不可变的对象,例如 Money。它们应该是线程安全的。

              【讨论】:

              • 对不起。我以为你说过我们必须使它成为线程安全的。
              • 有时,DTO 用作(网络和序列化)优化,因为您的业务对象可能非常繁重,但您只需要传输其信息的一小部分。
              【解决方案8】:

              值对象是可以封装为对象的有用的东西,但它没有标识。将其与具有身份的实体进行比较。因此,在订单处理系统中,Customer 或 Order 或 LineItem 是指向特定人员、事物或事件的概念,因此它们是实体,其中值对象类似于货币金额,没有独立存在自己。例如,对于应用程序的一部分涉及计算如何在不同帐户之间分配付款的系统,我创建了一个不可变的 Money 对象,该对象具有一个 divide 方法,该方法返回一个 Money 对象数组,将原始对象的金额平均分配给它们,即这样划分金额的代码就在一个方便编写 jsp 的人可以使用的地方,他们不必用非演示相关的代码来搞乱 jsp。

              数据传输对象是一个包装器,用于将事物捆绑在一起以跨应用层或层发送。这个想法是您希望通过设计发送大量信息的方法来最小化网络来回流量。

              【讨论】:

                猜你喜欢
                • 2011-07-16
                • 2020-06-06
                • 1970-01-01
                • 1970-01-01
                • 2011-03-14
                • 2021-08-02
                • 2012-07-22
                • 2010-11-28
                • 2017-04-11
                相关资源
                最近更新 更多