Saya punya aplikasi web. Saya tidak percaya teknologi itu penting. Strukturnya adalah aplikasi tingkat-N, ditunjukkan pada gambar di sebelah kiri. Ada 3 lapisan.
UI (pola MVC), Lapisan Logika Bisnis (BLL) dan Lapisan Akses Data (DAL)
Masalah yang saya miliki adalah BLL saya sangat besar karena memiliki logika dan jalur melalui panggilan peristiwa aplikasi.
Aliran khas melalui aplikasi dapat:
Acara dipecat di UI, melintasi metode di BLL, melakukan logika (mungkin di beberapa bagian BLL), akhirnya ke DAL, kembali ke BLL (di mana kemungkinan lebih banyak logika) dan kemudian mengembalikan beberapa nilai ke UI.
BLL dalam contoh ini sangat sibuk dan saya berpikir bagaimana membagi ini. Saya juga memiliki logika dan objek yang digabungkan yang tidak saya sukai.
Versi di sebelah kanan adalah usaha saya.
Logika ini masih bagaimana aplikasi mengalir antara UI dan DAL, tetapi ada kemungkinan ada sifat ... Hanya metode (mayoritas kelas dalam lapisan ini bisa mungkin statis karena mereka tidak menyimpan setiap negara). Lapisan Poco adalah tempat kelas yang memiliki properti (seperti kelas Orang di mana akan ada nama, usia, tinggi dll). Ini tidak ada hubungannya dengan aliran aplikasi, mereka hanya menyimpan keadaan.
Alurnya bisa:
Bahkan dipicu dari UI dan melewatkan beberapa data ke UI layer controller (MVC). Ini menerjemahkan data mentah dan mengubahnya menjadi model poco. Model poco kemudian diteruskan ke lapisan Logic (yang merupakan BLL) dan akhirnya ke lapisan permintaan perintah, berpotensi dimanipulasi di jalan. Lapisan query perintah mengkonversi POCO ke objek database (yang hampir sama, tetapi satu dirancang untuk kegigihan, yang lain untuk ujung depan). Item disimpan dan objek database dikembalikan ke lapisan Command Query. Ini kemudian dikonversi menjadi POCO, di mana ia kembali ke lapisan Logic, berpotensi diproses lebih lanjut dan akhirnya, kembali ke UI
Logika dan antarmuka bersama adalah tempat kami mungkin memiliki data persisten, seperti MaxNumberOf_X dan TotalAllowed_X dan semua antarmuka.
Baik logika / antarmuka bersama dan DAL adalah "basis" dari arsitektur. Ini tidak tahu apa-apa tentang dunia luar.
Semuanya tahu tentang poco selain logika / antarmuka bersama dan DAL.
Alurnya masih sangat mirip dengan contoh pertama, tetapi itu membuat setiap lapisan lebih bertanggung jawab untuk 1 hal (baik itu keadaan, aliran atau apa pun) ... tetapi apakah saya melanggar OOP dengan pendekatan ini?
Contoh untuk memperagakan Logika dan Poco dapat berupa:
public class LogicClass
{
private ICommandQueryObject cmdQuery;
public PocoA Method1(PocoB pocoB)
{
return cmdQuery.Save(pocoB);
}
/*This has no state objects, only ways to communicate with other
layers such as the cmdQuery. Everything else is just function
calls to allow flow via the program */
public PocoA Method2(PocoB pocoB)
{
pocoB.UpdateState("world");
return Method1(pocoB);
}
}
public struct PocoX
{
public string DataA {get;set;}
public int DataB {get;set;}
public int DataC {get;set;}
/*This simply returns something that is part of this class.
Everything is self-contained to this class. It doesn't call
trying to directly communicate with databases etc*/
public int GetValue()
{
return DataB * DataC;
}
/*This simply sets something that is part of this class.
Everything is self-contained to this class.
It doesn't call trying to directly communicate with databases etc*/
public void UpdateState(string input)
{
DataA += input;
}
}
sumber
Jawaban:
Ya, Anda sangat mungkin melanggar konsep inti OOP. Namun jangan merasa buruk, orang melakukan ini sepanjang waktu, itu tidak berarti bahwa arsitektur Anda "salah". Saya akan mengatakan itu mungkin kurang dipelihara daripada desain OO yang tepat, tapi ini agak subyektif dan bukan pertanyaan Anda. ( Ini adalah artikel saya yang mengkritik arsitektur n-tier secara umum).
Penalaran : Konsep OOP yang paling dasar adalah data dan logika membentuk satu unit (objek). Meskipun ini adalah pernyataan yang sangat sederhana dan mekanis, meskipun demikian, itu tidak benar-benar diikuti dalam desain Anda (jika saya mengerti Anda dengan benar). Anda cukup jelas memisahkan sebagian besar data dari sebagian besar logika. Memiliki metode stateless (seperti statik) misalnya disebut "prosedur", dan umumnya berlawanan dengan OOP.
Tentu saja selalu ada pengecualian, tetapi desain ini melanggar hal-hal ini sebagai aturan.
Sekali lagi, saya ingin menekankan "melanggar OOP"! = "Salah", jadi ini belum tentu penilaian nilai. Itu semua tergantung pada kendala arsitektur Anda, kasus penggunaan rawatan, persyaratan, dll.
sumber
Salah satu prinsip inti dari Pemrograman Fungsional adalah fungsi murni.
Salah satu prinsip inti dari Pemrograman Berorientasi Objek adalah menggabungkan fungsi-fungsi dengan data yang ditindaklanjuti.
Kedua prinsip inti ini hilang ketika aplikasi Anda harus berkomunikasi dengan dunia luar. Memang Anda hanya bisa setia pada cita-cita ini dalam ruang yang disiapkan khusus dalam sistem Anda. Tidak setiap baris kode Anda harus memenuhi cita-cita ini. Tetapi jika tidak ada baris kode Anda memenuhi ideal ini, Anda tidak dapat benar-benar mengklaim menggunakan OOP atau FP.
Jadi tidak apa-apa untuk memiliki data hanya "objek" yang Anda putar-putar karena Anda membutuhkannya untuk melewati batas yang tidak dapat Anda refactor untuk memindahkan kode yang tertarik. Hanya tahu itu bukan OOP. Itu kenyataan. OOP adalah ketika, begitu di dalam batas itu Anda mengumpulkan semua logika yang bertindak pada data itu ke satu tempat.
Bukan berarti Anda harus melakukannya juga. OOP bukan segalanya bagi semua orang. Itu adalah apa adanya. Hanya saja, jangan mengklaim sesuatu mengikuti OOP ketika tidak atau Anda akan membingungkan orang yang mencoba mempertahankan kode Anda.
POCO Anda tampaknya memiliki logika bisnis di dalamnya, jadi saya tidak akan terlalu khawatir tentang menjadi anemia. Yang membuat saya khawatir adalah mereka semua tampak sangat bisa berubah. Ingat bahwa getter dan setter tidak memberikan enkapsulasi nyata. Jika POCO Anda menuju batas itu maka baik-baik saja. Hanya mengerti ini tidak memberi Anda manfaat penuh dari objek OOP enkapsulasi nyata. Beberapa orang menyebut ini Obyek Transfer Data atau DTO.
Trik yang saya gunakan dengan sukses adalah untuk membuat objek OOP yang memakan DTO. Saya menggunakan DTO sebagai objek parameter . Konstruktor saya membaca status dari itu (dibaca sebagai salinan defensif ) dan mengabaikannya. Sekarang saya memiliki versi DTO yang sepenuhnya dienkapsulasi dan tidak dapat diubah. Semua metode yang berkaitan dengan data ini dapat dipindahkan di sini asalkan mereka berada di sisi batas itu.
Saya tidak menyediakan getter atau setter. Saya ikuti katakan, jangan tanya . Anda memanggil metode saya dan mereka melakukan apa yang perlu dilakukan. Mereka bahkan mungkin tidak memberi tahu Anda apa yang mereka lakukan. Mereka hanya melakukannya.
Sekarang akhirnya sesuatu, di suatu tempat akan berjalan ke batas lain dan ini semua berantakan lagi. Tidak apa-apa. Putar DTO lain dan lemparkan ke dinding.
Inilah inti dari semua arsitektur port dan adapter. Saya sudah membacanya dari sudut pandang fungsional . Mungkin itu akan menarik minat Anda juga.
sumber
Jika saya membaca penjelasan Anda dengan benar, objek Anda terlihat sedikit seperti ini: (rumit tanpa konteks)
Di mana kelas Poco Anda hanya berisi data dan kelas Logika Anda berisi metode yang bertindak atas data itu; ya, Anda telah melanggar prinsip "Classic OOP"
Sekali lagi, sulit untuk mengatakan dari deskripsi umum Anda, tetapi saya akan membahayakan bahwa apa yang Anda tulis dapat dikategorikan sebagai Model Domain Anemik.
Saya tidak berpikir ini adalah pendekatan yang sangat buruk, juga, jika Anda menganggap Poco Anda sebagai struct, tidak perlu melanggar OOP dalam arti yang lebih spesifik. Karena Objek Anda sekarang adalah LogicClasses. Memang jika Anda membuat Pocos Anda tidak berubah, desain bisa dianggap cukup fungsional.
Namun, ketika Anda mereferensikan Shared Logic, Pocos yang hampir tetapi tidak sama dan statika, saya mulai khawatir tentang detail desain Anda.
sumber
Satu masalah potensial yang saya lihat dalam desain Anda (dan ini sangat umum) - beberapa kode "OO" yang benar-benar terburuk yang pernah saya temui disebabkan oleh arsitektur yang memisahkan objek "Data" dari objek "Code". Ini adalah hal tingkat mimpi buruk! Masalahnya adalah bahwa di mana-mana dalam kode bisnis Anda ketika Anda ingin mengakses objek data Anda, Anda cenderung untuk hanya kode di sana inline (Anda tidak harus, Anda bisa membangun kelas utilitas atau fungsi lain untuk menanganinya tetapi ini adalah apa Saya telah melihat terjadi berulang kali dari waktu ke waktu).
Kode akses / pembaruan umumnya tidak dikumpulkan sehingga Anda berakhir dengan fungsi duplikat di mana-mana.
Di sisi lain, objek data tersebut bermanfaat, misalnya sebagai kegigihan basis data. Saya sudah mencoba tiga solusi:
Menyalin nilai masuk dan keluar ke objek "nyata" dan membuang objek data Anda membosankan (tetapi bisa menjadi solusi yang valid jika Anda ingin pergi ke sana).
Menambahkan metode pertengkaran data ke objek data dapat bekerja tetapi dapat membuat objek data berantakan besar yang melakukan lebih dari satu hal. Ini juga dapat membuat enkapsulasi lebih sulit karena banyak mekanisme kegigihan menginginkan pengakses publik ... Saya belum menyukainya ketika saya melakukannya tetapi ini adalah solusi yang valid
Solusi yang paling berhasil bagi saya adalah konsep kelas "Wrapper" yang merangkum kelas "Data" dan berisi semua fungsi pertengkaran data - maka saya tidak memaparkan kelas data sama sekali (Bahkan tidak setter dan getter) kecuali mereka benar-benar dibutuhkan). Ini menghilangkan godaan untuk memanipulasi objek secara langsung dan memaksa Anda untuk menambahkan fungsionalitas bersama ke pembungkus.
Keuntungan lainnya adalah Anda dapat memastikan bahwa kelas data Anda selalu dalam keadaan valid. Berikut ini adalah contoh kode cepat:
Perhatikan bahwa Anda tidak memiliki penyebaran pemeriksaan usia di seluruh kode Anda di area yang berbeda dan juga bahwa Anda tidak tergoda untuk menggunakannya karena Anda bahkan tidak bisa mengetahui apa ulang tahun itu (kecuali jika Anda memerlukannya untuk hal lain, di yang mana Anda dapat menambahkannya).
Saya cenderung tidak hanya memperluas objek data karena Anda kehilangan enkapsulasi ini dan jaminan keamanan - pada saat itu Anda mungkin juga menambahkan metode ke kelas data.
Dengan cara itu logika bisnis Anda tidak memiliki banyak sampah akses data / iterator tersebar di seluruh itu, itu menjadi jauh lebih mudah dibaca dan kurang berlebihan. Saya juga merekomendasikan untuk membiasakan diri selalu membungkus koleksi untuk alasan yang sama - menjaga perulangan / pencarian konstruksi dari logika bisnis Anda dan memastikan mereka selalu dalam keadaan baik.
sumber
Jangan pernah mengubah kode Anda karena Anda pikir atau seseorang memberi tahu Anda bahwa ini bukan ini atau bukan itu. Ubah kode Anda jika itu memberi Anda masalah dan Anda menemukan cara untuk menghindari masalah ini tanpa membuat orang lain.
Jadi selain Anda tidak menyukai hal-hal, Anda ingin menginvestasikan banyak waktu untuk melakukan perubahan. Tuliskan masalah yang Anda miliki sekarang. Tuliskan bagaimana desain baru Anda akan menyelesaikan masalah. Cari tahu nilai peningkatan dan biaya untuk melakukan perubahan Anda. Kemudian - dan ini yang paling penting - pastikan bahwa Anda memiliki waktu untuk menyelesaikan perubahan itu, atau Anda akan berakhir setengah di negara bagian ini, setengah di negara bagian itu, dan itulah situasi terburuk yang mungkin terjadi. (Saya pernah mengerjakan proyek dengan 13 jenis string yang berbeda, dan tiga upaya setengah-setengah yang dapat diidentifikasi untuk melakukan standarisasi pada satu jenis)
sumber
Kategori "OOP" jauh lebih besar dan lebih abstrak daripada yang Anda gambarkan. Itu tidak peduli dengan semua ini. Itu peduli tentang tanggung jawab yang jelas, kohesi, penggabungan Jadi pada level yang Anda tanyakan, tidak masuk akal untuk bertanya tentang "latihan OOPS".
Yang mengatakan, untuk contoh Anda:
Menurut saya ada kesalahpahaman tentang apa arti MVC. Anda memanggil UI Anda "MVC", secara terpisah dari logika bisnis dan "backend" kontrol Anda. Tetapi bagi saya, MVC mencakup seluruh aplikasi web:
Ada beberapa asumsi dasar yang sangat penting di sini:
Yang penting: UI adalah bagian dari MVC. Bukan sebaliknya (seperti pada diagram Anda). Jika Anda menerimanya, maka model gemuk sebenarnya cukup bagus - asalkan mereka memang tidak mengandung barang-barang yang seharusnya tidak mereka miliki.
Perhatikan bahwa "model gemuk" berarti bahwa semua logika bisnis ada dalam kategori Model (paket, modul, apa pun nama dalam bahasa pilihan Anda). Kelas individual jelas harus terstruktur OOP dengan cara yang baik per pedoman pengkodean apa pun yang Anda berikan sendiri (yaitu, beberapa baris kode maksimum per kelas atau per metode, dll.).
Juga perhatikan bahwa bagaimana layer data diimplementasikan memiliki konsekuensi yang sangat penting; terutama apakah lapisan model dapat berfungsi tanpa lapisan data (misalnya, untuk pengujian unit, atau untuk DB di memori murah pada laptop pengembang, bukan Oracle DB mahal atau apa pun yang Anda miliki). Tapi ini benar-benar detail implementasi pada tingkat arsitektur yang kita lihat sekarang. Jelas di sini Anda masih ingin memiliki pemisahan, yaitu saya tidak ingin melihat kode yang memiliki logika domain murni langsung disambungkan dengan akses data, sangat menyatukan ini. Topik untuk pertanyaan lain.
Untuk kembali ke pertanyaan Anda: Menurut saya ada banyak tumpang tindih antara arsitektur baru Anda dan skema MVC yang telah saya jelaskan, jadi Anda tidak berada di jalan yang sepenuhnya salah, tetapi Anda tampaknya menciptakan kembali beberapa hal, atau menggunakannya karena lingkungan / perpustakaan pemrograman Anda saat ini menyarankan demikian. Sulit mengatakannya untukku. Jadi saya tidak bisa memberi Anda jawaban yang tepat tentang apakah yang Anda maksudkan itu baik atau buruk. Anda dapat mengetahuinya dengan memeriksa apakah setiap "benda" memiliki tepat satu kelas yang bertanggung jawab atasnya; apakah semuanya sangat kohesif dan berpasangan rendah. Itu memberi Anda indikasi yang baik, dan, menurut pendapat saya, cukup untuk desain OOP yang baik (atau tolok ukur yang sama, jika Anda mau).
sumber