Đây có phải là thiết kế OOP xấu cho một mô phỏng liên quan đến giao diện?


13

Tôi đang thiết kế chương trình OOP nhỏ của riêng mình để mô phỏng Ma cà rồng, Người sói, Con người và Xe tải và đang cố gắng thực hiện sự hiểu biết hạn chế của riêng tôi về Giao diện.

( Tôi vẫn đang trừu tượng hóa ở đây và chưa có triển khai mã nào, vì vậy đây là một câu hỏi về thiết kế OOP ... tôi nghĩ vậy!)

Tôi có đúng không khi tìm kiếm 'hành vi chung' giữa các lớp này và thực hiện chúng dưới dạng giao diện ?

Ví dụ, Ma cà rồng và Chó sói cắn ... vậy tôi có nên có giao diện cắn không?

public class Vampire : Villain, IBite, IMove, IAttack

Tương tự như vậy đối với Xe tải ...

public class Truck : Vehicle, IMove

Và cho con người ...

public class Man : Human, IMove, IDead

Là suy nghĩ của tôi ngay tại đây? (Đánh giá cao sự giúp đỡ của bạn)


14
Động vật, rau và khoáng chất hiếm khi làm ví dụ tốt cho việc triển khai ứng dụng. Triển khai thực tế nói chung là trừu tượng hơn, như IEnumerable, IEquatablevv
Robert Harvey

6
Bạn có một đề cập duy nhất về những gì đối tượng của bạn sắp làm trong phần mềm của bạn ("cắn"). Phần mềm thường được thiết kế để làm một cái gì đó, dựa trên một mô hình đối tượng chỉ dựa trên các đặc điểm không dẫn đến bất cứ đâu.
tofro

@tofro Ý định của tôi là IBite sẽ chứa nhiều phương thức thực hiện hành vi liên quan đến (1) Việc giảm mức độ 'năng lượng / năng lượng' của người khác (2) Sự xuất hiện hoặc gọi đồ họa của 'máu' và (3) cập nhật các thống kê mô phỏng dữ liệu (như NoOfBites). Tôi nghĩ rằng tôi có thể đánh giá cao rằng một giao diện được sử dụng tốt nhất để thực hiện một loạt các hành vi phương thức.
dùng3396486

2
Không có lớp Người, Ma cà rồng và Xe đã triển khai giao diện IMove chưa? Tại sao bạn cần phải làm cho các lớp con thực hiện nó quá rõ ràng?
Pierre Arlaud

Có phải tất cả các giao diện này thực sự cần thiết? Trong Python may mắn là bạn không cần bất kỳ thứ gì trong số này, ehich là một thay đổi thực sự mới mẻ (ngôn ngữ đầu tiên của tôi là Object Pascal). Ngoài ra phương pháp ảo có thể là một giải pháp tốt hơn trong một số trường hợp.
Ajasja

Câu trả lời:


33

Nói chung, bạn muốn có các giao diện cho các đặc điểm chung của clasess của bạn.

Tôi hoàn toàn đồng ý với @Robert Harvey trong các bình luận, người nói rằng các giao diện thường đại diện cho các tính năng trừu tượng hơn của các lớp. Tuy nhiên, tôi thấy bắt đầu từ những ví dụ cụ thể hơn là một cách tốt để bắt đầu suy nghĩ trừu tượng.

Trong khi ví dụ của bạn là đúng về mặt kỹ thuật (tức là có, cả ma cà rồng và chó sói đều cắn, vì vậy bạn có thể có một giao diện cho điều đó), có một câu hỏi liên quan. Mỗi đối tượng có hàng ngàn đặc điểm (ví dụ động vật có thể có lông, có thể bơi, có thể trèo cây, v.v.). Bạn sẽ làm một giao diện cho tất cả chúng? Rất ít khả năng.

Bạn thường muốn các giao diện cho những thứ có ý nghĩa được nhóm lại trong một ứng dụng nói chung. Ví dụ: nếu bạn đang xây dựng một trò chơi, bạn có thể có một mảng các đối tượng IMove và cập nhật vị trí của chúng. Nếu bạn không muốn làm điều đó, có giao diện IMove là khá vô dụng.

Vấn đề là, đừng quá kỹ sư. Bạn cần suy nghĩ về cách bạn sẽ sử dụng giao diện đó và 2 lớp có một phương thức chung không phải là lý do đủ tốt để tạo giao diện.


1
Tôi chắc chắn hy vọng mỗi đối tượng không có hàng ngàn thuộc tính.
vườn

4
Các thuộc tính không như trong các thuộc tính oop mà là các thuộc tính / đặc điểm ngữ pháp (những thứ như liệt kê, so sánh, v.v.): D. Lựa chọn từ ngữ xấu.
Paul92

3
Đáng lưu ý rằng các giao diện hữu ích là những giao diện bạn sẽ sử dụng. Ví dụ, IBitekhông đặc biệt hữu ích, nhưng bạn có thể muốn IAttackđể bạn có thể xử lý tất cả những thứ tạo ra các cuộc tấn công hoặc IUpdateđể bạn có thể chạy các bản cập nhật cho mọi thứ hoặc IPhysicsEnabledđể bạn có thể áp dụng vật lý cho chúng, v.v.
anaximander

