Saya bekerja di sebuah proyek yang berhubungan dengan perangkat fisik, dan saya bingung bagaimana cara menyebutkan beberapa kelas dengan benar dalam proyek ini.
Mengingat perangkat yang sebenarnya (sensor dan penerima) adalah satu hal, dan representasi mereka dalam perangkat lunak adalah hal lain, saya berpikir tentang penamaan beberapa kelas dengan pola nama akhiran "Info".
Sebagai contoh, sementara a Sensor
akan menjadi kelas untuk mewakili sensor aktual (ketika itu sebenarnya terhubung ke beberapa perangkat yang berfungsi), SensorInfo
akan digunakan untuk hanya mewakili karakteristik sensor tersebut. Sebagai contoh, setelah menyimpan file, saya akan membuat cerita bersambung SensorInfo
ke tajuk file, alih-alih membuat cerita bersambung Sensor
, yang jenisnya bahkan tidak masuk akal.
Tapi sekarang saya bingung, karena ada jalan tengah pada siklus hidup objek di mana saya tidak bisa memutuskan apakah saya harus menggunakan satu atau yang lain, atau bagaimana cara mendapatkan satu dari yang lain, atau bahkan apakah kedua varian harus benar-benar diciutkan menjadi hanya satu kelas.
Juga, Employee
kelas contoh yang terlalu umum jelas hanya merupakan representasi dari orang yang sebenarnya, tetapi tidak ada yang menyarankan untuk menyebutkan kelas EmployeeInfo
, sejauh yang saya tahu.
Bahasa yang saya gunakan adalah .NET, dan pola penamaan ini tampaknya umum di seluruh kerangka kerja, untuk contoh dengan kelas-kelas ini:
Directory
danDirectoryInfo
kelas;File
danFileInfo
kelas;ConnectionInfo
kelas (tanpaConnection
kelas koresponden );DeviceInfo
kelas (tanpaDevice
kelas koresponden );
Jadi pertanyaan saya adalah: apakah ada alasan umum untuk menggunakan pola penamaan ini? Apakah ada kasus di mana masuk akal untuk memiliki pasangan nama ( Thing
dan ThingInfo
) dan kasus lain di mana seharusnya hanya ada ThingInfo
kelas, atau Thing
kelas, tanpa pasangannya?
sumber
Info
akhiran membedakanstatic
kelas yang berisi metode utilitas dari rekan stateful nya. Itu bukan "praktik terbaik"; itu hanya cara yang dibuat oleh tim .NET untuk memecahkan masalah tertentu. Mereka bisa saja dengan mudah membuatFileUtility
danFile
, tetapiFile.DoSomething()
danFileInfo.FileName
tampaknya membaca lebih baik.Foo
, Anda mungkin memiliki kelas utilitas non-instantiableFoos
. Ketika berbicara tentang penamaan, yang penting adalah konsistensi dalam API, dan idealnya di seluruh API pada platform.Employee
contoh ditemukan oleh lusinan, baik online atau di buku-buku klasik, sementara saya belum melihatEmployeeInfo
(mungkin karena seorang karyawan adalah makhluk hidup, tidak konstruksi teknis seperti koneksi atau file). Tetapi, setuju, jika kelasEmployeeInfo
akan diusulkan dalam suatu proyek, saya percaya itu bisa digunakan.Jawaban:
Saya pikir "info" adalah istilah yang salah. Objek memiliki status dan tindakan: "info" hanyalah nama lain untuk "status" yang sudah dimasukkan ke OOP.
Apa yang sebenarnya Anda coba model di sini? Anda memerlukan objek yang mewakili perangkat keras dalam perangkat lunak sehingga kode lain dapat menggunakannya.
Itu mudah dikatakan tetapi ketika Anda tahu, ada lebih dari itu. "Perangkat keras yang mewakili" sangat luas. Objek yang melakukan itu memiliki beberapa masalah:
Perangkat tertentu seperti sensor akan memiliki masalah lebih sedikit daripada mengatakan perangkat multifungsi printer / pemindai / faks. Sensor kemungkinan hanya menghasilkan aliran bit, sementara perangkat yang kompleks mungkin memiliki protokol dan interaksi yang kompleks.
Bagaimanapun, kembali ke pertanyaan spesifik Anda, ada beberapa cara untuk melakukan ini tergantung pada kebutuhan spesifik Anda dan juga kompleksitas interaksi perangkat keras.
Berikut adalah contoh bagaimana saya akan merancang hirarki kelas untuk sensor suhu:
ITemperatureSource: antarmuka yang mewakili apa pun yang dapat menghasilkan data suhu: sensor, bahkan bisa berupa pembungkus file atau data yang dikodekan dengan keras (pikirkan: pengujian tiruan).
Acme4680Sensor: ACME model 4680 sensor (bagus untuk mendeteksi ketika Roadrunner berada di dekatnya). Ini dapat menerapkan beberapa antarmuka: mungkin sensor ini mendeteksi suhu dan kelembaban. Objek ini berisi status tingkat program seperti "apakah sensor terhubung?" dan "apa bacaan terakhir?"
Acme4680SensorComm: hanya digunakan untuk berkomunikasi dengan perangkat fisik. Itu tidak mempertahankan banyak negara. Ini digunakan untuk mengirim dan menerima pesan. Ini memiliki metode C # untuk setiap pesan yang dimengerti oleh perangkat keras.
HardwareManager: digunakan untuk mendapatkan perangkat. Ini pada dasarnya adalah pabrik yang menyimpan instance: hanya ada satu instance objek perangkat untuk setiap perangkat perangkat keras. Harus cukup pintar untuk mengetahui bahwa jika ulir A meminta sensor suhu ACME dan ulir B meminta sensor kelembaban ACME, ini sebenarnya adalah objek yang sama dan harus dikembalikan ke kedua utas.
Di tingkat atas Anda akan memiliki antarmuka untuk setiap jenis perangkat keras. Mereka menggambarkan tindakan kode C # Anda akan mengambil pada perangkat, menggunakan tipe data C # (bukan misalnya byte array yang mungkin menggunakan driver perangkat mentah).
Pada tingkat yang sama Anda memiliki kelas enumerasi dengan satu instance untuk setiap jenis perangkat keras. Sensor suhu mungkin satu jenis, sensor kelembaban yang lain.
Satu level di bawah ini adalah kelas aktual yang mengimplementasikan antarmuka itu: mereka mewakili satu perangkat yang mirip dengan Acme4680Sensor yang saya jelaskan di atas. Kelas tertentu dapat menerapkan beberapa antarmuka jika perangkat dapat melakukan banyak fungsi.
Setiap kelas perangkat memiliki kelas Comm (komunikasi) pribadi yang menangani tugas tingkat rendah untuk berbicara dengan perangkat keras.
Di luar modul perangkat keras, satu-satunya lapisan yang terlihat adalah antarmuka / enum plus HardwareManager. Kelas HardwareManager adalah abstraksi pabrik yang menangani instantiation kelas perangkat, instance caching (Anda benar-benar - tidak ingin dua kelas perangkat berbicara dengan perangkat perangkat keras yang sama), dll. Kelas yang membutuhkan jenis sensor tertentu meminta HardwareManager untuk mendapatkan perangkat untuk enum tertentu, yang kemudian diketahui jika sudah dipakai, jika tidak cara membuatnya dan menginisialisasi, dll.
Tujuannya di sini adalah untuk memisahkan logika bisnis dari logika perangkat keras tingkat rendah. Saat Anda menulis kode yang mencetak data sensor ke layar, kode itu seharusnya tidak peduli jenis sensor apa yang Anda miliki jika dan hanya jika decoupling ini berada di tempat yang berpusat pada antarmuka perangkat keras tersebut.
Catatan: ada asosiasi antara HardwareManager dan setiap kelas perangkat yang tidak saya gambar karena diagram akan berubah menjadi sup panah.
sumber
Mungkin agak sulit untuk menemukan satu konvensi pemersatu di sini karena kelas-kelas ini tersebar di sejumlah ruang nama, (
ConnectionInfo
tampaknya ada diCrystalDecisions
, danDeviceInfo
diSystem.Reporting.WebForms
).Melihat contoh-contoh ini, tampaknya ada dua kegunaan akhiran yang berbeda:
Membedakan kelas yang menyediakan metode statis dengan kelas yang menyediakan metode instan. Ini adalah kasus untuk
System.IO
kelas, sebagaimana digarisbawahi oleh deskripsi mereka:Direktori :
Informasi Direktori :
Info
sepertinya pilihan yang sedikit aneh di sini, tetapi itu membuat perbedaan relatif jelas: sebuahDirectory
kelas dapat mewakili direktori tertentu atau menyediakan metode pembantu terkait direktori umum tanpa memegang status apa pun, sedangkan yangDirectoryInfo
benar-benar hanya bisa menjadi yang pertama.Menekankan bahwa kelas hanya menyimpan informasi dan tidak memberikan perilaku yang mungkin diharapkan dari nama yang tidak berakhiran .
Saya pikir bagian terakhir dari kalimat itu mungkin adalah bagian dari teka-teki yang membedakan, katakanlah,
ConnectionInfo
dariEmployeeInfo
. Jika saya memiliki kelas yang dipanggilConnection
, saya cukup berharap untuk benar-benar memberi saya fungsionalitas yang dimiliki oleh koneksi - saya akan mencari metode sepertivoid Open()
, dll. Namun, tidak ada orang waras yang berharap bahwa sebuahEmployee
kelas dapat benar-benar lakukan apa yang nyataEmployee
lakukan, atau cari metode sepertivoid DoPaperwork()
ataubool TryDiscreetlyBrowseFacebook()
.sumber
Secara umum, suatu
Info
objek merangkum informasi tentang keadaan suatu objek pada suatu saat . Jika saya meminta sistem untuk melihat file dan memberi sayaFileInfo
objek yang terkait dengan ukurannya, saya akan berharap objek itu melaporkan ukuran file pada saat permintaan diberikan (atau, lebih tepatnya, ukuran file beberapa saat antara saat panggilan dibuat dan ketika kembali). Jika ukuran file berubah antara waktu permintaan kembali dan waktuFileInfo
objek diperiksa, saya tidak akan mengharapkan perubahan seperti itu tercermin dalamFileInfo
objek.Perhatikan bahwa perilaku ini akan sangat berbeda dari
File
objek. Jika permintaan untuk membuka file disk dalam mode non-eksklusif menghasilkanFile
objek yang memilikiSize
properti, saya akan mengharapkan nilai yang dikembalikan dengan demikian berubah ketika ukuran file disk berubah, karenaFile
objek tidak hanya mewakili keadaan file - itu mewakili file itu sendiri .Dalam banyak kasus, objek yang menempel pada sumber daya harus dibersihkan ketika layanan mereka tidak lagi diperlukan. Karena
*Info
objek tidak melampirkan sumber daya, mereka tidak memerlukan pembersihan. Sebagai konsekuensinya, dalam kasus di mana suatuInfo
objek akan memenuhi persyaratan klien, mungkin lebih baik menggunakan kode daripada menggunakan objek yang akan mewakili sumber daya yang mendasarinya, tetapi yang hubungannya dengan sumber daya itu harus dibersihkan.sumber
File
kelas tidak dapat dipakai danFileInfo
objek melakukan pembaruan dengan sistem file yang mendasarinya.FileInfo
hanya menyimpan informasi yang ditangkap secara statis. Bukan? Juga, saya tidak pernah memiliki kesempatan untuk membuka file dalam mode non-eksklusif, tetapi itu harus mungkin, dan saya akan berharap ada metode yang akan melaporkan ukuran file saat ini, bahkan jika objek yang digunakan untuk membuka tidak disebutFile
(biasanya saya hanya menggunakanReadAllBytes
,WriteAllBytes
,ReadAllText
,WriteAllText
, dll).Saya tidak suka perbedaan ini. Semua objek adalah "representasi [s] dalam perangkat lunak." Itulah arti kata "objek".
Sekarang, mungkin masuk akal untuk memisahkan informasi tentang periferal dari kode aktual yang berinteraksi dengan periferal. Jadi, misalnya, Sensor memiliki-SensorInfo, yang berisi sebagian besar variabel instan, bersama dengan beberapa metode yang tidak memerlukan perangkat keras, sedangkan kelas Sensor bertanggung jawab untuk benar-benar berinteraksi dengan sensor fisik. Anda tidak memiliki-a
Sensor
kecuali komputer Anda memiliki sensor, tetapi Anda mungkin memiliki-aSensorInfo
.Masalahnya adalah bahwa desain semacam ini dapat digeneralisasi ke (hampir) kelas mana pun. Jadi kamu harus hati-hati. Anda jelas tidak akan memiliki
SensorInfoInfo
kelas, misalnya. Dan jika Anda memilikiSensor
variabel, Anda mungkin menemukan diri Anda melanggar hukum Demeter dengan berinteraksi denganSensorInfo
anggotanya. Tidak ada yang fatal, tentu saja, tetapi desain API tidak hanya untuk penulis perpustakaan. Jika Anda menjaga API Anda sendiri tetap bersih dan sederhana, kode Anda akan lebih mudah dikelola.Sumber daya filesystem seperti direktori, menurut saya, sangat dekat dengan tepi ini. Ada beberapa situasi di mana Anda ingin menggambarkan direktori yang tidak dapat diakses secara lokal, benar, tetapi pengembang rata-rata mungkin tidak berada dalam salah satu situasi tersebut. Menyulitkan struktur kelas dengan cara ini, menurut pendapat saya, tidak membantu. Kontras dengan pendekatan Python dalam
pathlib
: Ada satu kelas yang "sangat mungkin apa yang Anda butuhkan" dan berbagai kelas tambahan yang diabaikan oleh sebagian besar pengembang. Namun, jika Anda benar-benar membutuhkannya, mereka menyediakan sebagian besar antarmuka yang sama, hanya dengan metode konkret dilucuti.sumber
SensorInfo
akan menjadi semacam DTO, dan / atau juga seperti "snapshot", atau bahkan "spek", yang hanya mewakili data / keadaan bagian dariSensor
objek aktual bahwa "mungkin tidak ada di sana".PureWindowsPath
bertindak sedikit seperti objek Info, tetapi memiliki metode untuk melakukan hal-hal yang tidak memerlukan sistem Windows (misalnya mengambil subdirektori, memisahkan ekstensi file, dll.). Ini lebih bermanfaat daripada hanya menyediakan struct yang dimuliakan.Saya akan mengatakan bahwa konteks / domain penting, karena kami memiliki kode logika bisnis tingkat tinggi dan model tingkat rendah, komponen arsitektur, dan sebagainya ...
'Info', 'Data', 'Manager', 'Object', 'Class', 'Model', 'Controller' dll. Dapat berupa sufiks yang bau, terutama pada level yang lebih rendah, karena setiap objek memiliki beberapa informasi atau data, sehingga informasi itu tidak perlu.
Nama kelas dari domain bisnis harus seperti semua pemangku kepentingan membicarakannya, tidak peduli apakah itu terdengar aneh atau tidak 100% bahasa yang benar.
Sufiks yang bagus untuk struktur data misalnya 'Daftar', 'Peta' dan untuk pola petunjuk 'Dekorator', 'Adaptor' jika Anda pikir itu perlu.
Untuk skenario sensor Anda, saya tidak akan berharap
SensorInfo
untuk menyimpan apa sensor Anda, tetapiSensorSpec
.Info
imho lebih merupakan informasi turunan, sepertiFileInfo
sesuatu seperti ukuran, Anda tidak menyimpan atau filepath yang dibangun dari path dan nama file, dll.Poin lain:
Itu selalu mengingatkan saya pada memikirkan nama hanya untuk beberapa detik dan jika saya tidak menemukan nama, saya menggunakan nama-nama aneh dan 'TODO'-tandai mereka. Saya selalu dapat mengubahnya nanti karena IDE saya menyediakan dukungan refactoring. Ini bukan semboyan perusahaan yang harus bagus, tetapi hanya beberapa kode yang dapat kita ubah setiap kali kita inginkan. Ingatlah itu.
sumber
ThingInfo dapat berfungsi sebagai Proxy hanya baca yang bagus untuk Hal ini.
lihat http://www.dofactory.com/net/proxy-design-pattern
Proxy: "Berikan pengganti atau pengganti untuk objek lain untuk mengontrol akses ke sana."
Biasanya ThingInfo akan memiliki properti publik tanpa seter. Kelas-kelas ini dan metode-metode di kelas aman untuk digunakan dan tidak akan melakukan perubahan apa pun pada data dukungan, objek, atau objek lainnya. Tidak ada perubahan status atau efek samping lainnya yang akan terjadi. Ini dapat digunakan untuk pelaporan dan layanan web atau di mana pun Anda membutuhkan informasi tentang objek tetapi ingin membatasi akses ke objek itu sendiri.
Gunakan ThingInfo kapan pun memungkinkan dan batasi penggunaan Thing aktual hingga saat Anda benar-benar perlu mengubah objek Thing. Itu membuat membaca dan men-debug jauh lebih cepat ketika Anda terbiasa menggunakan pola ini.
sumber
Receiver
yang menerima aliran data dari banyakSensor
. Idenya adalah: penerima harus mengabstraksi sensor yang sebenarnya. Tetapi masalahnya adalah: Saya perlu info sensor sehingga saya bisa menulisnya ke beberapa file header. Solusinya: masingIReceiver
- masing akan memiliki daftarSensorInfo
. Jika saya mengirim perintah ke penerima yang menyiratkan perubahan status sensor, perubahan ini akan tercermin (melalui pengambil) pada masing-masingSensorInfo
.List<SensorInfo>
properti readonly -nya .Sejauh ini tidak ada orang dalam pertanyaan ini yang mengetahui alasan sebenarnya dari konvensi penamaan ini.
Sebuah
DirectoryInfo
bukanlah yang direktori. Ini adalah sebuah DTO dengan data tentang direktori. Mungkin ada banyak contoh yang menggambarkan direktori yang sama. Itu bukan entitas. Ini adalah objek nilai yang dibuang. ADirectoryInfo
tidak mewakili direktori aktual. Anda juga dapat menganggapnya sebagai pegangan atau pengontrol untuk direktori.Bandingkan dengan kelas yang bernama
Employee
. Ini mungkin objek entitas ORM dan itu adalah objek tunggal yang menggambarkan karyawan itu. Jika itu adalah nilai-objek tanpa identitas itu harus disebutEmployeeInfo
. AnEmployee
memang mewakili karyawan yang sebenarnya. Kelas DTO yang menyerupai nilai yang disebutEmployeeInfo
jelas tidak akan mewakili karyawan melainkan menggambarkannya atau menyimpan data tentangnya.Sebenarnya ada contoh di BCL di mana kedua kelas ada: A
ServiceController
adalah kelas yang menjelaskan Layanan Windows. Bisa ada sejumlah pengontrol seperti itu untuk setiap layanan. AServiceBase
(atau kelas turunan) adalah layanan aktual dan secara konseptual tidak masuk akal untuk memiliki beberapa instance dari itu per layanan yang berbeda.sumber
Proxy
pola yang disebutkan oleh @Shmoken, bukan?EmployeeInfo
dari layanan web. Itu bukan proxy. Contoh lebih lanjut: SeorangAddressInfo
bahkan tidak memiliki sesuatu yang dapat diproksikan karena alamat bukan entitas. Itu nilai yang berdiri sendiri.