Tôi có nên tránh sử dụng kế thừa đối tượng càng tốt để phát triển trò chơi không?


36

Tôi thích các tính năng OOP khi phát triển trò chơi với Unity. Tôi thường tạo một lớp cơ sở (chủ yếu là trừu tượng hóa) và sử dụng kế thừa đối tượng để chia sẻ cùng chức năng với các đối tượng khác.

Tuy nhiên, gần đây tôi đã nghe từ một người nào đó sử dụng kế thừa phải tránh và thay vào đó chúng ta nên sử dụng giao diện. Vì vậy, tôi đã hỏi tại sao, và anh ấy nói rằng "Tất nhiên sự kế thừa đối tượng là quan trọng, nhưng nếu có nhiều đối tượng mở rộng, mối quan hệ giữa các lớp được gắn kết sâu sắc.

Tôi đang sử dụng một lớp cơ sở trừu tượng, một cái gì đó giống như WeaponBasevà tạo lớp vũ khí cụ thể như Shotgun, AssaultRifle, Machinegunmột cái gì đó như thế. Có rất nhiều lợi thế, và một mô hình tôi thực sự thích là đa hình. Tôi có thể coi đối tượng được tạo bởi các lớp con như thể chúng là lớp cơ sở, để logic có thể được giảm đáng kể và có thể tái sử dụng nhiều hơn. Nếu tôi cần truy cập vào các trường hoặc phương thức của lớp con, tôi sẽ quay trở lại lớp con.

Tôi không muốn xác định cùng một lĩnh vực, thường được sử dụng trong các lớp học khác nhau, như AudioSource/ Rigidbody/ Animatorvà rất nhiều lĩnh vực thành viên Tôi định nghĩa như thế fireRate, damage, range, spreadvà vân vân. Ngoài ra trong một số trường hợp, một số phương pháp có thể được ghi đè. Vì vậy, tôi đang sử dụng các phương thức ảo trong trường hợp đó, để về cơ bản tôi có thể gọi phương thức đó bằng cách sử dụng phương thức từ lớp cha, nhưng nếu logic sẽ khác ở trẻ, tôi đã ghi đè chúng theo cách thủ công.

Vì vậy, tất cả những điều này nên được từ bỏ như là thực hành xấu? Tôi có nên sử dụng giao diện thay thế?


30
"Có rất nhiều lợi thế, và một mô hình tôi thực sự thích là đa hình." Đa hình không phải là một mô hình. Đó thực sự là toàn bộ quan điểm của OOP. Những thứ khác như các trường chung v.v có thể dễ dàng đạt được mà không cần sao chép mã hoặc kế thừa.
Khối

3
Có phải người đề xuất giao diện của bạn tốt hơn kế thừa cho bạn bất kỳ đối số nào không? Tôi tò mò.
Hawker65

1
@Cubic Tôi chưa bao giờ nói rằng đa hình là một mô hình. Tôi nói "... một mô hình tôi thực sự thích là đa hình". Điều đó có nghĩa là mẫu tôi thực sự thích là sử dụng "đa hình", không phải đa hình là mẫu.
đại

4
Mọi người chào hàng bằng cách sử dụng các giao diện trên sự kế thừa trong bất kỳ hình thức phát triển nào. Tuy nhiên, nó có xu hướng thêm nhiều chi phí, sự bất đồng về nhận thức, dẫn đến sự bùng nổ của lớp, thường thêm giá trị đáng ngờ và thường không kết hợp tốt trong một hệ thống trừ khi MỌI NGƯỜI trong dự án tập trung vào các giao diện. Vì vậy, đừng từ bỏ thừa kế. Tuy nhiên, các trò chơi có một số đặc điểm độc đáo và kiến ​​trúc của Unity mong bạn sử dụng mô hình thiết kế CES, đó là lý do tại sao bạn nên điều chỉnh cách tiếp cận của mình. Đọc loạt Wizards and Warriors của Eric Lippert mô tả các vấn đề thuộc loại trò chơi.
Dunk

