Kuncinya di sini bukan hanya pemisahan keprihatinan , tetapi juga prinsip tanggung jawab tunggal . Keduanya pada dasarnya adalah sisi yang berbeda dari koin yang sama: ketika saya berpikir SOC saya pikir top-down (saya punya masalah ini, bagaimana saya memisahkan mereka?) Sementara SRP lebih bottom-up (saya punya objek ini, apakah ada kekhawatiran tunggal? Haruskah dibelah? Apakah kekhawatirannya sudah terlalu banyak?).
Dalam contoh Anda, Anda memiliki entitas berikut dan tanggung jawabnya:
- Game: ini adalah kode yang membuat program "pergi."
- GameBoard: mempertahankan kondisi area bermain.
- Kartu: satu entitas di papan permainan.
- Player: melakukan tindakan yang mengubah status papan permainan.
Setelah Anda memikirkan tanggung jawab tunggal masing-masing entitas, garis menjadi lebih jelas.
Dalam aplikasi seperti game, ada kelas utama yang menjalankan loop utama, seperti Program atau Game. Pertanyaan saya adalah, apakah saya mempertahankan setiap referensi untuk setiap instance dari kelas di kelas ini, dan menjadikannya satu-satunya cara mereka berinteraksi?
Sebenarnya ada dua masalah di sini yang perlu diingat. Hal pertama yang harus diputuskan adalah entitas apa yang tahu tentang entitas lain? Entitas mana yang milik entitas lain?
Lihatlah tanggung jawab yang saya uraikan di atas. Pemain melakukan tindakan yang mengubah status papan permainan. Dengan kata lain, Pemain mengirim pesan ke (memanggil metode pada) papan permainan. Pesan-pesan itu kemungkinan melibatkan kartu: misalnya, pemain dapat meletakkan kartu di tangannya di papan, atau mengubah status kartu yang ada (misalnya membalikkan kartu atau memindahkannya ke lokasi baru).
Jelas, seorang pemain harus tahu tentang papan permainan yang bertentangan dengan asumsi yang Anda buat dalam pertanyaan Anda. Kalau tidak, pemain harus mengirim pesan ke permainan, yang kemudian menyampaikan pesan itu ke papan permainan. Karena pemain melakukan aksi di papan permainan, pemain harus tahu tentang papan permainan. Ini meningkatkan penggabungan: alih-alih pemain yang mengirim pesan secara langsung, sekarang dua aktor harus tahu cara mengirim pesan itu. The Law of Demeter menyiratkan bahwa jika sebuah objek harus bertindak pada objek lain, dalam skenario ini, bahwa objek lain harus disahkan pada melalui parameter untuk mengurangi coupling.
Selanjutnya, di mana Anda menyimpan status apa? Gim adalah penggeraknya di sini, ia harus menggulung semua objek baik secara langsung atau melalui proksi (mis. Pabrik atau konstruktor yang dipanggil gim). Pertanyaan logis selanjutnya adalah objek apa yang membutuhkan objek lain? Ini pada dasarnya apa yang saya minta di atas, tetapi cara yang berbeda untuk menanyakannya.
Cara saya mendesainnya adalah seperti ini:
Game menciptakan semua objek yang diperlukan untuk game.
Game mengocok kartu dan membaginya menurut permainan apa pun yang diwakilinya (poker, solitaire, dll).
Game menempatkan kartu di lokasi awal mereka: mungkin beberapa di papan permainan, yang lain di tangan pemain.
Game kemudian masuk ke loop utama yang mewakili giliran.
Setiap belokan akan terlihat seperti ini:
Game mengirim pesan ke (memanggil metode pada) pemain saat ini dan memberikan referensi ke papan permainan.
Player melakukan logika internal apa saja (pemutar komputer) atau interaksi pengguna yang diperlukan untuk menentukan permainan apa yang akan dilakukan.
Pemain mengirim pesan ke papan permainan yang disediakan untuk itu meminta untuk mengubah status papan permainan.
Board game memutuskan apakah langkah tersebut valid atau tidak ( bertanggung jawab untuk mempertahankan status game yang valid).
Kontrol kembali ke permainan, yang kemudian memutuskan apa yang harus dilakukan selanjutnya. Periksa kondisi menang? Seri? Pemain berikutnya? Giliran berikutnya? Tergantung pada permainan kartu tertentu yang sedang dimainkan.
Jika tergantung pada kelas Game untuk meletakkan kartu di papan, atau apakah lebih masuk akal bahwa, karena itu adalah tindakan pemain, itu harus berada di dalam kelas Player.
Keduanya: Game bertanggung jawab untuk pengaturan awal, tetapi Player melakukan tindakan di papan tulis. GameBoard bertanggung jawab untuk menjamin keadaan yang valid. Sebagai contoh, dalam Solitaire klasik hanya kartu teratas pada tumpukan yang dapat menghadap ke atas.
Kembali ke poin awal saya: Anda memiliki pemisahan kekhawatiran yang tepat. Anda mengidentifikasi objek yang tepat. Yang membuat Anda tersandung adalah mencari tahu bagaimana pesan mengalir melalui sistem dan objek mana yang harus menyimpan referensi ke objek lain. Saya akan mendesainnya seperti ini, yaitu pseudocode:
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);
}