Theo luật của Demeter, một lớp học có được phép trả lại một trong những thành viên của nó không?


13

Tôi có ba câu hỏi liên quan đến luật của Demeter.

Ngoài các lớp được chỉ định cụ thể để trả về các đối tượng - chẳng hạn như các lớp của nhà máy và nhà xây dựng - thì phương thức trả lại một đối tượng là gì, ví dụ như một đối tượng được giữ bởi một trong các thuộc tính của lớp hoặc sẽ vi phạm luật của demeter (1) ? Và nếu nó vi phạm luật demeter, liệu đối tượng được trả về có phải là một đối tượng bất biến đại diện cho một phần dữ liệu và không chứa gì ngoài getters cho dữ liệu này (2a) không? Hoặc là một ValueObject như vậy tự nó là một mô hình chống, bởi vì mọi thứ được thực hiện với dữ liệu trong lớp đó được thực hiện bên ngoài lớp (2b)?

Trong mã giả:

class A {}

class B {

    private A a;

    public A getA() {
        return this.a;
    }
}

class C {

    private B b;
    private X x;

    public void main() {
        // Is it okay for B.getA to exist?
        A a = this.b.getA();

        a.doSomething();

        x.doSomethingElse(a);
    }
}

Tôi nghi ngờ rằng luật của Demeter cấm một mô hình như ở trên. Tôi có thể làm gì để đảm bảo doSomethingElse()có thể được gọi trong khi không vi phạm luật (3)?


9
Rất nhiều người (đặc biệt là các lập trình viên chức năng) xem Luật Demeter là một mô hình chống đối. Những người khác xem nó như là "Gợi ý hữu ích đôi khi của Demeter."
David Hammen

1
Tôi sẽ nêu lên câu hỏi này vì nó minh họa cho sự phi lý của Luật Demeter. Đúng, LoD là "đôi khi hữu ích", nhưng nó thường ngu ngốc.
dùng949300

Làm thế nào là xthiết lập?
candied_orange

@CandiedOrange xchỉ là một thuộc tính khác có giá trị chỉ là một số đối tượng có thể gọi doSomethingElse.
dùng2180613

1
Ví dụ của bạn không vi phạm LOD. LOD là về việc ngăn khách hàng của một đối tượng khớp với các đối tượng đó hoặc các cộng tác viên. bạn muốn tránh những thứ như user.currentSession.isLoggedIn(). bởi vì nó kết nối khách hàng của userkhông chỉ usermà cả cộng tác viên của nó , session. Thay vào đó bạn muốn có thể viết user.isLoggedIn(). điều này thường đạt được bằng cách thêm một isLoggedInphương thức usermà các đại biểu thực hiện currentSession.
Giô-na

Câu trả lời:


7

Trong nhiều ngôn ngữ lập trình, tất cả các giá trị được trả về là các đối tượng. Như những người khác đã nói, không thể sử dụng các phương pháp của các đối tượng trả lại buộc bạn không bao giờ trả lại bất cứ điều gì cả. Bạn nên tự hỏi mình, "Trách nhiệm của lớp A, B và C là gì?" Đây là lý do tại sao sử dụng các tên biến metasyntactic như A, B và C luôn thu hút câu trả lời "nó phụ thuộc" bởi vì không có trách nhiệm kế thừa trong các điều khoản đó.

Bạn có thể muốn xem xét Thiết kế hướng miền, nó sẽ cung cấp cho bạn một tập hợp các phương pháp phỏng đoán sắc thái hơn để lý giải về việc chức năng sẽ đi đâu và ai nên gọi cái gì.

Câu hỏi thứ hai của bạn liên quan đến các đối tượng bất biến nói lên khái niệm về
Đối tượng Giá trị so với đối tượng Thực thể. trong DDD, Bạn có thể truyền các đối tượng giá trị xung quanh mà hầu như không có giới hạn nào. Điều này không nói đúng cho các đối tượng thực thể.