2
Tôi muốn nói rằng một giao diện là một trường hợp kế thừa cụ thể trong đó lớp cơ sở không chứa các trường dữ liệu. Tôi nghĩ đề xuất là cố gắng không xây dựng một cây khổng lồ trong đó mỗi đối tượng phải được phân loại, mà thay vào đó là rất nhiều cây nhỏ dựa trên các giao diện nơi bạn có thể tận dụng nhiều sự kế thừa. Đa hình không bị mất với các giao diện.
Emanuele Paolini

Câu trả lời:


65

Thành phần ưu tiên hơn sự kế thừa trong hệ thống thực thể và hàng tồn kho / vật phẩm của bạn. Lời khuyên này có xu hướng áp dụng cho các cấu trúc logic trò chơi, khi cách bạn có thể lắp ráp những thứ phức tạp (trong thời gian chạy) có thể dẫn đến rất nhiều kết hợp khác nhau; đó là khi chúng ta thích sáng tác.

Ưu tiên kế thừa thành phần trong logic cấp ứng dụng của bạn, mọi thứ từ cấu trúc UI đến các dịch vụ. Ví dụ,

Widget->Button->SpinnerButton hoặc là

ConnectionManager->TCPConnectionManagervs ->UDPConnectionManager.

... Có một hệ thống phân cấp phái sinh được xác định rõ ràng ở đây, chứ không phải là vô số các dẫn xuất tiềm năng, vì vậy việc sử dụng kế thừa sẽ dễ dàng hơn.

Dòng dưới cùng : sử dụng sự kế thừa nơi bạn có thể, nhưng sử dụng thành phần mà bạn phải. PS Lý do khác mà chúng tôi có thể ủng hộ thành phần trong các hệ thống thực thể là thường có nhiều thực thể và kế thừa có thể phải chịu chi phí hiệu năng để truy cập các thành viên trên mọi đối tượng; xem vtables .


10
Kế thừa không ngụ ý rằng tất cả các phương thức sẽ là ảo. Vì vậy, nó không tự động dẫn đến một chi phí hiệu suất. VTable chỉ đóng vai trò trong việc gửi các cuộc gọi ảo , không truy cập các thành viên dữ liệu.
Ruslan

1
@Ruslan Tôi đã thêm các từ 'may' và 'can' để chứa bình luận của bạn. Mặt khác, vì lợi ích của việc giữ câu trả lời ngắn gọn, tôi đã không đi sâu vào chi tiết. Cảm ơn.
Kỹ sư

7
P.S. The other reason we may favour composition in entity systems is ... performance: Điều này có thực sự đúng không? Nhìn vào trang WIkipedia được liên kết bởi @Linaith cho thấy, bạn cần phải soạn đối tượng giao diện của mình. Do đó, bạn có (vẫn hoặc thậm chí nhiều hơn) các cuộc gọi chức năng ảo và nhiều lỗi nhớ cache hơn, như bạn đã giới thiệu một mức độ gián tiếp khác.
Ngọn lửa

1
@Flamefire Vâng, đó là sự thật; có nhiều cách để sắp xếp dữ liệu của bạn sao cho hiệu quả bộ nhớ cache của bạn là tối ưu, bất kể và chắc chắn là tối ưu hơn nhiều so với các chỉ định bổ sung đó. Chúng không phải là sự kế thừa và thành phần, mặc dù nhiều hơn theo nghĩa mảng-struct / struct-of-Array (dữ liệu xen kẽ / không xen kẽ trong sắp xếp tuyến tính bộ đệm), tách thành các mảng riêng biệt và đôi khi sao chép dữ liệu để khớp với các tập hợp hoạt động tại các phần khác nhau của đường ống của bạn. Nhưng một lần nữa, đi sâu vào vấn đề đó ở đây sẽ là một sự chuyển hướng rất lớn mà tốt nhất nên tránh vì sự can thiệp.
Kỹ sư

