Kesulitan membuat kelas ini terbuka-tertutup

8

Inilah masalah saya: Saya ingin membaca input dari berbagai perangkat HID seperti gamepad, racing well, joystick, dll. Cukup banyak pengendali permainan apa pun. Masalahnya adalah mereka semua memiliki input yang berbeda.

Gamepad memiliki tombol, sakelar, dan stik, sementara arena balap mungkin memiliki stik gigi. Saya berhasil mengabstraksikan semua komponen yang berbeda ini menjadi hanya 3 jadi alih-alih memiliki kelas dasar dengan semua kemungkinan kombinasi:

abstract class Device
    {
    public Buttons Buttons;
    public Axes Axes;
    public Switches Switches;
    public GearSticks GearSticks;
    //many more
    }

Sekarang saya dapat memiliki:

abstract class Device
{
public Buttons Buttons;   //on or off
public Axes Axes;         //range [-100%:100%]
public Switches Switches; //multiple states
}

Pada awalnya saya senang dengan ini karena tampaknya mencakup semua jenis input yang mungkin dan sehingga kelas saya bisa tetap tertutup sementara terbuka untuk ekstensi melalui semua implementasi konkret karena semuanya dapat diabstraksi menjadi hanya 3 jenis input.

TETAPI kemudian saya berpikir sendiri bagaimana jika saya hanya menunda hal yang tak terhindarkan? Bagaimana jika suatu hari saya harus menambahkan bidang lain ke Devicekelas saya ? Itu tidak mendukung sesuatu seperti trackball!

Apakah ada cara saya bisa membuktikan kelas ini di masa depan? Cara saya melihatnya saya akan berakhir dengan sesuatu seperti ini:

public Device1 : Device
{
//buttons
//trackball
}

public Device2 : Device
{
//Switch
//Axis
}

public Device3 : Device
{
//trackball
//switch
}

Dan saya harus terus menambahkan properti ke kelas dasar saya setiap kali ada sesuatu yang baru untuk diterapkan.

Mihai Bratulescu
sumber
Satu-satunya cara untuk benar-benar membuktikan di masa depan kelas semacam itu adalah untuk menyimpan pasangan nilai kunci sebagai string dan meneruskan nilai di sekitar kelas Anda dengan semacam protokol tanpa tipe seperti Protocol Buffers. Tetapi HID lebih spesifik dari itu. Anda berada di platform apa? Ada beberapa perpustakaan pembantu yang dapat memudahkan proses ini; di sini adalah satu untuk .NET Framework: github.com/mikeobrien/HidLibrary . Atau yang ini: github.com/jcoenraadts/hid-sharp
Robert Harvey
Ini adalah pertanyaan rekayasa perangkat lunak yang sangat baik, dan saya tidak tahu mengapa Anda menerima downvote untuk itu.
Doc Brown
@RobertHarvey Ketika Anda mengatakan pasangan kunci-nilai sesuatu mulai masuk akal dalam pikiran saya, tetapi apa yang Anda maksud dengan protokol tanpa huruf? Saya di platform UWP.
Mihai Bratulescu
Protokol tanpa ketik adalah protokol yang bekerja sepenuhnya pada pasangan string, seperti protokol yang digunakan URL:, parameter1=value1&parameter2=value2dll.
Robert Harvey

Jawaban:

7

Saya cukup yakin ini dapat dilakukan dengan memperkenalkan konsep seperti abstrak InputChannel, dan perangkat memiliki daftar saluran input yang dapat dikonfigurasi. Saluran input akan memiliki nama, tipe, mungkin beberapa data meta, dan harus dapat menghasilkan beberapa "keadaan" yang cocok dengan tipe itu. Mungkin ada saluran yang sudah ditentukan sebelumnya seperti tombol, kapak atau sakelar, atau beberapa saluran baru yang saat ini tidak Anda kenal (tetapi mungkin ditambahkan kemudian dengan memperkenalkan InputChannelkelas anak baru ).

Dengan cara ini, perangkat akan menjadi semacam model meta, dan Anda juga akan membutuhkan cara untuk mengelola status perangkat, yang harus sesuai dengan daftar saluran input perangkat.