LOD đơn giản được thể hiện tốt hơn nhiều trong DDD như các quy tắc để truy cập vào Uẩn . Không có đối tượng bên ngoài nên giữ các tham chiếu đến các thành viên của tập hợp. Chỉ nên tham khảo root.

Bỏ DDD sang một bên, ít nhất bạn muốn phát triển các bộ khuôn mẫu cho các lớp của bạn hợp lý cho miền của bạn. Thực thi các quy tắc về những khuôn mẫu đó khi bạn thiết kế hệ thống của mình.

Cũng luôn nhớ rằng tất cả các quy tắc này là để quản lý sự phức tạp, không để bản thân bạn mắc kẹt.


1
DDD có cung cấp bất kỳ quy tắc nào khi các thành viên của lớp như dữ liệu có thể bị lộ (tức là phải có getters) không? Tất cả các cuộc thảo luận xung quanh Law of Demeter, tell, don't ask, getters are evil, object encapsulationsingle responsibility principledường như sôi lên xuống cho câu hỏi này: khi phái đoàn đến một đối tượng tên miền phải được sử dụng như là trái ngược với nhận được giá trị của đối tượng và làm điều gì đó với nó? Sẽ rất hữu ích khi có thể áp dụng các tiêu chuẩn sắc thái cho các tình huống phải đưa ra quyết định như vậy.
dùng2180613

Các nguyên tắc như "getters is evil" tồn tại bởi vì nhiều người coi các thể hiện của lớp là cấu trúc dữ liệu (Một túi dữ liệu được truyền xung quanh). Đây không phải là lập trình hướng đối tượng. Đối tượng là các gói chức năng / hành vi thực sự cung cấp một số công việc. Phạm vi công việc được xác định bởi trách nhiệm. Công việc này rõ ràng sẽ tạo ra các đối tượng được trả về từ các phương thức, v.v. Nếu lớp của bạn thực sự đang làm công việc có ý nghĩa mà bạn có thể định nghĩa rõ ràng ngoài "đại diện cho đối tượng dữ liệu X với các thuộc tính Y và Z", thì bạn đang đi đúng hướng . Tôi sẽ không căng thẳng về nó.
Jeremy

Để mở rộng điểm đó, khi bạn sử dụng nhiều getters / người hỏi, điều đó thường có nghĩa là bạn có một phương thức lớn nào đó đang sắp xếp mọi thứ, mà Fowler gọi là một kịch bản giao dịch. Trước khi bạn bắt đầu sử dụng một getter, hãy tự hỏi mình ... tại sao tôi lại yêu cầu dữ liệu này từ đối tượng? Tại sao chức năng này không phải là một phương thức trên đối tượng? Nếu lớp này không có trách nhiệm thực hiện chức năng này, thì hãy tiếp tục và sử dụng một getter. Cuốn sách [Tái cấu trúc] ( martinfowler.com/books/refactoring.html ) của Fowler là một cuốn sách về heuristic cho những câu hỏi như vậy.
Jeremy

Trong cuốn sách Thực hiện thiết kế hướng tên miền của mình, Vaughn Vernon đã đề cập đến Luật yêu cầu và nói, đừng hỏi trong Ch 10, trang 382. Có thể đáng xem nếu bạn có quyền truy cập vào nó.
Jeremy

6

Theo luật của Demeter, một lớp học có được phép trả lại một trong những thành viên của nó không?

Vâng, nó chắc chắn là như vậy.

Hãy nhìn vào những điểm chính :

  • Mỗi đơn vị chỉ nên có kiến ​​thức hạn chế về các đơn vị khác: chỉ các đơn vị "chặt chẽ" liên quan đến đơn vị hiện tại.
  • Mỗi đơn vị chỉ nên nói chuyện với bạn bè của mình; đừng nói chuyện với người lạ.
  • Chỉ nói chuyện với bạn bè ngay lập tức của bạn.

Tất cả ba trong số đó khiến bạn hỏi một câu: Ai là bạn?

Khi quyết định trả lại cái gì, Luật Demeter hoặc nguyên tắc ít kiến ​​thức nhất (LoD) không cho rằng bạn bảo vệ chống lại các lập trình viên khăng khăng vi phạm nó. Nó ra lệnh rằng bạn không buộc các lập trình viên vi phạm nó.

