Membagi antarmuka besar

9

Saya menggunakan antarmuka besar dengan sekitar 50 metode untuk mengakses database. Antarmuka telah ditulis oleh seorang rekan saya. Kami membahas ini:

Saya: 50 metode terlalu banyak. Ini bau kode.
Rekan kerja: Apa yang harus saya lakukan? Anda menginginkan akses DB - Anda memilikinya.
Saya: Ya, tapi tidak jelas dan sulit dipelihara di masa depan.
Rekan kerja: Baik, Anda benar, itu tidak baik. Bagaimana seharusnya tampilan antarmuka saat itu?
Saya: Bagaimana dengan 5 metode yang mengembalikan objek yang masing-masing memiliki 10 metode?

Mmmh, tapi bukankah ini sama? Apakah ini benar-benar mengarah pada kejelasan? Apakah ini sepadan dengan usaha?

Sesekali saya berada dalam situasi di mana saya menginginkan antarmuka dan hal pertama yang terlintas dalam pikiran adalah satu antarmuka besar . Apakah ada pola desain umum untuk ini?


Perbarui (menanggapi komentar SJuan):

"Jenis metode": Ini adalah antarmuka untuk mengambil data dari database. Semua metode memiliki bentuk (pseudocode)

List<Typename> createTablenameList()

Metode dan tabel tidak persis dalam hubungan 1-1, penekanannya lebih pada kenyataan bahwa Anda selalu mendapatkan beberapa jenis daftar yang berasal dari database.

TobiMcNamobi
sumber
12
Ada informasi yang relevan yang hilang (metode apa yang Anda miliki). Bagaimanapun, tebakan saya: jika Anda hanya membagi berdasarkan angka maka kolega Anda benar, Anda tidak meningkatkan apa pun. Satu kriteria pembagian yang mungkin oleh "entitas" (hampir setara dengan tabel) dikembalikan (jadi, a UserDaodan a CustomerDaodan a ProductDao)
SJuan76
Memang beberapa tabel secara semantik dekat dengan tabel lain yang membentuk "klik-klik". Begitu juga metodenya.
TobiMcNamobi
Apakah mungkin melihat kode? Saya tahu ini berumur 4 tahun dan Anda mungkin sudah memperbaikinya sekarang: D Tetapi saya ingin memikirkan masalah ini. Saya memecahkan sesuatu seperti ini sebelumnya.
clankill3r
@ clankill3r Memang saya tidak memiliki akses lagi ke antarmuka spesifik yang membuat saya memposting pertanyaan di atas. Masalahnya lebih umum. Bayangkan DB dengan sekitar 50 tabel dan untuk setiap tabel metode sepertiList<WeatherDataRecord> createWeatherDataTable() {db.open(); return db.select("*", "tbl_weatherData");}
TobiMcNamobi

Jawaban:

16

Ya, 50 metode adalah bau kode, tetapi bau kode berarti untuk melihatnya lagi, bukan berarti itu salah. Jika setiap klien menggunakan kelas itu berpotensi membutuhkan semua 50 metode, mungkin tidak ada kasus untuk membaginya. Namun, ini tidak mungkin. Maksud saya adalah, membelah antarmuka secara sewenang-wenang bisa lebih buruk daripada tidak membaginya sama sekali.

Tidak ada pola tunggal untuk memperbaikinya, tetapi prinsip yang menggambarkan keadaan yang diinginkan adalah Prinsip Segregasi Antarmuka ('I' dalam SOLID), yang menyatakan bahwa tidak ada klien yang harus dipaksa bergantung pada metode yang tidak digunakannya. .

Deskripsi ISP memang memberi Anda petunjuk tentang cara memperbaikinya: lihat klien . Seringkali, hanya dengan melihat kelas sepertinya semuanya milik bersama, tetapi pembagian yang jelas muncul ketika Anda melihat klien menggunakan kelas itu. Selalu pertimbangkan klien terlebih dahulu saat mendesain antarmuka.