2
Hãy nhớ giữ bình luận cho các yêu cầu dân sự để làm rõ hoặc phê bình và tranh luận về nội dung của các ý tưởng và không phải là tính cách hoặc kinh nghiệm của người nêu rõ chúng.
Josh

18

Bạn đã có một vài câu trả lời hay, nhưng con voi to lớn trong phòng trong câu hỏi của bạn là câu này:

nghe được từ ai đó rằng phải sử dụng kế thừa phải tránh và thay vào đó chúng ta nên sử dụng giao diện

Như một quy tắc của ngón tay cái, khi ai đó đưa cho bạn một quy tắc của ngón tay cái, sau đó bỏ qua nó. Điều này không chỉ dành cho "ai đó nói với bạn điều gì đó", mà còn để đọc nội dung trên internet. Trừ khi bạn biết tại sao (và thực sự có thể đứng đằng sau nó), những lời khuyên như vậy là vô giá trị và thường rất có hại.

Theo kinh nghiệm của tôi, các khái niệm quan trọng và hữu ích nhất trong OOP là "khớp nối thấp" và "độ gắn kết cao" (các lớp / đối tượng biết càng ít càng tốt về nhau và mỗi đơn vị chịu trách nhiệm về càng ít điều càng tốt).

Khớp nối thấp