Làm cho những nhầm lẫn này là chính xác lý do tại sao rất nhiều người nghĩ rằng một setter phải luôn luôn trả về khoảng trống. Không. Bạn phải cho phép một cách để thực hiện các truy vấn (getters) không thay đổi trạng thái của hệ thống. Đó là phân tách truy vấn lệnh cơ bản .

Điều này có nghĩa là bạn có thể tự do đi sâu vào một cơ sở mã kết nối bất cứ điều gì bạn muốn? Không. Xâu chuỗi lại với nhau những gì có nghĩa là bị xích lại với nhau. Nếu không, chuỗi có thể thay đổi và đột nhiên công cụ của bạn bị hỏng. Đây là những gì có nghĩa là bạn bè.

Chuỗi dài có thể được thiết kế cho. Các giao diện thông thạo, iDSL, phần lớn Java8 và StringBuilder cũ đều có nghĩa là cho phép bạn xây dựng chuỗi dài. Họ không vi phạm LoD vì mọi thứ trong chuỗi đều có nghĩa là làm việc cùng nhau và hứa sẽ tiếp tục làm việc cùng nhau. Bạn vi phạm demeter khi bạn xâu chuỗi những thứ không bao giờ nghe thấy về nhau. Bạn bè là những người hứa sẽ giữ cho chuỗi của bạn hoạt động. Bạn bè của bạn bè đã không.

Ngoài các lớp được chỉ định cụ thể để trả về các đối tượng - chẳng hạn như các lớp của nhà máy và nhà xây dựng - thì phương thức trả lại một đối tượng là gì, ví dụ như một đối tượng được giữ bởi một trong các thuộc tính của lớp hoặc sẽ vi phạm luật của demeter (1) ?

Điều này chỉ tạo ra một cơ hội để vi phạm demeter. Đây không phải là một vi phạm. Điều này thậm chí không hẳn là xấu.

Và nếu nó vi phạm luật demeter, liệu đối tượng được trả về có phải là một đối tượng bất biến đại diện cho một phần dữ liệu và không chứa gì ngoài getters cho dữ liệu này (2) không?

Bất biến là tốt nhưng không liên quan ở đây. Bắt mọi thứ thông qua một chuỗi dài hơn không làm cho nó tốt hơn. Điều gì làm cho nó tốt hơn là tách biệt việc nhận được từ việc sử dụng. Nếu bạn đang sử dụng, hãy hỏi những gì bạn cần làm tham số. Đừng đi săn lùng nó bằng cách đào sâu vào những kẻ lạ mặt.

Trong mã giả:

Tôi nghi ngờ rằng luật của Demeter cấm một mô hình như ở trên. Tôi có thể làm gì để đảm bảo rằng doS SomethingElse () có thể được gọi trong khi không vi phạm luật (3)?

Trước khi tôi nói về hãy x.doSomethingElse(a)hiểu rằng bạn đã viết một cách cơ bản

b.getA().doSomething()

Bây giờ, LoD không phải là một bài tập đếm số chấm . Nhưng khi bạn tạo một chuỗi bạn đang nói rằng bạn biết cách lấy một A(bằng cách sử dụng B) và bạn biết cách sử dụng một chuỗi A. Bây giờ ABtốt hơn là bạn thân vì bạn chỉ cần ghép chúng lại với nhau.

Nếu bạn vừa yêu cầu một cái gì đó để tay bạn một Anhư điều bạn có thể sử dụng Avà sẽ không chăm sóc nó đến từ đâu và Bcó thể sống một cuộc sống hạnh phúc và tự do của nỗi ám ảnh của bạn với nhận được Atừ B.

x.doSomethingElse(a)không có thông tin chi tiết về xnguồn gốc từ đâu, LoD không có gì để nói về nó.

