2009-03-05 37 views
5

Tôi đã gặp phải điều này một vài lần, và tôi tự hỏi cách OO để giải quyết các tham chiếu vòng tròn là gì. Bởi vì tôi có nghĩa là lớp A có lớp B là một thành viên, và B lần lượt có lớp A là một thành viên.Làm thế nào để giải quyết crossencess chéo trong OOP?

Một ví dụ về điều này sẽ là lớp Người có người phối ngẫu Người làm thành viên.

Person jack = new Person("Jack"); 
Person jill = new Person("Jill"); 
jack.setSpouse(jill); 
jill.setSpouse(jack); 

Ví dụ khác là các lớp sản phẩm có một số Bộ sưu tập các sản phẩm khác làm thành viên. Ví dụ, bộ sưu tập đó có thể là sản phẩm mà những người quan tâm đến sản phẩm này cũng có thể quan tâm và chúng tôi muốn duy trì danh sách đó trên cơ sở mỗi sản phẩm, chứ không phải trên cùng thuộc tính được chia sẻ (ví dụ: chúng tôi không muốn chỉ hiển thị tất cả các sản phẩm khác trong cùng một danh mục).

Product pc = new Product("pc"); 
Product monitor = new Product("monitor"); 
Product tv = new Product("tv"); 
pc.setSeeAlso({monitor, tv}); 
monitor.setSeeAlso({pc}); 
tv.setSeeAlso(null); 

(các sản phẩm này chỉ để làm một điểm, vấn đề này không phải là về thời tiết hay không sản phẩm nào đó sẽ liên hệ với nhau)

này sẽ được thiết kế xấu trong OOP nói chung? Liệu tất cả các ngôn ngữ OOP có nên cho phép điều này, hay nó chỉ là thực hành không tốt? Nếu đó là thực hành không tốt, thì cách nào tốt nhất để giải quyết vấn đề này?

+0

@Jon Limjap: Cảm ơn bạn đã sửa lỗi đánh máy. – tehvan

Trả lời

1

Đây không phải là vấn đề cơ bản trong thiết kế OO. Một ví dụ về một thời điểm nó có thể trở thành một vấn đề là trong quá trình truyền tải đồ thị, ví dụ, tìm đường đi ngắn nhất giữa hai đối tượng - bạn có khả năng có thể đi vào một vòng lặp vô hạn. Tuy nhiên, đó là một cái gì đó bạn sẽ phải xem xét trên cơ sở từng trường hợp cụ thể. Nếu bạn biết có thể có tham chiếu chéo trong một trường hợp như vậy, hãy mã hóa một số kiểm tra để tránh các vòng vô hạn (ví dụ, duy trì một tập hợp các nút truy cập để tránh truy cập lại). Nhưng nếu không có lý do nó có thể là một vấn đề (ví dụ như trong các ví dụ bạn đưa ra trong câu hỏi của bạn), thì không có gì xấu cả khi có các tham chiếu chéo đó. Và trong nhiều trường hợp, như bạn đã mô tả, đó là một giải pháp tốt cho vấn đề ở bàn tay.

2

Ví dụ bạn đưa ra là (với tôi, dù sao) các ví dụ về thiết kế OO hợp lý.

Vấn đề tham chiếu chéo mà bạn mô tả không phải là một tạo phẩm của bất kỳ quy trình thiết kế nào nhưng là đặc điểm thực tế của những thứ bạn đại diện cho đối tượng, vì vậy tôi không thấy có vấn đề gì.

Bạn gặp phải điều gì đã cho bạn ấn tượng rằng cách tiếp cận này là thiết kế kém?

Cập nhật ngày 11 tháng 3:

Trong các hệ thống thiếu thu gom rác thải, nơi quản lý bộ nhớ được quản lý một cách rõ ràng, một cách tiếp cận phổ biến là yêu cầu tất cả các đối tượng có một chủ sở hữu - một số đối tượng khác có trách nhiệm quản lý tuổi thọ của đối tượng đó.

Một ví dụ là lớp TComponent Delphi, cung cấp hỗ trợ tầng - phá hủy thành phần gốc và tất cả các thành phần được sở hữu cũng bị hủy.

Nếu bạn đang làm việc trên hệ thống như vậy, các loại vòng lặp tham chiếu được mô tả trong câu hỏi này có thể được coi là thiết kế kém vì không có chủ sở hữu rõ ràng, không có đối tượng chịu trách nhiệm quản lý thời gian hoạt động.

Cách mà tôi thấy điều này được xử lý trong một số hệ thống là giữ lại các tham chiếu (vì chúng nắm bắt đúng mối quan tâm của doanh nghiệp) và thêm vào đối tượng rõ ràng TransactionContext sở hữu mọi thứ được tải vào miền doanh nghiệp kho dữ liệu. Đối tượng ngữ cảnh này quan tâm đến việc biết các đối tượng nào cần được lưu và dọn dẹp mọi thứ khi quá trình xử lý hoàn tất.

+0

Nó có vẻ như cuộc sống thực với tôi, tôi chỉ nghĩ rằng sẽ không có máy ảo bị nhầm lẫn đối tượng đuổi ... tải một người, tải người phối ngẫu của người đó, một lần nữa người phối ngẫu của người đó, ... đó là một vòng lặp vĩnh cửu. Chỉ cần tự hỏi nếu tất cả các máy ảo có thể xử lý đúng cách. – tehvan