Điều này có nghĩa là bất kỳ "gói nội dung" nào trong mã của bạn nên phụ thuộc vào môi trường xung quanh càng ít càng tốt. Điều này áp dụng cho các lớp (thiết kế lớp) nhưng cũng có các đối tượng (triển khai thực tế), "tệp" nói chung (nghĩa là số #includes trên mỗi .cpptệp đơn , số lượng importtrên mỗi .javatệp đơn , v.v.).

Một dấu hiệu cho thấy hai thực thể được ghép nối là một trong số chúng sẽ bị phá vỡ (hoặc cần phải thay đổi) khi cái kia bị thay đổi theo bất kỳ cách nào.

Kế thừa tăng khớp nối, rõ ràng; thay đổi lớp cơ sở thay đổi tất cả các lớp con.

Các giao diện giảm khớp nối: bằng cách xác định hợp đồng rõ ràng, dựa trên phương pháp, bạn có thể tự do thay đổi mọi thứ về cả hai mặt của giao diện, miễn là bạn không thay đổi hợp đồng. (Lưu ý rằng "giao diện" là một khái niệm chung, các interfacelớp trừu tượng Java hoặc C ++ chỉ là chi tiết triển khai).

Độ kết dính cao

Điều này có nghĩa là để mỗi lớp, đối tượng, tệp, vv được quan tâm hoặc chịu trách nhiệm ít nhất có thể. Tức là tránh các lớp học lớn làm nhiều thứ. Trong ví dụ của bạn, nếu vũ khí của bạn có các khía cạnh hoàn toàn riêng biệt (đạn, hành vi bắn, biểu diễn đồ họa, biểu diễn kho, v.v.), thì bạn có thể có các lớp khác nhau đại diện chính xác cho một trong những điều đó. Lớp vũ khí chính sau đó biến thành "người nắm giữ" những chi tiết đó; một đối tượng vũ khí sau đó ít hơn một vài gợi ý đến những chi tiết đó.

Trong ví dụ này, bạn sẽ đảm bảo rằng lớp của bạn đại diện cho "Hành vi bắn" biết ít nhất có thể về con người về lớp vũ khí chính. Tối ưu, không có gì cả. Chẳng hạn, điều này có nghĩa là bạn có thể đưa ra "Hành vi bắn" cho bất kỳ đối tượng nào trong thế giới của bạn (tháp pháo, núi lửa, NPC ...) chỉ bằng một cái búng tay. Nếu đôi lúc bạn muốn thay đổi cách thể hiện vũ khí trong kho, thì bạn chỉ có thể làm như vậy - chỉ có lớp kho của bạn biết về điều đó.

Một dấu hiệu cho thấy một thực thể không gắn kết là nếu nó phát triển ngày càng lớn hơn, phân nhánh theo nhiều hướng cùng một lúc.

Kế thừa như bạn mô tả nó làm giảm sự gắn kết - các lớp vũ khí của bạn, vào cuối ngày, những khối lớn xử lý tất cả các khía cạnh khác nhau, không liên quan đến vũ khí của bạn.

Các giao diện gián tiếp tăng sự gắn kết bằng cách phân chia rõ ràng trách nhiệm giữa hai bên của giao diện.

làm gì bây giờ

Vẫn không có quy tắc cứng và nhanh, tất cả những điều này chỉ là hướng dẫn. Nói chung, như người dùng TKK đã đề cập trong câu trả lời của mình, kế thừa được dạy rất nhiều trong trường học và sách; đó là những thứ lạ mắt về OOP. Các giao diện có lẽ nhàm chán hơn để dạy, và cũng (nếu bạn đi qua các ví dụ tầm thường) khó hơn một chút, mở ra lĩnh vực tiêm phụ thuộc, không quá rõ ràng như thừa kế.

Vào cuối ngày, chương trình dựa trên thừa kế của bạn vẫn tốt hơn là không có thiết kế OOP rõ ràng nào cả. Vì vậy, cảm thấy tự do để gắn bó với nó. Nếu bạn muốn, bạn có thể suy nghĩ lại / google một chút về Kết nối thấp, Độ kết dính cao và xem liệu bạn có muốn thêm kiểu suy nghĩ đó vào kho vũ khí của mình không. Bạn luôn có thể cấu trúc lại để thử nó nếu bạn muốn, sau này; hoặc thử các cách tiếp cận dựa trên giao diện trên mô-đun mã mới lớn hơn tiếp theo của bạn.


Cảm ơn bạn đã giải thích chi tiết. Nó thực sự hữu ích để hiểu những gì tôi đang thiếu.
đại

13

Ý tưởng rằng kế thừa phải tránh chỉ đơn giản là sai.

Có tồn tại một nguyên tắc mã hóa được gọi là Thành phần trên Kế thừa . Nó nói rằng bạn có thể đạt được những điều tương tự với thành phần và tốt hơn là vì bạn có thể sử dụng lại một số mã. Xem tại sao tôi nên thích sáng tác hơn thừa kế?

Tôi phải nói rằng tôi thích các lớp vũ khí của bạn và nó sẽ làm theo cách tương tự. Nhưng bây giờ tôi chưa làm một trò chơi ...

Như James Trotter đã chỉ ra, bố cục có thể có một số lợi thế, đặc biệt là tính linh hoạt trong thời gian chạy để thay đổi cách thức hoạt động của vũ khí. Điều này sẽ có thể với thừa kế, nhưng nó khó khăn hơn.


14
Thay vì có các lớp cho vũ khí, bạn có thể có một lớp "vũ khí" duy nhất và các thành phần để xử lý việc bắn là gì, nó có kèm theo gì và chúng làm gì, "kho đạn" nào, bạn có thể xây dựng nhiều cấu hình , một số trong đó có thể là "súng ngắn" hoặc "súng trường tấn công", nhưng cũng có thể được thay đổi trong thời gian chạy, ví dụ như để tắt các tệp đính kèm và thay đổi dung lượng tạp chí, v.v. Hoặc toàn bộ cấu hình súng mới có thể được tạo ra thậm chí còn nghĩ đến ban đầu. Vâng, bạn có thể đạt được một cái gì đó tương tự với thừa kế, nhưng không dễ dàng như vậy.
Trotski94

3
@JamesTrotter Tại thời điểm này, tôi nghĩ về Borderlands và súng của nó, và tự hỏi điều quái dị này sẽ là gì chỉ với sự thừa kế ...
Baldrickk

1
@JamesTrotter Tôi ước đó là một câu trả lời chứ không phải bình luận.
Pharap

6

Vấn đề là sự kế thừa dẫn đến khớp nối - các đối tượng của bạn cần biết nhiều hơn về nhau. Đó là lý do tại sao quy tắc là "Luôn ưu tiên sáng tác hơn thừa kế". Điều này không có nghĩa là KHÔNG BAO GIỜ sử dụng tính kế thừa, nó có nghĩa là sử dụng nó ở nơi hoàn toàn phù hợp, nhưng nếu bạn đã từng ngồi đó với suy nghĩ "Tôi có thể làm điều này theo cả hai cách và cả hai đều có ý nghĩa", chỉ cần đi thẳng vào sáng tác.

Kế thừa cũng có thể là loại hạn chế. Bạn có một "WeaponBase" có thể là AssultRifle - tuyệt vời. Điều gì xảy ra khi bạn có một khẩu súng ngắn hai nòng và muốn cho phép nòng súng bắn độc lập - khó hơn một chút nhưng có thể làm được, bạn chỉ có một loại nòng đơn và nòng đôi, nhưng bạn không thể lắp 2 nòng đơn trên cùng một khẩu súng, bạn có thể? Bạn có thể mod một để có 3 thùng hay bạn cần một lớp mới cho điều đó?

Điều gì về một AssultRifle với GrenadeLauncher bên dưới - hmm, khó khăn hơn một chút. Bạn có thể thay thế GrenadeLauncher bằng đèn pin để săn đêm không?

Cuối cùng, làm thế nào để bạn cho phép người dùng của bạn tạo ra các khẩu súng trước bằng cách chỉnh sửa tệp cấu hình? Điều này thật khó khăn vì các mối quan hệ được mã hóa cứng có thể được kết hợp và cấu hình tốt hơn với dữ liệu.

Nhiều kế thừa có thể khắc phục một số ví dụ tầm thường này, nhưng nó bổ sung thêm các vấn đề riêng.

Trước khi có những câu nói phổ biến như "Thích sáng tác hơn thừa kế", tôi đã phát hiện ra điều này bằng cách viết một cây thừa kế quá phức tạp (rất thú vị và hoạt động hoàn hảo) sau đó phát hiện ra rằng nó thực sự khó sửa đổi và duy trì sau này. Câu nói chỉ là để bạn có một cách khác và nhỏ gọn để học và ghi nhớ một khái niệm như vậy mà không cần phải trải qua toàn bộ trải nghiệm - nhưng nếu bạn muốn sử dụng thừa kế nhiều, tôi khuyên bạn chỉ nên giữ một tâm trí cởi mở và đánh giá nó hoạt động tốt như thế nào đối với bạn - không có gì khủng khiếp sẽ xảy ra nếu bạn sử dụng thừa kế nhiều và đó có thể là một trải nghiệm học tập tuyệt vời ở mức tồi tệ nhất (tốt nhất là nó có thể hoạt động tốt cho bạn)


2
"làm thế nào để bạn cho phép người dùng của bạn tạo ra các khẩu súng trước bằng cách chỉnh sửa tệp cấu hình?" -> Mẫu tôi thường làm là tạo một lớp cuối cùng cho mỗi vũ khí. Chẳng hạn, như Glock17. Đầu tiên tạo lớp WeaponBase. Lớp này được trừu tượng hóa, định nghĩa các giá trị phổ biến như chế độ lửa, tốc độ bắn, kích thước tạp chí, đạn tối đa, viên. Ngoài ra, nó xử lý đầu vào của người dùng, như mouse1 / mouse2 / tải lại và gọi phương thức tương ứng. Chúng gần như ảo hoặc được chia thành nhiều chức năng hơn để nhà phát triển có thể ghi đè chức năng cơ bản nếu cần. Và sau đó tạo một lớp khác mở rộng WeaponBase,
đại

2
một cái gì đó như SlideStoppableWeapon. Hầu hết các khẩu súng ngắn đều có thứ này và điều này xử lý các hành vi bổ sung như trượt dừng lại. Cuối cùng tạo lớp Glock17 mở rộng từ SlideStoppableWeapon. Glock17 là lớp cuối cùng, nếu khẩu súng có khả năng duy nhất chỉ có, cuối cùng được viết ở đây. Tôi thường sử dụng mẫu này khi súng có cơ chế bắn đặc biệt hoặc cơ chế nạp lại. Thực hiện hành vi độc đáo đó để kết thúc hierachy của lớp, kết hợp các logic chung càng nhiều càng tốt từ phụ huynh.
đại

2
@modernator Như tôi đã nói, đó chỉ là một hướng dẫn - nếu bạn không tin tưởng nó, hãy thoải mái bỏ qua nó, chỉ cần để mắt đến kết quả theo thời gian và đánh giá lợi ích so với đau thường xuyên. Tôi cố gắng nhớ những nơi tôi đã nhón chân và giúp người khác tránh điều tương tự, nhưng những gì hiệu quả với tôi có thể không hiệu quả với bạn.
Bill K

7
@modernator Trong Minecraft họ đã tạo Monstermột lớp con của WalkingEntity. Sau đó, họ thêm slime. Làm thế nào tốt để bạn cho rằng làm việc?
dùng253751

1
@immibis Bạn có tài liệu tham khảo hoặc liên kết đến vấn đề đó không? Các từ khóa minecraft của Google khiến tôi chìm đắm trong các liên kết video ;-)
Peter - Tái lập lại

