对象对其他对象了解多少?是违反得墨忒耳定律的例子吗?

How much can object know about other object? Is it example of demeter's law violation?

我对如何允许对象相互了解存在疑问。

在我的简单示例中,我有 classes: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() returns array list(这很危险)

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

在第三个结账示例中,它与客户没有直接关联,不使用任何关于客户的知识,它只是将任务委托给直接连接到 customercart(它有现场客户客户)

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

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

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

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

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

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

选项 3:cart.addBillToCustomer(bill);

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

计费

结论

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

为什么需要单独的 Bill 和 Cart 对象?看起来很奇怪,你有一个购物车,你从那里得到客户,然后给客户添加账单。

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

(我问这个是因为如果你现在可以简化抽象,以后可能会受益)

首先,让我们试着弄清楚得墨忒耳定律的内容:

Law of Demeter (LoD):

A method f of a class C should only call the methods of these:

  1. C
  2. An object created by f
  3. An object passed as an argument to f
  4. An object held in an instance variable of C

让我们考虑一些使用您的 Checkout class:

的示例
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.对象对其他对象了解多少?

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

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

2。是得墨忒耳违例的例子吗?

  • 第一种情况和第二种情况。
  • 第三种情况可以 - 一个对象 (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
    }
}

也就是说,必须更改客户的实施。