2010-05-13 80 views
5

Đối với dự án học kỳ của tôi, nhóm của tôi và tôi phải tạo một tệp .jar (thư viện, không chạy được) có chứa khung phát triển trò chơi và thể hiện các khái niệm về OOP. Nó được cho là một FRAMEWORK và một nhóm khác được cho là sử dụng khuôn khổ của chúng tôi và ngược lại. Vì vậy, tôi muốn biết làm thế nào chúng ta nên bắt đầu. Chúng tôi nghĩ rằng một số phương pháp:
1. Bắt đầu với một lớp đồng bằngLớp hoặc Lớp cơ sở trừu tượng?

public class Enemy { 
    public Enemy(int x, int y, int health, int attack, ...) { 
     ... 
    } 
    ... 
} 
public class UserDefinedClass extends Enemy { 
    ... 
} 

2. Bắt đầu với một lớp trừu tượng mà kẻ thù người dùng định nghĩa phải kế thừa các thành viên trừu tượng

public abstract class Enemy { 
    public Enemy(int x, int y, int health, int attack, ...) { 
     ... 
    } 
    public abstract void draw(); 
    public abstract void destroy(); 
    ... 
} 
public class UserDefinedClass extends Enemy { 
    ... 
    public void draw() { 
     ... 
    } 
    public void destroy() { 
     ... 
    } 
} 

3. Tạo siêu ABC (Lớp cơ sở trừu tượng) mà TẤT CẢ được kế thừa từ

public abstract class VectorEntity { 
    ... 
} 
public abstract class Enemy extends VectorEntity { 
    ... 
} 
public class Player extends VectorEntity { 
    ... 
} 
public class UserDefinedClass extends Enemy { 
    ... 
} 

Tôi nên sử dụng cái nào? đây có phải là cách tốt hơn không?

Trả lời

6

Vâng, có một chút khó khăn để nói chắc chắn mà không biết chiều sâu những gì bạn đang làm, và thậm chí sau đó nó khá chủ quan. Tuy nhiên, có một số điều cần xem xét có thể cho bạn biết.

  1. Họ sẽ thực sự khởi tạo một Kẻ thù hay làm tất cả kẻ thù thực sự cần phải thuộc loại có nguồn gốc? Nếu bạn không thực sự sẽ khởi tạo Enemies chứ không phải là các kiểu có nguồn gốc, thì nó có thể là giao diện hoặc một lớp trừu tượng.

  2. Nếu bạn đang tìm cách cung cấp hành vi thực tế trong lớp cơ sở của bạn, thì rõ ràng nó cần phải là một lớp chứ không phải là một giao diện.

  3. Các phương pháp cần phải có cho API nhưng không có ý nghĩa gì đối với bạn để cung cấp bất kỳ triển khai nào phải trừu tượng.

  4. Các phương pháp có ý nghĩa tốt khi triển khai chúng trong lớp cơ sở cần phải triển khai trong lớp cơ sở. Và nếu nó không có ý nghĩa tốt cho họ để được ghi đè, sau đó làm cho họ cuối cùng.

  5. Làm cho các lớp học chia sẻ một lớp cơ sở chung thực sự chỉ có ý nghĩa nếu chúng thực sự chia sẻ hành vi hoặc bạn cần để có thể xử lý tất cả chúng ở đâu đó trong mã của bạn. Nếu chúng không thực sự giống nhau, thì có lẽ chúng không nên chia sẻ một lớp cơ sở. Ví dụ, nếu cả Enemy và Player được cho là có thể được hiển thị, nó có thể có ý nghĩa để có một lớp cơ sở chung xử lý chức năng hiển thị của chúng. Nhưng nếu Enemy là thứ có thể hiển thị được, và Player là một khái niệm trừu tượng hơn - giống như bộ điều khiển trò chơi - và không thể hiển thị được, thì có lẽ sẽ không có ý nghĩa gì nếu họ chia sẻ một lớp cơ sở. Nói chung, tốt hơn là nên tạo bố cục hơn là thừa kế khi xây dựng lớp, vì vậy nếu các lớp được đề cập không thực sự chia sẻ hành vi và không thực sự có mối quan hệ "là-a" với lớp cơ sở chung thì họ không nên chia sẻ một lớp cơ sở chung.

  6. Ưu tiên các lớp cơ sở của bạn chỉ chia sẻ phương thức chứ không phải dữ liệu. Nói cách khác, trong một cây thừa kế, tốt nhất là chỉ có lá có thể tức thời. Có nhiều thứ khác nhau như equals() bị hỏng khi bạn có các lớp cơ sở với dữ liệu thực tế trong chúng. Đó không phải là để nói rằng bạn không thể làm điều đó - mọi người làm điều đó tất cả các thời gian - nhưng nó có thể gây ra vấn đề và tốt nhất là tránh nếu nó không cần thiết.

  7. Ưu tiên ghi đè phương pháp trừu tượng. Nếu không, trong các lớp dẫn xuất, bạn có nguy cơ không gọi phương thức của lớp cơ sở hoặc thay đổi hoàn toàn phương thức của nó.

