Dua antarmuka dengan tanda tangan identik


13

Saya mencoba membuat model permainan kartu di mana kartu memiliki dua set fitur penting:

Yang pertama adalah efek. Ini adalah perubahan kondisi permainan yang terjadi saat Anda memainkan kartu. Antarmuka untuk efek adalah sebagai berikut:

boolean isPlayable(Player p, GameState gs);
void play(Player p, GameState gs);

Dan Anda dapat menganggap kartu tersebut dapat dimainkan jika dan hanya jika Anda dapat memenuhi biayanya dan semua efeknya dapat dimainkan. Seperti itu:

// in Card class
boolean isPlayable(Player p, GameState gs) {
    if(p.resource < this.cost) return false;
    for(Effect e : this.effects) {
        if(!e.isPlayable(p,gs)) return false;
    }
    return true;
}

Oke, sejauh ini, cukup sederhana.

Set fitur lain pada kartu adalah kemampuan. Kemampuan ini adalah perubahan kondisi permainan yang dapat Anda aktifkan sesuka hati. Ketika datang dengan antarmuka untuk ini, saya menyadari mereka membutuhkan metode untuk menentukan apakah mereka dapat diaktifkan atau tidak, dan metode untuk menerapkan aktivasi. Akhirnya menjadi

boolean isActivatable(Player p, GameState gs);
void activate(Player p, GameState gs);

Dan saya menyadari bahwa dengan pengecualian menyebutnya "aktifkan" daripada "bermain", Abilitydan Effectmemiliki tanda tangan yang sama persis.


Apakah buruk memiliki banyak antarmuka dengan tanda tangan yang identik? Haruskah saya menggunakan satu saja, dan memiliki dua set antarmuka yang sama? Seperti itu:

Set<Effect> effects;
Set<Effect> abilities;

Jika demikian, langkah refactoring apa yang harus saya ambil jika tidak menjadi identik (karena lebih banyak fitur dilepaskan), terutama jika mereka berbeda (yaitu keduanya mendapatkan sesuatu yang seharusnya tidak dimiliki oleh orang lain, sebagai lawan dari hanya satu yang mendapatkan dan yang lainnya menjadi subset lengkap)? Saya khususnya khawatir bahwa menggabungkan mereka akan menjadi tidak berkelanjutan segera setelah sesuatu berubah.

Cetak halus:

Saya menyadari pertanyaan ini muncul karena pengembangan game, tetapi saya merasa ini adalah jenis masalah yang bisa dengan mudah muncul dalam pengembangan non-game, terutama ketika mencoba mengakomodasi model bisnis dari beberapa klien dalam satu aplikasi seperti yang terjadi pada setiap proyek yang pernah saya lakukan dengan lebih dari satu pengaruh bisnis ... Juga, cuplikan yang digunakan adalah cuplikan Java, tetapi ini bisa dengan mudah diterapkan pada banyak bahasa berorientasi objek.


Cukup ikuti KISS dan YAGNI dan Anda harus baik-baik saja.
Bernard

2
Satu-satunya alasan pertanyaan ini muncul adalah karena fungsi Anda memiliki akses ke status yang terlalu banyak, karena parameter Player dan GameState yang sangat lebar dan tidak dibatasi.
Lars Viklund

Jawaban:


18

Hanya karena dua antarmuka memiliki kontrak yang sama, tidak berarti mereka memiliki antarmuka yang sama.

Prinsip Substitusi Liskov menyatakan:

Misalkan q (x) menjadi properti yang dapat dibuktikan tentang objek x dari tipe T. Kemudian q (y) harus dapat dibuktikan untuk objek y dari tipe S di mana S adalah subtipe dari T.

Atau, dengan kata lain: Apa pun yang benar dari antarmuka atau supertype harus benar dari semua subtipe-nya.

Jika saya memahami deskripsi Anda dengan benar, suatu kemampuan bukanlah suatu efek dan suatu efek bukanlah suatu kemampuan. Jika salah satu mengubah kontraknya, tidak mungkin yang lain akan berubah dengannya. Saya tidak melihat alasan yang baik untuk mencoba mengikat mereka satu sama lain.