3

Trái ngược với các câu trả lời khác, điều này không liên quan gì đến sự kế thừa so với thành phần. Kế thừa so với thành phần là một quyết định bạn đưa ra liên quan đến cách một lớp sẽ được thực hiện. Giao diện so với các lớp là một quyết định có trước đó.

Các giao diện là những công dân hạng nhất của bất kỳ ngôn ngữ OOP nào. Các lớp học là chi tiết thực hiện thứ cấp. Tâm trí của các lập trình viên mới bị biến dạng nghiêm trọng bởi các giáo viên và sách giới thiệu các lớp học và kế thừa trước các giao diện.

Nguyên tắc chính là bất cứ khi nào có thể, các loại của tất cả các tham số phương thức và giá trị trả về phải là các giao diện, không phải các lớp. Điều này làm cho API của bạn linh hoạt và mạnh mẽ hơn nhiều. Phần lớn thời gian, phương thức của bạn và người gọi của họ sẽ không có lý do để biết hoặc quan tâm đến các lớp thực tế của các đối tượng họ đang xử lý. Nếu API của bạn không biết về chi tiết triển khai, bạn có thể tự do chuyển đổi giữa thành phần và kế thừa mà không phá vỡ bất cứ điều gì.


Nếu các giao diện là công dân hạng nhất của bất kỳ ngôn ngữ OOP nào thì tôi tự hỏi liệu Python, Ruby, không phải là ngôn ngữ OOP.
BlackJack

