Chìa khóa ở đây không chỉ là sự tách biệt các mối quan tâm , mà còn là nguyên tắc trách nhiệm duy nhất . Hai mặt cơ bản là hai mặt khác nhau của cùng một đồng tiền: khi tôi nghĩ SOC tôi nghĩ từ trên xuống (tôi có những mối quan tâm này, làm cách nào để tách chúng ra?) Trong khi SRP có nhiều từ dưới lên (tôi có đối tượng này, nó có mối quan tâm duy nhất? Có nên chia tách? Mối quan tâm của nó đã bị chia tách quá nhiều chưa?).
Trong ví dụ của bạn, bạn có các thực thể sau và trách nhiệm của chúng:
- Trò chơi: đây là mã làm cho chương trình "đi".
- GameBoard: duy trì trạng thái của khu vực chơi.
- Thẻ: một thực thể duy nhất trên bảng trò chơi.
- Người chơi: thực hiện các hành động thay đổi trạng thái của bảng trò chơi.
Khi bạn nghĩ về trách nhiệm duy nhất của mỗi thực thể, các dòng trở nên rõ ràng hơn.
Trong một ứng dụng như trò chơi, có một lớp chính chạy vòng lặp chính, chẳng hạn như Chương trình hoặc Trò chơi. Câu hỏi của tôi là, tôi có duy trì mọi tham chiếu đến mọi phiên bản của một lớp trong lớp này không và biến nó thành cách duy nhất để chúng tương tác?
Thực sự có hai vấn đề ở đây cần ghi nhớ. Điều đầu tiên để quyết định là những gì thực thể biết về các thực thể khác? Những thực thể thuộc về các thực thể khác?
Nhìn vào những trách nhiệm tôi đã nêu ở trên. Người chơi thực hiện các hành động thay đổi trạng thái của bảng trò chơi. Nói cách khác, Người chơi gửi tin nhắn đến (gọi phương thức) trên bảng trò chơi. Những tin nhắn đó có khả năng liên quan đến thẻ: ví dụ: người chơi có thể đặt thẻ trên tay lên bảng hoặc thay đổi trạng thái của thẻ hiện tại (ví dụ: lật thẻ hoặc chuyển thẻ sang vị trí mới).
Rõ ràng, một người chơi phải biết về bảng trò chơi mâu thuẫn với giả định bạn đưa ra trong câu hỏi của mình. Nếu không, người chơi phải gửi tin nhắn đến trò chơi, sau đó chuyển tiếp tin nhắn đó đến bảng trò chơi. Vì người chơi thực hiện các hành động trên bảng trò chơi, người chơi phải biết về bảng trò chơi. Điều này làm tăng tính khớp nối: thay vì người chơi gửi tin nhắn trực tiếp, bây giờ hai diễn viên phải biết cách gửi tin nhắn đó. Các Luật của Demeter ngụ ý rằng nếu một đối tượng phải hành động trên đối tượng khác, trong trường hợp này, đối tượng khác cần được thông qua vào thông qua tham số để giảm khớp nối.
Tiếp theo, bạn lưu trữ nhà nước ở đâu? Trò chơi là trình điều khiển ở đây, nó phải tạo ra tất cả các đối tượng trực tiếp hoặc thông qua proxy (ví dụ: một nhà máy hoặc trong một nhà xây dựng mà trò chơi gọi). Câu hỏi tiếp theo hợp lý là những đối tượng cần những đối tượng khác? Về cơ bản, đây là những gì tôi đã hỏi ở trên, nhưng một cách khác để hỏi nó.
Cách tôi sẽ thiết kế nó là như thế này:
Trò chơi tạo ra tất cả các đối tượng cần thiết cho trò chơi.
Trò chơi xáo trộn các thẻ và chia chúng cho mỗi trò chơi mà nó đại diện (poker, solitaire, v.v.).
Trò chơi đặt các thẻ vào vị trí ban đầu của chúng: có thể một số trên bảng trò chơi, một số khác trong tay người chơi.
Trò chơi sau đó đi vào vòng lặp chính của nó đại diện cho một lượt.
Mỗi lượt sẽ như thế này:
Trò chơi gửi một thông điệp tới (gọi một phương thức trên) trình phát hiện tại và cung cấp một tham chiếu đến bảng trò chơi.
Trình phát thực hiện bất kỳ logic bên trong (trình phát máy tính) hoặc tương tác người dùng cần thiết để xác định những gì chơi để thực hiện.
Người chơi gửi tin nhắn đến bảng trò chơi được cung cấp cho nó để yêu cầu thay đổi trạng thái của bảng trò chơi.
Ban trò chơi quyết định xem di chuyển có hợp lệ hay không (có trách nhiệm duy trì trạng thái trò chơi hợp lệ).
Kiểm soát trở lại trò chơi, sau đó quyết định làm gì tiếp theo. Kiểm tra điều kiện thắng? Vẽ tranh? Người chơi tiếp theo? Lượt tiếp theo? Phụ thuộc vào trò chơi thẻ cụ thể đang được chơi.
Nếu nó thuộc vào lớp Trò chơi để đặt các thẻ lên bảng hoặc điều đó có ý nghĩa hơn, bởi vì đó là hành động của người chơi, nên nó nằm trong lớp Người chơi.
Cả hai: Game chịu trách nhiệm thiết lập ban đầu, nhưng Người chơi thực hiện các hành động trên bảng. GameBoard chịu trách nhiệm đảm bảo trạng thái hợp lệ. Ví dụ, trong Solitaire cổ điển, chỉ có thẻ trên cùng trên một cọc có thể ngửa lên.
Quay lại quan điểm ban đầu của tôi: bạn có những mối quan tâm đúng đắn. Bạn đã xác định các đối tượng thích hợp. Điều khiến bạn vấp ngã là tìm ra cách các thông điệp truyền qua hệ thống và đối tượng nào sẽ giữ các tham chiếu đến các đối tượng khác. Tôi sẽ thiết kế nó như thế này, đó là mã giả:
class Game {
main();
}
class GameBoard {
// Data structures specific to the game being played. There is a
// lot of hand-waving here to give the general idea without
// getting bogged down in the implementation.
Map<Card, Location> cards;
GameBoard(Map<Card, Location>);
// Return false if the move is invalid.
bool flip(Card);
bool move(Card, Location);
}
class Card {
// Make Rank and Suit enums.
Suit suit;
Rank rank;
bool faceUp;
}
class Player {
Set<Card> hand;
Player(Set<Card>);
void takeTurn(GameBoard);
}