Khi nào một tham chiếu tròn đến một con trỏ cha mẹ được chấp nhận?


24

Câu hỏi Stack Overflow này là về một đứa trẻ có tham chiếu đến cha mẹ của nó, thông qua một con trỏ.

Nhận xét ban đầu khá quan trọng của thiết kế là một ý tưởng khủng khiếp.

Tôi hiểu điều này có lẽ không phải là ý tưởng tốt nhất nói chung. Từ một quy tắc chung, có vẻ công bằng khi nói, "đừng làm điều này!"

Tuy nhiên, tôi tự hỏi những loại điều kiện sẽ tồn tại nơi bạn sẽ cần phải làm một cái gì đó như thế này. Câu hỏi này ở đây và các câu trả lời / bình luận liên quan cho thấy ngay cả đối với các biểu đồ không làm điều gì đó như thế này.


1
Câu hỏi bạn liên kết có vẻ khá toàn diện về chủ đề này.
Cuộc đua nhẹ nhàng với Monica

4
@LightnessRacesinOrbit "không làm điều này" không thực sự hữu ích cho đến khi hiểu lý do tại sao .
enderland

2
Tôi thấy nhiều hơn "không làm điều này" ở đó. Tôi thấy những ưu và nhược điểm được tranh luận bởi nhiều chuyên gia.
Cuộc đua nhẹ nhàng với Monica

1
Bạn có thể có một danh sách hai chiều cần đi qua, một số loại bộ đệm tròn, có lẽ bạn đang đại diện cho hai đoạn đường được kết nối trong một trò chơi - nếu bạn cần thể hiện một cái gì đó hình tròn, thì đây có thể là một ý tưởng tốt.
rhughes

2
Quy tắc thực tế của tôi về ngón tay cái là một câu hỏi "Trẻ em có thể tồn tại mà không cần cha mẹ không?". (Nếu bạn xem xét XmlDocument và các nút của nó: một nút không thể tồn tại mà không có bối cảnh cây của tài liệu. Đó là một điều vô nghĩa). Nếu câu trả lời là không thì các liên kết hai chiều đều ổn: bạn có hai đối tượng chỉ có thể tồn tại cùng nhau. Nếu các đối tượng có thể tồn tại độc lập, thì tôi loại bỏ một trong hai liên kết đó.
mikalai

Câu trả lời:


43

Chìa khóa ở đây không phải là liệu hai đối tượng có tham chiếu vòng tròn hay không, mà là các tham chiếu đó cho biết quyền sở hữu của nhau.

Hai đối tượng không thể "sở hữu" lẫn nhau: điều này gây ra một tình huống khó xử cho việc khởi tạo và xóa lệnh. Một phải là một tham chiếu tùy chọn, hoặc nói cách khác chỉ ra rằng một đối tượng sẽ không quản lý vòng đời của đối tượng kia.

Hãy xem xét một danh sách liên kết đôi: hai nút liên kết qua lại với nhau, nhưng không "sở hữu" cái kia (danh sách sở hữu cả hai). Điều này có nghĩa là không nút nào phân bổ bộ nhớ cho người kia hoặc chịu trách nhiệm về việc nhận dạng hoặc quản lý trọn đời của người kia.

Cây có mối quan hệ tương tự, mặc dù các nút trong cây có thể phân bổ trẻ em và cha mẹ làm con riêng. Liên kết từ một đứa trẻ đến cha mẹ giúp truyền tải, nhưng một lần nữa không xác định quyền sở hữu.