Namun, pendekatan generik semacam itu memiliki risiko overengineering, juga dikenal sebagai efek Inner-platform . Misalnya, mungkin tidak mudah untuk menambahkan fungsionalitas spesifik ke perangkat generik, atau peristiwa tertentu, atau interaksi antara saluran input yang berbeda. Mungkin juga lebih sulit untuk menggunakan dan memahami bagi pengguna perpustakaan perangkat generik Anda.

Perhatikan bahwa tidak selalu bermanfaat untuk membuat solusi yang paling abstrak. Mengubah persyaratan dalam perangkat keras biasanya memerlukan lebih banyak upaya untuk diimplementasikan dalam perangkat keras itu sendiri daripada dalam perangkat lunak yang sesuai, sehingga seringkali lebih baik tetap menggunakan solusi yang lebih spesifik dalam perangkat lunak, dan mengubah perangkat lunak bila diperlukan.

Doc Brown
sumber
Sebuah InputChannelpekerjaan bisa, semua saya membutuhkannya untuk lakukan adalah untuk memperbarui negara itu dan menaikkan onChangedEvent. Tapi Anda mungkin benar tentang rekayasa berlebihan ... Saya akan mengingat ini. Apakah Anda pikir menambahkan bidang lain ke kelas setiap saat dapat diterima?
Mihai Bratulescu
1
@MihaiBratulescu: itu tergantung pada jenis perangkat lunak apa yang Anda tulis - beberapa kerangka permainan untuk diri sendiri atau tim Anda, di mana Anda mengetahui kasus penggunaan dan dapat bereaksi sesuai ketika persyaratan berubah, atau beberapa kerangka kerja umum seperti Qt atau .Net framework yang akan menjadi digunakan oleh ratusan ribu dev dalam situasi yang tidak terduga. Untuk yang terakhir, solusi yang sangat generik dan SOLID jauh lebih penting daripada solusi sebelumnya.
Doc Brown
plang di depan - perhentian Anda berikutnya, Twilight Zone : "Dukungan sangat terbatas untuk perpustakaan ini. Hampir tidak mungkin memecahkan masalah dengan begitu banyak perangkat dan konfigurasi." Ini dari referensi hidLibrary @RobertHarvey . Dengan keras mengisyaratkan apa yang ada di dalam untuk meraih teori medan terpadu Tar Baby
radarbob
3

Gagasan di balik prinsip buka-tutup adalah bahwa Anda lebih kecil kemungkinannya untuk merusak fungsionalitas yang ada jika Anda menerapkan fungsionalitas baru melalui pewarisan alih-alih modifikasi kelas yang ada. Dan Anda dapat melakukannya, dengan mewarisi dari Hid Anda. Dalam satu tahun dan beberapa bulan Anda dapat membuat Hid2020 yang mewarisi dari Hid dan menambahkan dukungan untuk trackball yang akan ditemukan pada Q4 tahun 2019. Setelah penemuan dan mempopulerkan detektor squeeze pada tahun 2023 Anda dapat membuat kelas Hid2024 yang turun dari Hid2019.

Itu akan menjadi pendekatan defensif. Tetapi juga akan sedikit ceroboh dari perspektif desain yang bersih. Dalam kasus Anda, saya tidak akan kehilangan tidur karena melanggar O dan hanya mengubah kelas dasar sebagai dunia di sekitar Anda berubah. Sepertinya penerapan trackball atau jenis kontrol baru lainnya tidak akan berdampak pada cara Anda menangani penekanan tombol atau mengganti perubahan status sekarang.

Martin Maat
sumber
Ditto pada paragraf ke-2. Desain yang koheren adalah masalahnya. Dan berbicara tentang koheren; O / C telah melayani tujuannya di sini, hanya dengan mempertimbangkan O / C membuat orang menyadari bahwa kelas akan dibuka (dimodifikasi) terlalu sering mungkin karena desain yang tidak memadai. IMO ini adalah raison d'etre dari Prinsip Terbuka / Tertutup.
radarbob