Cara lain untuk menentukan apakah dan di mana antarmuka harus dipisah adalah dengan membuat implementasi kedua. Yang sering terjadi adalah implementasi kedua Anda tidak memerlukan banyak metode, jadi yang jelas harus dipisah menjadi antarmuka mereka sendiri.

Karl Bielefeldt
sumber
Jawaban ini dan utnapistim sangat bagus.
TobiMcNamobi
13

Rekan kerja: Baik, Anda benar, itu tidak baik. Bagaimana seharusnya tampilan antarmuka saat itu?

Saya: Bagaimana dengan 5 metode yang mengembalikan objek yang masing-masing memiliki 10 metode?

Itu bukan kriteria yang baik (sebenarnya tidak ada kriteria dalam pernyataan itu). Anda dapat mengelompokkannya dengan (dengan asumsi aplikasi Anda adalah aplikasi transaksi keuangan, sebagai contoh saya):

  • fungsionalitas (memasukkan, memperbarui, memilih, transaksi, metadata, skema, dll)
  • entitas (pengguna DAO , setoran DAO, dll.)
  • area aplikasi (transaksi keuangan, manajemen pengguna, total dll)
  • tingkat abstraksi (semua kode akses tabel adalah modul terpisah; semua API pilih berada dalam hierarki mereka sendiri, dukungan transaksi terpisah, semua kode konversi dalam modul, semua kode validasi dalam modul, dll.)

Mmmh, tapi bukankah ini sama? Apakah ini benar-benar mengarah pada kejelasan? Apakah ini sepadan dengan usaha?

Jika Anda memilih kriteria yang tepat, pasti. Jika tidak, pasti tidak :).