Trong hầu hết các thiết kế OO, tham chiếu đến một đối tượng khác là thành viên dữ liệu của đối tượng ngụ ý quyền sở hữu. Ví dụ: giả sử chúng ta có các lớp Xe và Động cơ. Không ai là rất hữu ích của riêng mình. Chúng ta có thể nói rằng các đối tượng này phụ thuộc vào nhau: chúng đòi hỏi sự hiện diện của đối tượng khác để thực hiện công việc hữu ích. Nhưng cái nào "sở hữu" cái kia? Trong trường hợp này, chúng tôi sẽ nói rằng Car sở hữu Engine vì chiếc xe là "container" trong đó tất cả các thành phần ô tô sống. Trong cả thiết kế OO và thế giới thực, chiếc xe là tổng của các bộ phận của nó và tất cả các bộ phận đó được kết nối với nhau trong bối cảnh của chiếc xe. Động cơ có thể có tham chiếu trở lại Xe hoặc có thể có tham chiếu đến TorqueConverter,

Tài liệu tham khảo thông tư có thể là một mùi thiết kế xấu, nhưng không nhất thiết phải. Khi được sử dụng một cách thận trọng và được ghi lại một cách chính xác, chúng có thể giúp việc sử dụng các cấu trúc dữ liệu dễ dàng hơn.

Hãy thử đi qua một cái cây mà không có tài liệu tham khảo đi cả hai chiều giữa cha mẹ và con cái. Chắc chắn, bạn có thể đưa ra một cách tiếp cận dựa trên ngăn xếp dễ vỡ và phức tạp, hoặc bạn có thể sử dụng cách tiếp cận dựa trên tham chiếu rất đơn giản.


16

Có một số khía cạnh để xem xét trong một thiết kế như vậy:

  • các phụ thuộc cấu trúc
  • mối quan hệ sở hữu (iecro so với các loại khác của PGS)
  • nhu cầu điều hướng

Kết cấu phụ thuộc giữa các lớp:

Nếu bạn nhắm đến việc tái sử dụng các lớp thành phần, bạn nên tránh sự phụ thuộc không cần thiết và tránh các cấu trúc vòng tròn khép kín như vậy.

Tuy nhiên, đôi khi hai lớp được liên kết mạnh mẽ về mặt khái niệm. Trong trường hợp này, tránh phụ thuộc không phải là một lựa chọn thực sự. Ví dụ: một cây và các lá của nó, hay nói chung là một hỗn hợp và các thành phần của nó .

Quyền sở hữu đối tượng:

Có một đối tượng sở hữu đối tượng khác? Hay nói cách khác: nếu một đối tượng bị phá hủy, thì đối tượng kia cũng sẽ bị phá hủy?

Chủ đề này đã được Snowman giải quyết chuyên sâu, vì vậy tôi sẽ không đề cập đến vấn đề này ở đây.

Nhu cầu điều hướng giữa các đối tượng:

Một vấn đề cuối cùng là điều hướng cần. Hãy lấy ví dụ yêu thích của tôi, mẫu thiết kế tổng hợp của Gang of bốn .

Gamma & al. đề cập một cách rõ ràng về nhu cầu tiềm năng cần có một tham chiếu cha mẹ rõ ràng: " Duy trì tham chiếu từ các thành phần con đến cha mẹ của chúng có thể đơn giản hóa việc truyền tải và quản lý cấu trúc hỗn hợp " có thể làm chậm đáng kể các hoạt động và theo cách hàm mũ. Một tài liệu tham khảo trực tiếp, thậm chí thông tư có thể dễ dàng thao tác các vật liệu tổng hợp của bạn.

Một ví dụ có thể là một mô hình đồ họa của một hệ thống điện tử. Một cấu trúc tổng hợp có thể đại diện cho các bảng điện tử, mạch, các yếu tố. Để hiển thị và thao tác mô hình, bạn cần một số proxy hình học trong chế độ xem GUI. Sau đó, chắc chắn việc điều hướng từ phần tử GUI được người dùng chọn đến thành phần này dễ dàng hơn nhiều, để tìm ra phần tử nào là cha mẹ và các phần tử anh chị em có liên quan, hơn là bắt đầu tìm kiếm từ trên xuống.