@BlackJack: Trong Ruby, giao diện ẩn (gõ vịt nếu bạn muốn) và được thể hiện bằng cách sử dụng bố cục thay vì thừa kế (cũng có Mixins ở đâu đó ở giữa, theo như tôi nghĩ) ...
AnoE

@BlackJack "Giao diện" (khái niệm) không yêu cầu interface(từ khóa ngôn ngữ).
Caleth

2
@Caleth Nhưng gọi họ là công dân hạng nhất thì IMHO. Khi một mô tả ngôn ngữ tuyên bố một cái gì đó là công dân hạng nhất, điều đó thường có nghĩa là nó là một thứ có thật trong ngôn ngữ đó có một loại và giá trị và có thể được truyền qua. Giống như các lớp là công dân hạng nhất trong Python, cũng như các hàm, phương thức và mô-đun. Nếu chúng ta đang nói về khái niệm này, thì giao diện cũng là một phần của tất cả các ngôn ngữ không phải OOP.
BlackJack

2

Bất cứ khi nào ai đó nói với bạn rằng một phương pháp cụ thể là tốt nhất cho mọi trường hợp, thì cũng giống như nói với bạn rằng một và cùng một loại thuốc chữa được tất cả các bệnh.

Kế thừa so với thành phần là một câu hỏi is-a vs has-a. Giao diện là một cách khác (thứ 3), thích hợp cho một số tình huống.

