对象知道其他对象多少钱?这是demeter法律违规的例子吗?

时间:2015-03-26 11:19:24

标签: java class oop design-patterns

我理解如何让对象相互了解是个问题。

在我的简单示例中,我已经上课:BillCustomerCheckoutCart。客户列出了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直接相关的customer(它有现场客户客户)

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

4 个答案:

答案 0 :(得分:3)

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

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

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

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

选项3:cart.addBillToCustomer(bill);

继续以上,第三次调用的语义非常不同。这个电话的内容是: Customer只能通过Cart s 付费。

结论

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

答案 1 :(得分:2)

首先,让我们试着找出demeter的哪些定律:

  

得墨忒耳定律(LoD):

     

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

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

让我们考虑使用您的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。对象能了解多少其他对象?

demeter的法律明确规定: C类的方法f应该只调用这些方法:

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

    <强> 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
        }
    }
    

    即,必须更改客户的实施。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

为什么需要单独的Bill and Cart对象?你有一个购物车看起来很奇怪,你从中获得了客户并向客户添加账单。

客户可能只有国家付费或未付款的购物车对象,为什么将它们分开很重要?

(我问这个问题,因为如果你现在可以简化抽象,那么以后就可以受益了)