LoD có thể khuyến khích tách sử dụng khỏi xây dựng . Nhưng tôi sẽ chỉ ra rằng nếu bạn tôn trọng mọi đối tượng là không thân thiện, bạn sẽ bị kẹt viết mã trong các phương thức tĩnh. Bạn có thể xây dựng một biểu đồ đối tượng phức tạp tuyệt vời theo cách này nhưng cuối cùng bạn phải gọi một phương thức trên nó để bắt đầu mọi thứ hoạt động. Bạn chỉ cần quyết định bạn bè của bạn là ai. Không thể tránh khỏi điều đó.

Vì vậy, có, một lớp được phép trả lại một trong những thành viên của nó theo LoD. Khi bạn làm, bạn nên làm rõ nếu thành viên đó thân thiện với lớp của bạn bởi vì nếu không, một số khách hàng có thể thử ghép bạn với lớp đó bằng cách sử dụng bạn để có được nó trước khi sử dụng nó. Điều đó quan trọng bởi vì bây giờ những gì bạn trả lại phải luôn hỗ trợ việc sử dụng đó.

Có nhiều trường hợp điều này chỉ đơn giản là không phải là một mối quan tâm. Bộ sưu tập có thể bỏ qua điều này đơn giản vì chúng có nghĩa là bạn bè với mọi thứ sử dụng chúng. Đối tượng giá trị tương tự là thân thiện với tất cả mọi người. Nhưng nếu bạn đã viết một tiện ích xác thực địa chỉ yêu cầu một đối tượng nhân viên mà nó trích xuất một địa chỉ từ đó, thì chỉ cần hỏi địa chỉ, bạn sẽ hy vọng rằng cả nhân viên và địa chỉ đều đến từ cùng một thư viện.


Giao diện thông thạo không phải là ngoại lệ đối với LOD vì một số khái niệm về sự thân thiện .... Trong giao diện trôi chảy, mỗi phương thức trong chuỗi được gọi trên cùng một đối tượng, vì tất cả đều tự trả về. settings.darkTheme().monospaceFont()chỉ là cú pháp đường chosettings.darkTheme(); settings.monospaceFont();
Jonah

3
@Jonah Trở về bản thân là sự thân thiện tối thượng. Và không phải tất cả các giao diện trôi chảy làm. Nhiều iDSL cũng thông thạo và trả về các loại khác nhau để thay đổi các phương thức có sẵn tại các điểm khác nhau. Cân nhắc jOOQ , xây dựng bước , xây dựng thuật sĩ , và cơn ác mộng của riêng tôi xây dựng con quái vật . Thông thạo là một phong cách. Không phải là một mô hình.
candied_orange

2

Ngoài các lớp được chỉ định cụ thể để trả về các đối tượng [...]

Tôi không hiểu lắm về lập luận này. Bất kỳ đối tượng nào cũng có thể trả về một đối tượng là kết quả của một thông báo. Đặc biệt là nếu bạn nghĩ trong "mọi thứ như đối tượng".

Tuy nhiên, các lớp là các đối tượng duy nhất có thể khởi tạo các đối tượng mới theo cách riêng của chúng bằng cách gửi cho chúng tin nhắn "mới" (hoặc thường được thay thế bằng một từ khóa mới trên hầu hết các ngôn ngữ OOP phổ biến)


Dù sao, liên quan đến câu hỏi của bạn, tôi khá nghi ngờ về một đối tượng trả lại một trong các thuộc tính của nó để được sử dụng ở nơi khác. Có thể là đối tượng này sẽ bị rò rỉ thông tin về trạng thái hoặc chi tiết thực hiện của nó.

Và bạn sẽ không muốn đối tượng C biết về chi tiết triển khai của B. Đây là một sự phụ thuộc không mong muốn vào đối tượng A từ phối cảnh đối tượng C.

Vì vậy, bạn có thể muốn tự hỏi mình nếu C, bằng cách biết A, không biết quá nhiều cho công việc anh ta phải làm, độc lập với LOD.

