Sekali waktu yang lalu saya mengajukan pertanyaan tentang Stack Overflow tentang warisan.
Saya telah mengatakan saya merancang mesin catur dalam mode OOP. Jadi saya mewarisi semua karya saya dari kelas abstrak Piece tetapi warisan tetap berjalan. Biarkan saya tunjukkan dengan kode
public abstract class Piece
{
public void MakeMove();
public void TakeBackMove();
}
public abstract class Pawn: Piece {}
public class WhitePawn :Pawn {}
public class BlackPawn:Pawn {}
Programmer telah menemukan desain saya sedikit di atas teknik dan menyarankan untuk menghapus kelas potongan berwarna dan tahan warna sepotong sebagai anggota properti seperti di bawah ini.
public abstract class Piece
{
public Color Color { get; set; }
public abstract void MakeMove();
public abstract void TakeBackMove();
}
Dengan cara ini Piece bisa tahu warnanya. Setelah implementasi saya melihat implementasi saya berjalan seperti di bawah ini.
public abstract class Pawn: Piece
{
public override void MakeMove()
{
if (this.Color == Color.White)
{
}
else
{
}
}
public override void TakeBackMove()
{
if (this.Color == Color.White)
{
}
else
{
}
}
}
Sekarang saya melihat bahwa properti color keep menyebabkan pernyataan if dalam implementasi. Itu membuat saya merasa kami membutuhkan potongan warna khusus dalam hierarki warisan.
Dalam kasus seperti itu, apakah Anda memiliki kelas seperti WhitePawn, BlackPawn atau Anda akan mendesain dengan mempertahankan properti Color?
Tanpa melihat masalah seperti itu bagaimana Anda ingin memulai desain? Menyimpan properti Color atau memiliki solusi pewarisan?
Sunting: Saya ingin menentukan bahwa contoh saya mungkin tidak sepenuhnya cocok dengan contoh kehidupan nyata. Jadi sebelum mencoba menebak detail implementasi, konsentrasikan saja pertanyaannya.
Saya sebenarnya hanya bertanya apakah menggunakan properti Warna akan menyebabkan apakah pernyataan lebih baik untuk menggunakan warisan?
Jawaban:
Bayangkan Anda merancang aplikasi yang berisi kendaraan. Apakah Anda membuat sesuatu seperti ini?
Dalam kehidupan nyata, warna adalah properti dari objek (baik mobil atau bidak catur), dan harus diwakili oleh properti.
Apakah
MakeMove
danTakeBackMove
berbeda untuk orang kulit hitam dan kulit putih? Tidak sama sekali: peraturannya sama untuk kedua pemain, yang berarti bahwa kedua metode itu akan sama persis untuk orang kulit hitam dan kulit putih , yang berarti Anda menambahkan kondisi di mana Anda tidak membutuhkannya.Di sisi lain, jika Anda punya
WhitePawn
danBlackPawn
, saya tidak akan terkejut jika cepat atau lambat Anda akan menulis:atau bahkan sesuatu seperti:
sumber
direction
properti dan menggunakannya untuk bergerak daripada menggunakan properti warna. Warna idealnya hanya digunakan untuk rendering, bukan untuk logika.direction
properti itu boolean. Saya tidak melihat apa yang salah dengan itu. Yang membingungkan saya tentang pertanyaan sampai komentar Freshblood di atas adalah mengapa ia ingin mengubah logika gerakan berdasarkan warna. Saya akan mengatakan itu lebih sulit untuk dipahami karena tidak masuk akal untuk warna mempengaruhi perilaku. Jika yang ingin Anda lakukan adalah membedakan pemain mana yang memiliki bagian yang Anda dapat menempatkannya dalam dua koleksi terpisah dan menghindari kebutuhan akan properti atau warisan. Potongan-potongan itu sendiri bisa sangat tidak menyadari pemain mana yang mereka miliki.direction
atau mungkinplayer
properti daripada yangcolor
ada di logika permainan.white castle's moves differ from black's
- bagaimana? Apakah maksud Anda castling? Baik King-side castling dan Queen-side castling 100% simetris untuk Hitam Putih.Yang harus Anda tanyakan pada diri sendiri adalah mengapa Anda benar-benar membutuhkan pernyataan itu di kelas Gadai Anda:
Saya cukup yakin itu akan cukup untuk memiliki seperangkat fungsi kecil seperti
di kelas Piece Anda dan terapkan semua fungsi di
Pawn
(dan derivasi lain dariPiece
) menggunakan fungsi-fungsi tersebut, tanpa harus memilikiif
pernyataan di atas. Hanya pengecualian yang berfungsi untuk menentukan arah gerakan untuk Gadai berdasarkan warnanya, karena ini khusus untuk pion, tetapi dalam hampir semua kasus lain Anda tidak memerlukan kode khusus warna.Pertanyaan menarik lainnya adalah apakah Anda benar-benar harus memiliki subclass yang berbeda untuk berbagai jenis potongan sama sekali. Itu tampak masuk akal dan alami bagi saya, karena aturan untuk gerakan untuk setiap bagian berbeda dan Anda pasti dapat menerapkan ini dengan menggunakan fungsi kelebihan beban yang berbeda untuk setiap jenis bagian.
Tentu saja, orang bisa mencoba model ini dengan cara yang lebih generik, dengan memisahkan properti dari potongan-potongan dari aturan bergerak mereka, tetapi ini mungkin berakhir di kelas abstrak
MovingRule
dan hirarki subclassMovingRulePawn
,MovingRuleQueen
... saya tidak akan memulainya bahwa cara, karena bisa dengan mudah mengarah ke bentuk lain dari rekayasa berlebihan.sumber
Warisan tidak berguna ketika ekstensi tidak memengaruhi kemampuan eksternal objek .
Properti Color tidak memengaruhi cara objek Piece digunakan . Sebagai contoh, semua bagian dapat menggunakan metode moveForward atau moveBackwards (bahkan jika langkah tersebut tidak sah), tetapi logika enkapsulasi Piece menggunakan properti Color untuk menentukan nilai absolut yang mewakili gerakan maju atau mundur (-1 atau + 1 pada "sumbu y"), sejauh menyangkut API Anda, superclass dan subkelas Anda adalah objek yang identik (karena semuanya memperlihatkan metode yang sama).
Secara pribadi, saya akan mendefinisikan beberapa konstanta yang mewakili setiap jenis bagian, kemudian menggunakan pernyataan beralih untuk bercabang ke metode pribadi yang mengembalikan kemungkinan gerakan untuk contoh "ini".
sumber
Board
kelas (misalnya) bisa bertanggung jawab untuk mengubah konsep ini menjadi -1 atau +1 tergantung pada warna dari potongan.Secara pribadi saya tidak akan mendapatkan apa pun dari
Piece
semuanya, karena saya pikir Anda tidak mendapatkan apa-apa dari melakukan itu. Agaknya sepotong tidak benar-benar peduli bagaimana bergerak, hanya kotak yang merupakan tujuan yang valid untuk langkah selanjutnya.Mungkin Anda bisa membuat Kalkulator Bergerak Valid yang mengetahui pola pergerakan yang tersedia untuk jenis keping, dan dapat mengambil daftar kotak tujuan yang valid, misalnya
sumber
Piece
sama sekali". Tampaknya Piece bertanggung jawab atas lebih dari sekadar menghitung gerakan, yang merupakan alasan untuk memisahkan gerakan sebagai masalah terpisah. Menentukan bagian mana yang mendapatkan kalkulator mana yang merupakan masalah ketergantungan injeksi, misalnyavar my_knight = new Piece(new KnightMoveCalculator())
Dalam jawaban ini, saya berasumsi bahwa dengan "mesin catur", penulis pertanyaan berarti "catur AI", bukan hanya mesin validasi bergerak.
Saya pikir menggunakan OOP untuk bagian dan gerakan valid mereka adalah ide yang buruk karena dua alasan:
Beranjak dari pertimbangan kinerja, termasuk warna bagian dalam hirarki jenis menurut saya adalah ide yang buruk. Anda akan menemukan diri Anda dalam situasi di mana Anda perlu memeriksa apakah dua potong memiliki warna yang berbeda, misalnya untuk menangkap. Memeriksa kondisi ini mudah dilakukan menggunakan properti. Melakukannya menggunakan
is WhitePiece
-test akan lebih buruk dalam perbandingan.Ada juga alasan praktis lain untuk menghindari perpindahan generasi yang bergerak ke metode khusus, yaitu bagian yang berbeda memiliki gerakan yang sama, misalnya benteng dan ratu. Untuk menghindari kode duplikat, Anda harus memfaktisasi kode umum menjadi metode dasar, dan melakukan panggilan mahal lainnya.
Yang sedang berkata, jika itu mesin catur pertama yang Anda tulis, saya pasti akan menyarankan untuk pergi dengan prinsip pemrograman bersih dan menghindari trik optimasi yang dijelaskan oleh penulis Crafty. Berurusan dengan aturan catur yang spesifik (castling, promotion, en-passant) dan teknik AI yang kompleks (hashing board, alpha-beta cut) cukup menantang.
sumber
WhichMovesArePossible
adalah pendekatan yang masuk akal.