Kế thừa hoặc logic là một logic: bạn sử dụng nó khi lớp mới sẽ hành xử và được sử dụng hoàn toàn giống như (bên ngoài) lớp cũ mà bạn bắt nguồn từ đó, nếu lớp mới sẽ tiếp xúc với công chúng tất cả các chức năng mà lớp cũ có ... sau đó bạn kế thừa.

Thành phần hoặc logic có logic: Nếu lớp mới chỉ cần sử dụng nội bộ lớp cũ, mà không để lộ các tính năng của lớp cũ cho công chúng, thì bạn sử dụng thành phần - nghĩa là có một thể hiện của lớp cũ là một tài sản thành viên hoặc một biến của cái mới. (Đây có thể là một tài sản riêng, hoặc được bảo vệ, hoặc bất cứ điều gì, tùy thuộc vào trường hợp sử dụng). Điểm mấu chốt ở đây là đây là trường hợp sử dụng mà bạn không muốn phơi bày các tính năng của lớp gốc và sử dụng cho công chúng, chỉ sử dụng nó trong nội bộ, trong trường hợp thừa kế bạn phơi bày chúng ra công chúng, thông qua lớp mới. Đôi khi bạn cần một cách tiếp cận, và đôi khi khác.

Các giao diện: Các giao diện dành cho một trường hợp sử dụng khác - khi bạn muốn lớp mới thực hiện một phần và không hoàn toàn thực hiện và hiển thị cho công chúng chức năng của lớp cũ. Điều này cho phép bạn có một lớp mới, lớp từ một hệ thống phân cấp hoàn toàn khác với lớp cũ, chỉ hoạt động như lớp cũ trong một số khía cạnh.

Giả sử bạn có các loại sinh vật được đại diện bởi các lớp và chúng có chức năng được đại diện bởi các chức năng. Ví dụ, một con chim sẽ có Talk (), Fly () và Poop (). Lớp Vịt sẽ kế thừa từ lớp Chim, thực hiện tất cả các tính năng.

Lớp Fox, rõ ràng, không thể bay. Vì vậy, nếu bạn xác định tổ tiên có tất cả các tính năng, thì bạn không thể lấy được con cháu đúng cách.

Tuy nhiên, nếu bạn chia các tính năng thành các nhóm, đại diện cho từng nhóm cuộc gọi bằng giao diện, giả sử, có chứa Takeoff (), FlapWings () và Land (), thì bạn có thể cho lớp Fox thực hiện các chức năng từ ITalk và IPoop nhưng không phải là IFly. Sau đó, bạn sẽ xác định các biến và tham số để chấp nhận các đối tượng triển khai một giao diện cụ thể và sau đó mã làm việc với chúng sẽ biết nó có thể gọi gì ... và luôn có thể truy vấn các giao diện khác, nếu nó cần xem các chức năng khác cũng có thực hiện cho các đối tượng hiện tại.

Mỗi cách tiếp cận này đều có các trường hợp sử dụng khi đó là cách tốt nhất, không có cách tiếp cận nào là giải pháp tốt nhất tuyệt đối cho mọi trường hợp.


1