+0

VM? Bạn có nghĩa là runtimes? Các hệ thống chỉ đếm được (ví dụ như các con trỏ thông minh của COM hoặc cuộn), bất kỳ hệ thống GC thực nào cũng sẽ không –

+0

Các chu trình tham chiếu được xử lý tốt bởi cả môi trường CLR (.NET) và JVM. Chúng cũng được xử lý tốt bởi môi trường Lisp và Smalltalk ban đầu, trong đó phần lớn công nghệ này ban đầu được chứng minh. – Bevan

1

Thời gian chính vấn đề này là nếu nó trở nên quá khó hiểu để đối phó, hoặc duy trì, vì nó có thể trở thành một dạng mã spaghetti.

Tuy nhiên, để chạm vào ví dụ của bạn;

See Also là hoàn toàn hợp lệ nếu điều này là một tính năng bạn cần trong mã của bạn - đó là một danh sách đơn giản của con trỏ (hoặc tham chiếu) để các vật dụng khác một người sử dụng có thể quan tâm đến

Tương tự như nó được. hoàn toàn hợp lệ để thêm vợ/chồng, vì đây là mối quan hệ thực sự đơn giản đơn giản mà sẽ không gây nhầm lẫn cho người nào đó đang duy trì mã của bạn.

Tôi luôn thấy nó là một mã số tiềm năng, hoặc có thể là cảnh báo để lùi lại một bước và hợp lý hoá những gì tôi đang làm.

Đối với một số hệ thống tìm các mối quan hệ đệ quy trong mã của bạn (được đề cập trong một chú thích ở trên), chúng có thể xuất hiện bất kể kiểu thiết kế này. Gần đây tôi đã làm việc trên một hệ thống thu thập siêu dữ liệu đã đệ quy 'các loại' các mối quan hệ - tức là các cột có liên quan về mặt logic với các cột khác. Nó cần phải được xử lý bởi mã cố gắng phân tích cú pháp hệ thống của bạn.

-2

Một cách để khắc phục điều này là tham chiếu đến đối tượng khác qua id.

ví dụ:

Person jack = new Person(new PersonId("Jack")); 
Person jill = new Person(new PersonId("Jill")); 
jack.setSpouse(jill.getId()); 
jill.setSpouse(jack.getId()); 

Tôi không nói đó là giải pháp hoàn hảo, nhưng nó sẽ ngăn chặn tham chiếu vòng tròn. Bạn đang sử dụng một đối tượng thay vì tham chiếu đối tượng để mô hình hóa mối quan hệ.

+0

Đây là suy nghĩ quá giống như một cơ sở dữ liệu. Việc đặt tham chiếu đến một đối tượng sẽ hoàn thành điều tương tự với ít công việc hơn. – Boden

+0

Nhưng nếu bạn muốn lưu đồ thị đối tượng vào cơ sở dữ liệu, phương pháp này sẽ cho phép tải một phần biểu đồ đối tượng mà không cần ủy quyền và ngăn chặn tham chiếu vòng tròn. Như tôi đã nói, nó không phải là giải pháp hoàn hảo nhưng tôi đã thấy nó được sử dụng thành công trong một ứng dụng mã đa triệu. – parkr

0

Tôi không nghĩ rằng tham chiếu vòng tròn như vậy là một vấn đề.

Tuy nhiên, việc đặt tất cả các mối quan hệ bên trong đối tượng đó có thể gây quá nhiều lộn xộn, vì vậy bạn có thể muốn đại diện cho chúng bên ngoài. Ví dụ. bạn có thể sử dụng bảng băm để lưu trữ các mối quan hệ giữa các sản phẩm.

1

Tôi không nghĩ đây là ví dụ về tham chiếu chéo.

chéo tham khảo thường gắn liền với trường hợp này:

class A 
{ 
    public void MethodA(B objectB) 
    { 
     objectB.SomeMethodInB(); 
    } 
} 

class B 
{ 
    public void MethodB(A objectA) 
    { 
     objectA.SomeMethodInA(); 
    } 
} 

Trong trường hợp này từng đối tượng loại "đạt ở" với nhau; Một cuộc gọi B, B gọi A, và chúng trở nên kết hợp chặt chẽ. Điều này thậm chí còn tồi tệ hơn nếu A và B nằm trong các gói/không gian tên/hội đồng khác nhau; trong nhiều trường hợp, chúng sẽ tạo ra các lỗi thời gian biên dịch khi các assembly được biên dịch một cách tuyến tính.

Cách để giải quyết điều đó là có đối tượng hoặc triển khai giao diện với phương thức mong muốn.

Trong trường hợp của bạn, bạn chỉ có một mức độ "đạt trong":

public Class Person 
{ 
    public void setSpouse(Person person) 
    { ... } 
} 

Tôi không nghĩ rằng đây là không hợp lý, thậm chí cũng không một trường hợp tham khảo chéo/tham chiếu vòng tròn.

0

Tham khảo các đối tượng khác không phải là thiết kế OO thực sự. Đó là cách nhà nước được quản lý trong mỗi đối tượng.

Nguyên tắc chung là Luật Demeter. Nhìn vào giấy hoàn hảo này của LoD (Paperboy và ví): click here

Các vấn đề liên quan