1
Câu trả lời này nêu lên một số điểm rất tốt. Đoạn cuối tổng hợp nó khá tốt; ít nhất cũng như bạn có thể với mức độ chi tiết được cung cấp.
Cuộc đua nhẹ nhàng với Monica

1
nhóm phương thức commons phù hợp hơn cho các lớp trừu tượng. Giao diện được thực hiện để thiết kế các hợp đồng phải tôn trọng những người thực hiện nó, chứ không phải nhóm thực hiện tương tự cho một số đối tượng.
Walfrat

28

Có vẻ như bạn đang tạo ra một loạt các giao diện phương thức duy nhất . Điều này là tốt trên mặt của nó nhưng hãy nhớ rằng các giao diện không thuộc sở hữu của các lớp / es thực hiện chúng. Họ được sở hữu bởi các khách hàng sử dụng chúng. Các khách hàng quyết định nếu một cái gì đó cần phải là một cái gì đó có thể di chuyển và tấn công.

Nếu tôi có một Combatlớp với một fight()phương thức, phương thức đó có thể có nhu cầu gọi cả hai move()attack()trên cùng một đối tượng. Điều đó cho thấy mạnh mẽ cần một ICombatantgiao diện fight()có thể gọi move()attack()thông qua. Điều này sạch hơn là fight()lấy một IAttackvật thể và đúc nó IMoveđể xem liệu nó cũng có thể di chuyển.

Điều đó không có nghĩa là bạn cũng không thể có IMove IAttackgiao diện. Tôi chỉ hy vọng bạn không tạo ra chúng mà không có khách hàng cần chúng. Ngược lại, nếu không có khách hàng nào cần phải làm cho một đối tượng vừa di chuyển vừa tấn công thì ICombatantkhông cần thiết.

Cách nhìn đơn giản này về giao diện thường bị mất vì mọi người thích các ví dụ sau. Các giao diện đầu tiên chúng tôi tiếp xúc là trong các thư viện. Thật không may, các thư viện không có ý tưởng gì về khách hàng của họ. Vì vậy, họ chỉ có thể đoán nhu cầu khách hàng của họ. Không phải là ví dụ tốt nhất để làm theo.


1
Chết tiệt này là tốt. Chơi game có vẻ như là một cách thực sự tốt để sử dụng và giải thích OOP.
JeffO

6
@JeffO cho đến khi bạn thực sự thực hiện một trò chơi lớn một cách hợp lý và nhận ra rằng OOP là một mớ hỗn độn nóng bỏng và bạn sẽ tốt hơn với các hệ thống dựa trên thành phần hoặc thiết kế hướng dữ liệu.
Darkhogg

"Các giao diện được sở hữu bởi các khách hàng sử dụng chúng"
Tibos


1
+1 cho sự khác biệt giữa các thư viện và ứng dụng, tôi thường (quá nhiều ?: /) Đọc hàng tấn thứ chỉ phù hợp với cái này và không phải cái khác.
Walfrat

3

Xem xét liệu có phổ biến để có các bộ sưu tập các đối tượng với các kết hợp khả năng khác nhau hay không và liệu mã có thể muốn thực hiện một hành động đối với các mục đó, trong một bộ sưu tập, có hỗ trợ nó không . Nếu vậy, và nếu có một "hành vi mặc định" hợp lý cho các đối tượng không có hỗ trợ hữu ích cho một số hành động, thì có thể hữu ích khi các giao diện được triển khai bởi một loạt các lớp, không chỉ các hành vi có thể hành xử hữu ích.

Ví dụ, giả sử chỉ có một vài loại sinh vật có thể có Woozles và người ta muốn những sinh vật đó có NumerOfWoozlestài sản. Nếu một tài sản như vậy nằm trong một giao diện chỉ được thực hiện bởi các sinh vật có thể có Woozles, thì mã muốn tìm tổng số Woozles được giữ bởi một bộ sưu tập các loại sinh vật hỗn hợp sẽ phải nói điều gì đó như:

int total = 0;
foreach (object it in creatures)
{
   IWoozleCountable w = trycast(it, IWoozleCountable);
   if (w != null) total += w.WoozleCount;
}

Tuy nhiên, nếu WoozleCount là thành viên của Creature / ICreature, mặc dù một vài kiểu con sẽ ghi đè lên triển khai WoozleCount mặc định của Creature luôn trả về 0, mã có thể được đơn giản hóa thành:

int total = 0;
foreach (ICreature it in creatures)
   total += it.WoozleCount;

Mặc dù một số người có thể an tâm với ý tưởng rằng mọi Sinh vật đều thực hiện một thuộc tính WoozleCount thực sự chỉ hữu ích cho một vài kiểu con, nhưng thuộc tính đó sẽ có ý nghĩa đối với tất cả các loại, cho dù nó có hữu ích với các loại vật phẩm đó hay không, và tôi sẽ coi giao diện "bồn rửa nhà bếp" là ít mùi mã hơn so với toán tử trycast.

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.