Đối với một trò chơi, đặc biệt là với Unity, hoạt động với kiến ​​trúc thành phần thực thể, bạn nên ưu tiên bố cục hơn sự kế thừa cho các thành phần trò chơi của bạn. Nó linh hoạt hơn nhiều và tránh gặp phải tình huống bạn muốn một thực thể trò chơi "trở thành" hai thứ nằm trên những chiếc lá khác nhau của một cây thừa kế.

Vì vậy, ví dụ, giả sử bạn có TalkingAI trên một nhánh của cây thừa kế và VolumetricCloud trên một nhánh riêng khác và bạn muốn có một đám mây biết nói. Điều này là khó khăn với một cây thừa kế sâu sắc. Với thành phần thực thể, bạn chỉ cần tạo một thực thể có các thành phần TalkingAI và Cloud và bạn sẽ ổn.

Nhưng điều đó không có nghĩa là trong quá trình triển khai đám mây thể tích của bạn, bạn không nên sử dụng tính kế thừa. Mã cho điều đó có thể là đáng kể và bao gồm một số lớp và bạn có thể sử dụng OOP khi cần thiết cho nó. Nhưng tất cả sẽ lên tới một thành phần trò chơi duy nhất.

Là một lưu ý phụ, tôi có một số vấn đề với điều này:

Tôi thường tạo một lớp cơ sở (chủ yếu là trừu tượng hóa) và sử dụng kế thừa đối tượng để chia sẻ cùng chức năng với các đối tượng khác.

Kế thừa không phải là để tái sử dụng mã. Đó là để thiết lập một mối quan hệ. Rất nhiều thời gian đi đôi với nhau, nhưng bạn phải cẩn thận. Chỉ vì hai mô-đun có thể cần sử dụng cùng một mã, điều đó không có nghĩa là một mô-đun cùng loại với nhau.

Bạn có thể sử dụng các giao diện nếu bạn muốn, có một List<IWeapon>loại vũ khí khác nhau trong đó. Bằng cách này, bạn có thể kế thừa từ cả giao diện IWeapon và lớp con MonoBehaviour cho tất cả các vũ khí và tránh mọi vấn đề khi không có nhiều kế thừa.


-3

Bạn dường như không hiểu giao diện là gì hoặc làm gì. Tôi đã mất hơn 10 năm suy nghĩ về OO và hỏi một số người thực sự thông minh là tôi có thể có một khoảnh khắc ah-ha với các giao diện. Hi vọng điêu nay co ich.

Tốt nhất là sử dụng kết hợp chúng. Trong tâm trí OO của tôi mô hình hóa thế giới và mọi người cho phép, một số ví dụ sâu sắc. Linh hồn được giao thoa với cơ thể thông qua tâm trí. Tâm trí là giao diện giữa linh hồn (một số người xem xét trí tuệ) và các mệnh lệnh mà nó đưa ra cho cơ thể. Giao diện tâm trí cho cơ thể là giống nhau cho tất cả mọi người, nói theo chức năng.

Sự tương tự tương tự có thể được sử dụng giữa người (thể xác và linh hồn) giao thoa với thế giới. Cơ thể là giao diện giữa tâm trí và thế giới. Mọi người đều giao tiếp với thế giới theo cùng một cách, sự độc đáo duy nhất là CÁCH chúng ta tương tác với thế giới, đó là cách chúng ta sử dụng MIND để quyết định cách chúng ta giao tiếp / tương tác trên thế giới.

Một giao diện sau đó chỉ đơn giản là một cơ chế để liên kết cấu trúc OO của bạn với một cấu trúc OO khác khác với mỗi bên có các thuộc tính khác nhau phải thương lượng / ánh xạ với nhau để tạo ra một số đầu ra chức năng trong một môi trường / môi trường khác nhau.

Vì vậy, để tâm trí gọi hàm mở, nó phải triển khai giao diện neur_command_system với THAT có API riêng với hướng dẫn cách thực hiện tất cả các yêu cầu cần thiết trước khi gọi mở.

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.