Beberapa contoh:

  • lihat objek ADODB untuk contoh sederhana dari OO primitif (DB API Anda mungkin sudah menawarkan ini)

  • lihat model data Django ( https://docs.djangoproject.com/en/dev/topics/db/models/ ) untuk ide model data dengan abstraksi tingkat tinggi (dalam C ++ Anda mungkin perlu sedikit lebih banyak pelat ketel kode, tapi ini ide yang bagus). Implementasi ini dirancang dengan peran "model" dalam pikiran, dalam pola desain MVC.

  • lihat API sqlite untuk ide API datar ( http://www.sqlite.org/c3ref/funclist.html ), yang terdiri dari primitif fungsional saja (C API).

utnapistim
sumber
3

Sesekali saya berada dalam situasi di mana saya menginginkan antarmuka dan hal pertama yang terlintas dalam pikiran adalah satu antarmuka besar. Apakah ada pola desain umum untuk ini?

Ini adalah desain anti-pola yang disebut kelas monolitik . Memiliki 50 metode di kelas atau interface adalah pelanggaran mungkin dari SRP . Kelas monolitik muncul karena ia berusaha menjadi segalanya bagi semua orang.

Metode alamat DCI mengasapi. Pada dasarnya banyak tanggung jawab suatu kelas dapat dibagi ke peran (diturunkan ke kelas lain) yang hanya relevan dalam konteks tertentu. Penerapan peran dapat dicapai dalam sejumlah cara termasuk mixin atau dekorator . Pendekatan ini membuat kelas tetap fokus dan ramping.

Bagaimana dengan 5 metode yang mengembalikan objek yang masing-masing memiliki 10 metode?

Ini menyarankan instantiate semua peran ketika objek itu sendiri instantiated. Tapi mengapa peran instantiate Anda mungkin tidak perlu? Sebaliknya, instantiate peran dalam konteks di mana Anda benar-benar membutuhkannya.

Jika Anda menemukan bahwa refactoring ke DCI tidak jelas, Anda bisa menggunakan pola pengunjung yang lebih sederhana . Ini memberikan manfaat serupa tanpa menekankan penciptaan konteks use case.

EDIT: Pemikiran saya tentang ini telah mengubah beberapa. Saya memberikan jawaban alternatif.

Mario T. Lanza
sumber
1

Sepertinya saya setiap jawaban lain tidak ada gunanya. Intinya adalah bahwa sebuah antarmuka idealnya harus mendefinisikan perilaku atom. Itu adalah aku dalam SOLID.

Kelas harus memiliki satu tanggung jawab tetapi ini masih bisa mencakup beberapa perilaku. Untuk tetap dengan objek klien database biasa, ini mungkin menawarkan fungsionalitas CRUD penuh. Itu akan menjadi empat perilaku: membuat, membaca, memperbarui, dan menghapus. Dalam dunia SOLID murni, klien basis data akan mengimplementasikan bukan IDatabaseClient tetapi ICreator, IReader, IUpdater dan IDeleter.

Ini akan memiliki sejumlah manfaat. Pertama, hanya dengan membaca deklarasi kelas seseorang akan langsung belajar banyak tentang kelas, antarmuka yang diterapkannya menceritakan keseluruhan cerita. Kedua, jika objek klien dilewatkan sebagai argumen, satu sekarang memiliki opsi berguna yang berbeda. Itu bisa diteruskan sebagai IReader dan orang bisa yakin callee hanya akan bisa membaca. Perilaku yang berbeda dapat diuji secara terpisah.

Namun ketika datang ke pengujian, praktik umum adalah dengan hanya menampar antarmuka pada kelas yang merupakan replika 1-ke-1 dari antarmuka kelas penuh. Jika hanya pengujian yang Anda pedulikan, ini mungkin pendekatan yang valid. Ini memungkinkan Anda membuat boneka dengan cukup cepat. Tapi itu hampir tidak pernah SOLID dan benar-benar penyalahgunaan antarmuka untuk tujuan khusus.

Jadi ya, 50 metode adalah bau tetapi tergantung pada maksud dan tujuannya apakah itu buruk atau tidak. Ini tentu tidak ideal.

Martin Maat
sumber
0

Lapisan Akses Data cenderung memiliki banyak metode yang melekat pada satu kelas. Jika Anda pernah bekerja dengan Entity Framework atau alat ORM lainnya, Anda akan melihat mereka menghasilkan 100-an metode. Saya menganggap Anda dan kolega Anda menerapkannya secara manual. Tidak perlu bau kode, tetapi tidak cantik untuk dilihat. Tanpa mengetahui domain Anda, sulit dikatakan.

Roman Mik
sumber
Metode atau properti?
JeffO
0

Saya menggunakan protokol (menyebutnya antarmuka jika Anda mau) hampir secara universal untuk semua apis dengan FP dan OOP. (Ingat Matriksnya? Tidak ada konkret!) Tentu saja ada tipe-tipe konkret tetapi dalam ruang lingkup suatu program setiap tipe dianggap sebagai sesuatu yang berperan dalam suatu konteks.

Ini berarti objek yang melewati seluruh program, ke dalam fungsi, dll. Dapat dianggap sebagai entitas abstrak dengan serangkaian perilaku yang dinamai. Objek dapat dianggap sebagai memainkan peran yang merupakan seperangkat protokol. Seseorang (tipe konkret) bisa jadi lelaki, ayah, suami, teman, dan karyawan, tetapi saya tidak bisa membayangkan banyak fungsi yang akan menganggap entitas lebih dari 2 dari jumlah itu.

Saya kira itu mungkin bahwa objek kompleks dapat mematuhi sejumlah protokol yang berbeda, tetapi Anda masih akan kesulitan untuk mendapatkan api metode 50. Sebagian besar protokol memiliki 1 atau 2 metode, mungkin 3, tetapi tidak pernah 50! Setiap entitas yang memiliki 50 metode harus dibuat agregat dari sekelompok komponen yang lebih kecil masing-masing dengan tanggung jawabnya sendiri. Entitas pada umumnya akan menghadirkan antarmuka yang lebih sederhana yang mengabstraksi total jumlah apis di dalamnya.

Daripada berpikir dalam hal objek dan metode, mulailah berpikir dalam hal abstraksi dan kontrak dan peran apa yang dimainkan subjek dalam beberapa konteks.

Mario T. Lanza
sumber