2

Dari Wikipedia : " antarmuka sering digunakan untuk menentukan tipe abstrak yang tidak mengandung data, tetapi memperlihatkan perilaku yang didefinisikan sebagai metode ". Menurut pendapat saya antarmuka digunakan untuk menggambarkan suatu perilaku, jadi jika Anda memiliki perilaku yang berbeda masuk akal untuk memiliki antarmuka yang berbeda. Membaca pertanyaan Anda kesan yang saya dapatkan adalah bahwa Anda berbicara tentang perilaku yang berbeda sehingga antarmuka yang berbeda mungkin merupakan pendekatan terbaik.

Poin lain yang Anda katakan adalah jika salah satu dari perilaku itu berubah. Lalu apa yang terjadi ketika Anda hanya memiliki satu antarmuka?


1

Jika aturan permainan kartu Anda membuat perbedaan antara "efek" dan "kemampuan", Anda perlu memastikan bahwa mereka adalah antarmuka yang berbeda. Ini akan membuat Anda tidak sengaja menggunakan salah satu dari mereka di mana yang lain diperlukan.

Yang mengatakan, jika mereka sangat mirip, mungkin masuk akal untuk mendapatkan mereka dari leluhur yang sama. Pertimbangkan dengan hati-hati: apakah Anda memiliki alasan untuk percaya bahwa "efek" dan "kemampuan" akan selalu tentu memiliki antarmuka yang sama? Jika Anda menambahkan elemen ke effectantarmuka, apakah Anda mengharapkan elemen yang sama ditambahkan ke abilityantarmuka?

Jika demikian, maka Anda dapat menempatkan elemen-elemen tersebut di featureantarmuka yang sama dari mana keduanya berasal. Jika tidak, maka Anda sebaiknya tidak mencoba menyatukan mereka - Anda akan membuang waktu memindahkan barang-barang antara antarmuka dasar dan turunan. Namun, karena Anda tidak berniat untuk benar-benar menggunakan antarmuka dasar umum untuk apa pun kecuali "jangan ulangi diri Anda sendiri", itu mungkin tidak membuat banyak perbedaan. Dan, jika Anda tetap pada niat itu, tebakan saya adalah bahwa jika Anda membuat pilihan yang salah di awal, refactoring untuk memperbaikinya nanti mungkin relatif sederhana.


0

Apa yang Anda lihat pada dasarnya adalah artefak dari ekspresif terbatasnya sistem tipe.

Secara teoritis, jika sistem tipe Anda memungkinkan Anda menentukan perilaku kedua antarmuka tersebut secara akurat, maka tanda tangannya akan berbeda karena perilaku mereka berbeda. Secara praktis, ekspresifitas sistem tipe dibatasi oleh keterbatasan mendasar seperti Masalah Pemutusan dan Teorema Padi, sehingga tidak semua aspek perilaku dapat diekspresikan.

Sekarang, sistem tipe yang berbeda memiliki tingkat ekspresi yang berbeda, tetapi akan selalu ada sesuatu yang tidak dapat diungkapkan.

Sebagai contoh: dua antarmuka yang memiliki perilaku kesalahan yang berbeda mungkin memiliki tanda tangan yang sama di C #, tetapi tidak di Jawa (karena di Jawa pengecualian adalah bagian dari tanda tangan). Dua antarmuka yang perilakunya hanya berbeda dalam efek sampingnya mungkin memiliki tanda tangan yang sama di Jawa, tetapi akan memiliki tanda tangan yang berbeda di Haskell.

Jadi, selalu mungkin bahwa Anda akan berakhir dengan tanda tangan yang sama untuk perilaku yang berbeda. Jika menurut Anda penting untuk dapat membedakan antara dua perilaku tersebut pada lebih dari sekadar tingkat nominal, maka Anda perlu beralih ke sistem tipe yang lebih (atau berbeda) ekspresif.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.