Có những trường hợp điều này có thể hợp pháp, ví dụ, yêu cầu một đối tượng bộ sưu tập cho một trong các mục của nó là phù hợp và không vi phạm bất kỳ quy tắc Demeter nào. Yêu cầu một đối tượng Sách cho đối tượng SQLConnection của nó, mặt khác, có rủi ro và nên tránh.

LOD tồn tại bởi vì nhiều lập trình viên có xu hướng yêu cầu các đối tượng cung cấp thông tin trước khi thực hiện một công việc từ đó. LOD ở đó để nhắc nhở chúng ta rằng đôi khi (nếu không phải luôn luôn) chúng ta chỉ làm quá phức tạp mọi thứ bằng cách muốn các đối tượng của chúng ta làm quá nhiều. Đôi khi chúng ta chỉ cần yêu cầu một đối tượng khác thực hiện công việc cho chúng ta. LOD ở đó để làm cho chúng ta suy nghĩ hai lần về nơi chúng ta đặt hành vi trong các đối tượng của chúng ta.


0

Ngoài các lớp được chỉ định cụ thể để trả về các đối tượng - chẳng hạn như các lớp của nhà máy và nhà xây dựng - liệu phương thức trả về một đối tượng có ổn không?

Tại sao không? Bạn dường như đề nghị rằng cần phải báo hiệu cho các lập trình viên của mình rằng lớp đặc biệt có nghĩa là trả lại các đối tượng hoặc hoàn toàn không phải trả lại các đối tượng. Trong các ngôn ngữ mà mọi thứ đều là một đối tượng, điều này sẽ hạn chế nghiêm trọng các lựa chọn của bạn, vì bạn hoàn toàn không thể trả lại bất cứ điều gì.


Ví dụ là về ẩn b.getA().doSomething()x.doSomethingElse(b.getA())
user2180613

A a = b.getA(); a.DoSomething();- quay lại một dấu chấm.
Robert Harvey

2
@ user2180613 - Nếu bạn muốn hỏi về b.getA().doSomething()x.doSomethingElse(b.getA())bạn nên hỏi rõ ràng về điều đó. Khi nó đứng, câu hỏi của bạn dường như là về this.b.getA().
David Hammen

1
Như Achưa là thành viên của C, không được tạo ra trong Cvà không được cung cấp để C, dường như sử dụng Atrong C(trong bất cứ cách) vi phạm LoD. Đếm số chấm không phải là những gì LoD nói về, nó chỉ là một công cụ minh họa. Có thể chuyển đổi Driver().getCar().getSteeringWheel().getFrontWheels().toRight()thành các câu lệnh riêng biệt mà mỗi câu chứa một dấu chấm, nhưng vi phạm LoD vẫn còn đó. Tôi đọc trong nhiều bài viết trên diễn đàn này rằng việc thực hiện các hành động nên được ủy quyền cho đối tượng thực hiện hành vi thực tế tức là tell don't ask. Trả lại giá trị dường như mâu thuẫn với điều đó.
dùng2180613

1
Tôi cho rằng đó là sự thật nhưng nó không trả lời câu hỏi.
dùng2180613

0

Phụ thuộc vào những gì bạn đang làm. Nếu bạn tiếp xúc với một thành viên trong lớp, khi đó không phải là thành viên của lớp bạn, hoặc loại thành viên đó là gì, đó là một điều tồi tệ. Nếu sau đó bạn thay đổi loại thành viên đó và loại hàm trả về thành viên đó và mọi người phải thay đổi mã của họ thì đó là điều xấu.

Mặt khác, nếu giao diện của lớp bạn nói rằng nó sẽ cung cấp một thể hiện của lớp A nổi tiếng, thì tốt thôi. Nếu bạn may mắn và bạn có thể làm điều này bằng cách cung cấp một thành viên trong lớp của bạn, tốt cho bạn. Nếu bạn đã thay đổi thành viên đó thành A thành B, thì bạn sẽ cần phải viết lại phương thức trả về A, rõ ràng bây giờ nó sẽ phải xây dựng một và điền vào đó với dữ liệu có sẵn, thay vì chỉ trả về một lớp một thành viên. Giao diện của lớp bạn sẽ không thay đổi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.