Tất nhiên, như Gamma & al đã chỉ ra, bạn phải đảm bảo sự bất biến của mối quan hệ vòng tròn. Điều này có thể khó, như câu hỏi SO mà bạn đề cập đã chỉ ra. Nhưng nó hoàn toàn có thể quản lý và một cách an toàn.

Phần kết luận

Nhu cầu điều hướng sẽ không được đánh giá thấp. Không phải vô cớ mà UML rõ ràng đã nhấn mạnh nó trong ký hiệu mô hình hóa. Và vâng, có những tình huống hoàn toàn hợp lệ trong đó cần tham khảo thông tư.

Điểm duy nhất là đôi khi mọi người có xu hướng đi vào một hướng như vậy để nhanh chóng. Vì vậy, đáng để xem xét tất cả 3 khía cạnh liên quan trước khi đưa ra quyết định có quyết định hay không.


1
Câu trả lời này là IMHO không được đánh giá cao. OP hỏi: "Do đó đã là một mối quan hệ cha-con, khi là nó ok để thực hiện điều này bằng một tham chiếu vòng tròn". Vì vậy, cấu trúc và "quyền sở hữu" (theo nghĩa được đề cập ở đây) đã rõ ràng. Điều đó có nghĩa là tiêu chí duy nhất để thêm tài liệu tham khảo ở bên này hay bên kia là câu hỏi về "nhu cầu điều hướng" và "sự tái sử dụng độc lập của trẻ".
Doc Brown

@DocBrown - Bạn luôn có thể đặt tiền thưởng cho câu hỏi này một khi nó đủ điều kiện. :-)

1
@ GlenH7: điều đó sẽ không cho câu trả lời này nhiều phiếu hơn, chỉ có câu trả lời của Người tuyết (mà tôi nghĩ rằng nó bỏ lỡ phần nào câu hỏi).
Doc Brown

1
@DocBrown - Nhưng repz, đàn ông, repz!

... và nếu bạn thêm các lựa chọn về khả năng hiển thị như được bảo vệ bởi công chúng, điều đó mang đến cho bạn 9 tùy chọn khác nhau :)
mikalai

8

Thông thường, tài liệu tham khảo vòng tròn là một ý tưởng rất xấu bởi vì chúng có nghĩa là phụ thuộc vòng tròn. Có thể bạn đã biết tại sao các khoản giảm giá tròn là xấu, nhưng để hoàn thiện, phiên bản tl; dr là bất cứ khi nào các lớp A và B đều phụ thuộc vào nhau, không thể hiểu / sửa / tối ưu hóa / v.v. hiểu / sửa chữa / tối ưu hóa / vv các lớp khác cùng một lúc. Điều này nhanh chóng dẫn đến các cơ sở mã nơi bạn không thể thay đổi bất cứ điều gì mà không thay đổi mọi thứ.

Tuy nhiên, nó có thể có một tham chiếu vòng tròn mà không cần tạo ác phụ thuộc vòng tròn. Điều này hoạt động miễn là tài liệu tham khảo là tùy chọn đúng theo nghĩa chức năng. Điều đó có nghĩa là bạn có thể dễ dàng loại bỏ nó khỏi các lớp và chúng vẫn hoạt động, ngay cả khi chúng hoạt động chậm hơn. Trường hợp sử dụng chính mà tôi biết đối với các tham chiếu tạo không phụ thuộc vòng tròn như vậy là cho phép truyền tải nhanh các cấu trúc dữ liệu dựa trên nút như danh sách được liên kết, cây và đống. Ví dụ, về nguyên tắc, bất kỳ thao tác nào bạn có thể thực hiện trong danh sách liên kết đôi, bạn cũng có thể thực hiện trên danh sách liên kết đơn, tình cờ có một vài thao tác (như di chuyển ngược qua danh sách) có mức lớn hơn nhiều O với phiên bản liên kết đôi.


6

