Seperempat abad yang lalu ketika saya sedang belajar C ++, saya diajarkan bahwa antarmuka harus memaafkan dan sejauh mungkin tidak peduli tentang urutan metode yang dipanggil karena konsumen mungkin tidak memiliki akses ke sumber atau dokumentasi sebagai pengganti ini.
Namun, setiap kali saya membimbing para programmer junior dan senior devs mendengar saya, mereka telah bereaksi dengan heran yang membuat saya bertanya-tanya apakah ini benar-benar suatu hal atau apakah itu baru saja keluar dari mode.
Jelas seperti lumpur?
Pertimbangkan antarmuka dengan metode ini (untuk membuat file data):
OpenFile
SetHeaderString
WriteDataLine
SetTrailerString
CloseFile
Sekarang Anda tentu saja bisa melalui ini secara berurutan, tetapi mengatakan Anda tidak peduli tentang nama file (berpikir a.out
) atau apa header dan trailer string dimasukkan, Anda bisa menelepon AddDataLine
.
Contoh yang kurang ekstrim mungkin menghilangkan header dan trailer.
Namun yang lain mungkin mengatur string header dan trailer sebelum file dibuka.
Apakah ini prinsip desain antarmuka yang diakui atau hanya jalan POLA sebelum diberi nama?
NB tidak terjebak dalam hal-hal kecil dari antarmuka ini, itu hanya contoh demi pertanyaan ini.
sumber
Jawaban:
Salah satu cara di mana Anda dapat tetap berpegang pada prinsip paling tidak heran adalah dengan mempertimbangkan prinsip-prinsip lain seperti ISP dan SRP , atau bahkan KERING .
Dalam contoh spesifik yang Anda berikan, sarannya adalah ada ketergantungan tertentu dalam pemesanan untuk memanipulasi file; tetapi API Anda mengontrol akses file dan format data, yang baunya sedikit seperti pelanggaran SRP.
Edit / Perbarui: ini juga menyarankan bahwa API itu sendiri meminta pengguna untuk melanggar KERING, karena mereka perlu mengulangi langkah yang sama setiap kali mereka menggunakan API .
Pertimbangkan API alternatif di mana operasi IO terpisah dari operasi data. dan tempat API sendiri 'memiliki' pemesanan:
ContentBuilder
Penulis File
Dengan pemisahan di atas,
ContentBuilder
tidak perlu benar-benar "melakukan" apa pun selain menyimpan baris / header / trailer (Mungkin jugaContentBuilder.Serialize()
metode yang mengetahui urutannya). Dengan mengikuti prinsip-prinsip SOLID lainnya, tidak penting lagi apakah Anda mengatur tajuk atau cuplikan sebelum atau setelah menambahkan baris, karena tidak ada apa pun di dalamContentBuilder
file yang benar-benar ditulis untuk diarsipkan sampai diteruskanFileWriter.Write
.Ini juga memiliki manfaat tambahan menjadi sedikit lebih fleksibel; misalnya, mungkin bermanfaat untuk menulis konten ke logger diagnostik, atau mungkin membagikannya di jaringan daripada menulisnya langsung ke file.
Saat merancang API Anda juga harus mempertimbangkan pelaporan kesalahan, apakah itu keadaan, nilai pengembalian, pengecualian, panggilan balik, atau yang lainnya. Pengguna API mungkin berharap untuk dapat secara terprogram mendeteksi setiap pelanggaran kontraknya, atau bahkan kesalahan lain yang tidak dapat dikontrolnya seperti kesalahan I / O file.
sumber
SetHeader
atauAddLine
masalah. Untuk menghilangkan ketergantungan ini bukan ISP atau SRP, itu hanya POLA.FileWriter
kemudian dapat membutuhkan nilai dariContentBuilder
langkah terakhir dalamWrite
metode untuk memastikan semua konten input selesai, membuatInvalidContentException
tidak perlu.ContentBuilder
dan memungkinkanFileWriter.Write
untuk merangkum sedikit pengetahuan itu. Pengecualian akan diperlukan jika terjadi sesuatu yang tidak beres dengan konten, (misalnya, seperti tajuk yang hilang). Pengembalian juga bisa berfungsi, tapi saya bukan penggemar mengubah pengecualian menjadi kode pengembalian.Ini bukan hanya tentang POLA, tetapi juga tentang mencegah keadaan tidak sah sebagai kemungkinan sumber bug.
Mari kita lihat bagaimana kami dapat memberikan beberapa kendala pada contoh Anda tanpa memberikan implementasi konkret:
Langkah pertama: Jangan biarkan apa pun dipanggil, sebelum file dibuka.
Sekarang harus jelas bahwa
CreateDataFileInterface.OpenFile
harus dipanggil untuk mengambilDataFileInterface
contoh, di mana data aktual dapat ditulis.Langkah kedua: Pastikan, header dan trailer selalu diatur.
Sekarang Anda harus memberikan semua parameter yang diperlukan dimuka untuk mendapatkan
DataFileInterface
: nama file, header dan trailer. Jika string trailer tidak tersedia hingga semua baris ditulis, Anda juga dapat memindahkan parameter ini keClose()
(kemungkinan mengubah nama metodeWriteTrailerAndClose()
) sehingga file setidaknya tidak dapat diselesaikan tanpa string trailer.Untuk membalas komentar:
Benar. Saya tidak ingin lebih berkonsentrasi pada contoh daripada yang perlu untuk membuat poin saya, tapi itu pertanyaan yang bagus. Dalam hal ini saya pikir saya akan menyebutnya
Finalize(trailer)
dan berpendapat bahwa itu tidak terlalu banyak. Menulis trailer dan menutup adalah detail implementasi belaka. Tetapi jika Anda tidak setuju atau memiliki situasi yang sama di mana itu berbeda, berikut ini adalah solusi yang mungkin:Saya tidak akan benar-benar melakukannya untuk contoh ini tetapi ini menunjukkan bagaimana melakukan teknik tersebut secara konsekuen.
By the way, saya berasumsi bahwa metode sebenarnya harus dipanggil dalam urutan ini, misalnya untuk menulis banyak baris secara berurutan. Jika ini tidak diperlukan, saya selalu lebih suka pembangun, seperti yang disarankan oleh Ben Cottrel .
sumber
WriteTrailerAndClose()
) Sedang melakukan pelanggaran terhadap SRP. (Ini adalah sesuatu yang telah saya perjuangkan dengan beberapa kesempatan, tetapi saran Anda tampaknya menjadi contoh yang mungkin). Bagaimana Anda merespons?OpenFile
kelebihan yang tidak memerlukan satu.