Tôi chắc chắn rằng tôi có thể nghĩ ra nhiều hơn, nhưng không thực sự quen thuộc với dự án của bạn, nó nhất thiết phải là chung chung. Trong số 3 lựa chọn mà bạn đưa ra, tôi có thể đi với 2. 3 có vẻ như bạn có thể đang tạo ra một lớp cơ sở cho các lớp không liên quan, và 1 sẽ dẫn đến Enemy là instantiatable, mà bạn có thể không cần và chắc chắn sẽ làm cho nó để nhiều hơn các lá trong hệ thống phân cấp thừa kế của bạn sẽ là instantiatable. Có thể bạn vẫn sẽ kết thúc với dữ liệu trong các lớp cơ sở với 2, nhưng bạn có nhiều khả năng chỉ ghi đè các phương thức trừu tượng và bạn sẽ gặp ít vấn đề hơn với hành vi thay đổi trong các lớp dẫn xuất.

0

Quy tắc ứng xử của tôi là miễn là có nhiều lớp chia sẻ cùng một hoạt động/dữ liệu/phương pháp/chức năng, chúng phải là phần mở rộng của cùng lớp trừu tượng.

Vì vậy, nếu nó là tôi làm việc đó:

  • Nếu TẤT CẢ các lớp đều có một điểm chung, đã một top-level abstract class rằng tập hợp này chức năng/lĩnh vực/dữ liệu ở một nơi.
  • Nếu không, chỉ những lớp thực sự có điểm chung nên mở rộng cấp thấp hơn abstract class.

Nếu chỉ có các phương pháp là những gì các lớp sẽ có chung, interface cũng có thể được sử dụng. Tuy nhiên, tôi luôn thấy rằng sớm hay muộn tôi thấy rằng các lớp triển khai interface có cùng các trường private. Tại thời điểm này, tôi chuyển đổi interface thành abstract class giữ các trường riêng tư này (để lưu trên các dòng mã nếu không có gì khác).

1

Tùy chọn thứ tư sẽ là sử dụng giao diện.

interface Enemy { 

    public void draw(); 

    . . . 

} 

Nếu bạn mới bắt đầu, tôi sẽ tránh tùy chọn thứ ba của bạn. Hãy để khuôn khổ phát triển một chút và xem liệu có cần thiết cho nó hay không.

0

Chỉ cần một câu trả lời nhỏ ra khỏi cuốn sách "hiệu quả hơn C++" trang 271:

"Make lớp cơ sở trừu tượng mà không phải là ở phần cuối của một hệ thống phân cấp". Tôi quá lười biếng để cung cấp cho bạn toàn bộ chapert, nhưng autor liệt kê một số lý do tốt cho điều đó.

+0

Ngoài ra còn có "Java hiệu dụng" được áp dụng nhiều hơn trong trường hợp này. Nó nói một cái gì đó tương tự nhưng với những thứ cụ thể hơn cho Java - giống như cách 'equals()' không hoạt động hoàn toàn đúng khi trộn các kiểu có nguồn gốc và các kiểu cơ sở nếu các lớp cơ sở không phải là tất cả trừu tượng. –

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