Lý do thường không phải là một ý tưởng tốt để làm điều này là vì nó vi phạm Nguyên tắc đảo ngược phụ thuộc . Mọi người đã viết rất nhiều về điều này chi tiết hơn nhiều so với tôi có thể trình bày đầy đủ trong bài viết này, nhưng nó làm cho nó khó khăn để duy trì, bởi vì khớp nối quá chặt chẽ. Thay đổi một trong hai lớp hầu như luôn luôn cần một sự thay đổi trong lớp kia, trong khi nếu các phụ thuộc chỉ chỉ một chiều, các thay đổi ở một bên của giao diện sẽ bị cô lập. Nếu cả hai lớp chỉ đến một giao diện trừu tượng, thậm chí tốt hơn.

Một ngoại lệ chính là khi bạn không có hai lớp khác nhau ở các mức độ trừu tượng khác nhau, nhưng hai nút của cùng một lớp, chẳng hạn như trong một cây, một danh sách liên kết đôi, v.v ... Ở đây có nhiều mối quan hệ cấu trúc hơn là một mối quan hệ trừu tượng. Tài liệu tham khảo thông tư vì lợi ích của hiệu quả thuật toán được chấp nhận và thậm chí được khuyến khích trong các trường hợp này.


5

[...] Đề nghị ngay cả đối với các biểu đồ không làm điều gì đó như thế này.

Đôi khi bạn chỉ cần truy cập mọi thứ theo kiểu từ dưới lên từ một cấu trúc dữ liệu khác với cây, trong khi cây cần truy cập mọi thứ theo cách từ trên xuống.

Ví dụ, một phần tư có thể lưu trữ các phần tử trong phần mềm đồ họa vector. Tuy nhiên, lựa chọn của người dùng được lưu trữ trong một danh sách lựa chọn riêng biệt của các tham chiếu / con trỏ phần tử vector. Khi người dùng muốn xóa lựa chọn đó, chúng tôi phải cập nhật tứ giác, và có thể sẽ hiệu quả hơn rất nhiều khi cập nhật cây theo kiểu từ dưới lên bắt đầu từ lá thay vì từ trên xuống. Nếu không, bạn phải, cho mỗi phần tử, làm việc từ gốc đến lá và sau đó sao lưu lại.


1
Vâng, ví dụ này thực sự thích hợp. Chính xác là những điều tôi đã cố gắng mô tả trong lập luận của tôi về điều hướng trong một hỗn hợp: con trỏ ngược tăng tốc đáng kể tốc độ. Cảm ơn cho giai thoại hiệu suất thú vị của bạn và sơ đồ rất rõ ràng! +1
Barshe

@Christophe Tôi cũng thích ví dụ của bạn! Tôi không chắc liệu tôi có thực sự trả lời đúng câu hỏi không vì có lẽ nó liên quan đến "quyền sở hữu vòng tròn" hơn là chỉ các con trỏ ngược cho phép chúng ta vượt qua cấu trúc dữ liệu lên / xuống. Nhưng tôi chủ yếu trả lời phần "đồ thị" cuối cùng của câu hỏi.

2

Doom 3 có một ví dụ về một đối tượng con có con trỏ tới đối tượng cha. Cụ thể nó sử dụng danh sách xâm nhập . Tóm lại, một danh sách xâm nhập giống như một danh sách được liên kết ngoại trừ mỗi nút chứa một con trỏ tới chính danh sách đó.

Ưu điểm:

  • Khi các đối tượng có thể tồn tại trong một số danh sách cùng một lúc, bộ nhớ cho các nút danh sách cần được phân bổ và giải phóng chỉ một lần.

  • Khi một đối tượng cần phải bị hủy, bạn có thể dễ dàng xóa nó khỏi tất cả các danh sách mà nó không cần tìm kiếm trong từng danh sách một cách tuyến tính.

Tôi nghĩ rằng đây là một kịch bản khá cụ thể, nhưng nếu tôi hiểu câu hỏi của bạn, thì đó là một ví dụ về việc sử dụng chấp nhận được một đối tượng con có chứa một con trỏ tới đối tượng cha của nó.

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.