Saya membuat permainan papan (seperti catur) di Jawa, di mana masing-masing bagian adalah jenisnya sendiri (seperti Pawn
, Rook
dll.). Untuk bagian GUI dari aplikasi saya perlu gambar untuk masing-masing bagian ini. Sejak melakukan berpikir seperti
rook.image();
melanggar pemisahan UI dan logika bisnis, saya akan membuat presenter yang berbeda untuk masing-masing bagian dan kemudian memetakan jenis potongan ke penyaji yang sesuai seperti
private HashMap<Class<Piece>, PiecePresenter> presenters = ...
public Image getImage(Piece piece) {
return presenters.get(piece.getClass()).image();
}
Sejauh ini baik. Namun, saya merasakan guru OOP yang bijaksana akan mengerutkan kening ketika memanggil getClass()
metode dan akan menyarankan menggunakan pengunjung misalnya seperti ini:
class Rook extends Piece {
@Override
public <T> T accept(PieceVisitor<T> visitor) {
return visitor.visitRook(this);
}
}
class ImageVisitor implements PieceVisitor<Image> {
@Override
public Image visitRook(Rook rook) {
return rookImage;
}
}
Saya suka solusi ini (terima kasih, guru), tetapi memiliki satu kelemahan signifikan. Setiap kali tipe bagian baru ditambahkan ke aplikasi, PieceVisitor perlu diperbarui dengan metode baru. Saya ingin menggunakan sistem saya sebagai kerangka permainan papan di mana potongan-potongan baru dapat ditambahkan melalui proses sederhana di mana pengguna kerangka kerja hanya akan memberikan implementasi baik karya dan presenternya, dan cukup tancapkan ke kerangka kerja. Pertanyaan saya: apakah ada solusi OOP yang bersih tanpa instanceof
, getClass()
dll. Yang memungkinkan perluasan jenis ini?
sumber
Jawaban:
Ya ada.
Izinkan saya bertanya kepada Anda. Dalam contoh Anda saat ini, Anda menemukan cara untuk memetakan jenis potongan ke gambar. Bagaimana cara mengatasi masalah sepotong yang dipindahkan?
Teknik yang lebih kuat daripada bertanya tentang tipe adalah mengikuti Tell, jangan bertanya . Bagaimana jika masing-masing bagian menggunakan
PiecePresenter
antarmuka dan tampilannya seperti ini:Konstruksi akan terlihat seperti ini:
Gunakan akan terlihat seperti:
Idenya di sini adalah untuk menghindari mengambil tanggung jawab untuk melakukan apa pun yang hal-hal lain bertanggung jawab dengan tidak bertanya tentang hal itu atau membuat keputusan berdasarkan itu. Alih-alih pegang referensi untuk sesuatu yang tahu apa yang harus dilakukan tentang sesuatu dan katakan itu untuk melakukan sesuatu tentang apa yang Anda ketahui.
Ini memungkinkan terjadinya polimorfisme. Anda tidak PEDULI dengan apa yang Anda bicarakan. Anda tidak peduli apa yang harus dikatakan. Anda hanya peduli bahwa itu dapat melakukan apa yang perlu Anda lakukan.
Diagram yang baik yang membuat ini di lapisan yang terpisah, mengikuti tell-don't-ask, dan menunjukkan bagaimana untuk tidak memasangkan layer ke layer secara tidak adil adalah ini :
Ia menambahkan lapisan use case yang belum pernah kami gunakan di sini (dan tentu saja dapat menambahkan) tetapi kami mengikuti pola yang sama seperti yang Anda lihat di sudut kanan bawah.
Anda juga akan melihat bahwa Presenter tidak menggunakan warisan. Ini menggunakan komposisi. Warisan harus menjadi cara terakhir untuk mendapatkan polimorfisme. Saya lebih suka desain yang disukai menggunakan komposisi dan delegasi. Mengetik keyboard sedikit lebih banyak tetapi lebih banyak daya.
sumber
Bagaimana dengan itu:
Model Anda (kelas angka) memiliki metode umum yang mungkin Anda perlukan dalam konteks lain juga:
Gambar yang akan digunakan untuk menampilkan gambar tertentu mendapatkan nama file dengan skema penamaan:
Kemudian Anda dapat memuat gambar yang sesuai tanpa mengakses informasi tentang kelas java.
Saya pikir Anda tidak harus fokus pada kelas sebanyak itu. Alih-alih berpikir dalam hal objek bisnis .
Dan solusi generik adalah pemetaan apa pun. Trik IMHO adalah memindahkan pemetaan itu dari kode ke sumber daya yang lebih mudah dipelihara.
Contoh saya melakukan pemetaan ini dengan konvensi yang cukup mudah diimplementasikan dan menghindari menambahkan informasi terkait tampilan ke dalam model bisnis . Di sisi lain Anda dapat menganggapnya sebagai pemetaan "tersembunyi" karena tidak diungkapkan di mana pun.
Pilihan lain adalah melihat ini sebagai kasus bisnis terpisah dengan lapisan MVC-nya sendiri termasuk lapisan ketekunan yang berisi pemetaan.
sumber
Saya akan membuat UI / view class yang terpisah untuk setiap bagian yang berisi informasi visual. Setiap salah satu dari kelas tampilan ini memiliki penunjuk ke model / rekan bisnisnya yang berisi posisi dan aturan permainan potongan tersebut.
Jadi, ambil pion misalnya:
Ini memungkinkan untuk pemisahan logika dan UI sepenuhnya. Anda bisa meneruskan pointer potongan logika ke kelas game yang akan menangani pemindahan potongan. Satu-satunya kelemahan adalah bahwa Instansiasi harus terjadi di kelas UI.
sumber
Piece* p
. Bagaimana saya tahu bahwa saya harus membuatPawnView
untuk menampilkannya, dan bukan aRookView
atauKingView
? Atau apakah saya harus membuat tampilan atau presenter yang menyertainya segera setiap kali saya membuat Karya baru? Itu pada dasarnya akan menjadi solusi @ CandiedOrange dengan dependensi terbalik. Dalam hal ini,PawnView
konstruktor juga dapat mengambilPawn*
, bukan hanya aPiece*
.Saya akan mendekati ini dengan membuat
Piece
generik, di mana parameternya adalah jenis enumerasi yang mengidentifikasi jenis potongan, masing-masing bagian memiliki referensi ke satu jenis tersebut. Kemudian UI dapat menggunakan peta dari enumerasi seperti sebelumnya:Ini memiliki dua keunggulan menarik:
Pertama, berlaku untuk sebagian besar bahasa yang diketik secara statis: Jika Anda menentukan papan Anda dengan tipe potongan untuk xpect, Anda tidak dapat memasukkan jenis potongan yang salah ke dalamnya.
Kedua, dan mungkin lebih menarik, jika Anda bekerja di Jawa (atau bahasa JVM lainnya), Anda harus mencatat bahwa setiap nilai enum bukan hanya objek independen, tetapi juga dapat memiliki kelasnya sendiri. Ini berarti bahwa Anda dapat menggunakan objek jenis potongan Anda sebagai objek Strategi untuk memberi pelanggan perilaku potongan:
(Jelas implementasi yang sebenarnya harus lebih kompleks dari itu, tetapi mudah-mudahan Anda mendapatkan ide)
sumber
Saya seorang programmer pragmatis dan saya benar-benar tidak peduli apa itu arsitektur yang bersih atau kotor. Saya percaya persyaratan dan itu harus ditangani dengan cara sederhana.
Persyaratan Anda adalah logika aplikasi catur Anda akan diwakili di berbagai lapisan presentasi (perangkat) seperti di web, aplikasi seluler, atau bahkan aplikasi konsol sehingga Anda perlu mendukung persyaratan ini. Anda mungkin lebih suka menggunakan warna yang sangat berbeda, membuat gambar pada setiap perangkat.
Seperti yang Anda lihat, parameter presenter harus diteruskan pada setiap perangkat (lapisan presentasi) secara berbeda. Itu berarti lapisan presentasi Anda akan memutuskan bagaimana untuk mewakili setiap bagian. Apa yang salah dalam solusi ini?
sumber
Ada solusi lain yang akan membantu Anda untuk abstrak UI dan logika domain sepenuhnya. Papan Anda harus diekspos ke lapisan UI Anda dan lapisan UI Anda dapat memutuskan bagaimana cara mewakili bagian dan posisi.
Untuk mencapai ini, Anda dapat menggunakan string Fen . String Fen pada dasarnya adalah informasi keadaan papan dan memberikan potongan saat ini dan posisi mereka di papan tulis. Jadi papan Anda dapat memiliki metode yang mengembalikan keadaan papan saat ini melalui string Fen maka lapisan UI Anda dapat mewakili papan sesuai keinginan. Inilah sebenarnya cara kerja mesin catur saat ini. Mesin catur adalah aplikasi konsol tanpa GUI tetapi kami menggunakannya melalui GUI eksternal. Mesin catur berkomunikasi dengan GUI melalui string fen dan notasi catur.
Anda bertanya bagaimana jika saya menambahkan bagian baru? Tidak realistis bahwa catur akan memperkenalkan karya baru. Itu akan menjadi perubahan besar di domain Anda. Jadi ikuti prinsip YAGNI.
sumber