【问题标题】:How much can object know about other object? Is it example of demeter's law violation?对象可以了解其他对象多少?这是demeter违反法律的例子吗?
【发布时间】:2015-05-30 09:20:43
【问题描述】:

我在理解如何让对象相互了解方面遇到了问题。

在我的简单示例中,我有类:Bill、Customer、Checkout 和 Cart。客户有他们的bills 列表,购物车关联到customer 并结帐完成购买操作(将bill 添加到customer)

SO 在方法 public void checkouts(Cart cart) 我初始化新账单 Bill bill = new Bill(cart) 然后我想向客户添加账单。我有 3 种可能的方法:

cart.getCustomer().getBills().add(bill);
//   ^^^ directly add bill to ArrayList in Customer
cart.getCustomer().addBill(bill);
//   ^^^ Customer get bill and put it itself in their own method
cart.addBillToCustomer(bill);
//   ^^^ In this example class Checkout doesn't now anything about customer. 
//       Responsibility goes to cart which calls getCustomer().addBill(bill)

我猜第一个确实是错误的,因为Checkout 必须知道getBills() 返回array list(这很危险)

在第二个示例中,checkouts 获取与它没有直接关联的客户,并且它必须知道客户界面才能添加账单。

在第三个结帐示例中,与客户没有直接关联,不要使用任何关于客户的知识,它只是将任务委托给cart,cart 直接连接到customer(它有字段客户客户)

哪种解决方案最好,为什么以及为什么其他解决方案更差?

【问题讨论】:

    标签: java class oop design-patterns


    【解决方案1】:

    三个在Java 中有效,但最好的解决方案是cart.addBillToCustomer(bill); 仅如果您在addBillToCustomer 方法中进行必要的验证。

    【讨论】:

      【解决方案2】:

      选项 1:cart.getCustomer().getBills().add(bill);

      这样做的主要问题是您依赖于集合getBills() 返回的可变性。这是一个坏主意,因为它破坏了封装,这就是为什么通常在 getter 中返回内部集合的不可变副本/视图的原因。到那时,这个解决方案根本行不通。

      选项 2:cart.getCustomer().addBill(bill);

      这假设知道购物车将有一个与之关联的客户,并且客户可以向他们添加账单。这些假设没有错,但还有第三个隐含假设:Cart 不需要知道客户何时收到新账单。事实上,Cart 甚至不必存在。

      选项 3:cart.addBillToCustomer(bill);

      从上面继续,第三次调用的语义是非常不同的。这个电话的意思是:Customers 只能通过Carts 计费。

      结论

      总而言之:选项 1 是完全错误的,但您选择选项 2 还是选项 3 取决于您希望模型表达什么,它们代表了业务逻辑之间的实际差异。

      【讨论】:

      • Ad2: Cart doesn't need to know when a customer is issued with a new bill. - 但它不知道,是吗?它只是将任务委托给客户。而且我也没有得到In fact Cart doesn't even have to exist. - 那是怎么回事?结帐必须知道哪个customer 得到bill,对吗?这是否意味着我应该考虑使用customer 向方法void checkouts 添加额外的参数?
      • @FilipBartuzi 这意味着如果您可以从任何其他来源获得Customer 实例,您可以向它开具账单。因此,例如,后台进程可以从数据库中加载客户数据并向他们添加账单,而无需 Cart 对象。而在第三种情况下,如果不先创建 Cart,就无法创建账单。
      • @FilipBartuzi 这也意味着如果Cart 的状态需要在Customer 计费时更新(例如,您在Cart 本身中有一个isBilled 标志),选项 2 不是您需要的。
      【解决方案3】:

      为什么需要单独的 Bill 和 Cart 对象?你有一辆购物车,然后你从中获得客户,然后向客户添加账单,这看起来真的很奇怪。

      客户可能只拥有状态为已付款或未付款的 Cart 对象,为什么将它们分开很重要?

      (我问这个是因为如果您现在可以使抽象更简单,那么以后可能会受益)

      【讨论】:

        【解决方案4】:

        首先,让我们试着弄清楚得墨忒耳的定律是什么:

        得墨忒耳法则 (LoD):

        类 C 的方法 f 应该只调用 这些方法:

        1. C
        2. 由 f 创建的对象
        3. 作为参数传递给 f 的对象
        4. 保存在 C 的实例变量中的对象

        让我们考虑一些使用您的 Checkout 类的示例:

        public class Checkout {
            private MyLogger logger;
        
            public void log(String msg) {
                // Write down the msg here
            }
        
            // 1. method log1 should call method(s) of class Checkout, i.e Checkout.log
            public void log1() {
                log("Hello");
            }
        
            // 2. method log2 should call method(s) of an object created by itself, i.e Date.toString
            public void log2() {
                Date now = new Date();
                System.out.println(now.toString());
            }
        
            // 3. method log3 should call method(s) of an object passed as an argument to it, i.e Cart.toString
            public void log3(Cart cart) {
                System.out.println(cart.toString());
            }
        
            // 4. method log4 should call method(s) of an object which is an instance variable of C, i.e Logger.log
            public void log4() {
                logger.log("some log message");
            }
        
        
            public void checkouts(Cart cart) {
                Bill bill = new Bill()
                cart.getCustomer().getBills().add(bill);
                //   ^^^ directly add bill to ArrayList in Customer
                cart.getCustomer().addBill(bill);
                //   ^^^ Customer get bill and put it itself in their own method
                cart.addBillToCustomer(bill);
                //   ^^^ In this example class Checkout doesn't now anything about customer. 
                //       Responsibility goes to cart which calls getCustomer().addBill(bill)
            }
        }
        
            public class Cart {
                Customer customer;
            }
            
            public class Customer {
                List<Bill> bills;
            
                public void addBill(Bill bill) {
            
                }
            }
            
            public class Bill {}
        

        现在,回到你的问题:

        1.对象可以了解其他对象多少?

        得墨忒耳定律中明确规定: C 类的方法 f 应该只调用这些方法:

        1. C
        2. 由 f 创建的对象
        3. 作为参数传递给 f 的对象
        4. 保存在 C" 的实例变量中的对象。

        2。是不是demeter违法的例子?

        • 第一种和第二种情况可以。
        • 第三种情况没问题 - 一个对象 (Cart) 作为参数传递给 f

        解释:

        对于第一个cart.getCustomer().getBills().add(bill);,如果我们改变实现如下:

        public class Customer {
            Bill bills[]; // error bills.add(Bill)
        }
        

        那么代码将不再起作用,因为数组没有add方法。

        另外,对于第二个: cart.getCustomer().addBill(bill);

        public class Customer {
            Bill bills[];
        
            public void addBillBefore (Bill bill) {
                bills.add(bill);
            }
        

        必须改为

            public void addBillAfter(Bill bill) {
                // Assume that current index is correctly setup
                bills[currentIndex] = bill
            }
        }
        

        也就是说,客户的实现必须改变。

        【讨论】:

        • 我总是将得墨忒耳法则更多地视为指导方针:)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多