Saya dalam proyek sistem terdistribusi yang ditulis dalam java di mana kami memiliki beberapa kelas yang sesuai dengan objek bisnis dunia nyata yang sangat kompleks. Objek-objek ini memiliki banyak metode yang sesuai dengan tindakan yang dapat diterapkan pengguna (atau agen lain) pada objek tersebut. Akibatnya, kelas-kelas ini menjadi sangat kompleks.
Pendekatan arsitektur sistem umum telah menyebabkan banyak perilaku terkonsentrasi pada beberapa kelas dan banyak skenario interaksi yang mungkin.
Sebagai contoh dan untuk menjaga hal-hal mudah dan jelas, katakanlah Robot dan Mobil adalah kelas dalam proyek saya.
Jadi, di kelas Robot saya akan memiliki banyak metode dalam pola berikut:
- tidur(); isSleepAvaliable ();
- bangun(); isAwakeAvaliable ();
- berjalan (Arah); isWalkAvaliable ();
- menembak (Arah); isShootAvaliable ();
- turnOnAlert (); isTurnOnAlertAvailable ();
- turnOffAlert (); isTurnOffAlertAvailable ();
- mengisi ulang (); isRechargeAvailable ();
- matikan(); isPowerOffAvailable ();
- stepInCar (Mobil); isStepInCarAvailable ();
- stepOutCar (Mobil); isStepOutCarAvailable ();
- Penghancuran diri(); isSelfDestructAvailable ();
- mati(); isDieAvailable ();
- hidup(); isAwake (); isAlertOn (); getBatteryLevel (); getCurrentRidingCar (); getAmmo ();
- ...
Di kelas Mobil, akan serupa:
- nyalakan(); isTurnOnAvaliable ();
- matikan(); isTurnOffAvaliable ();
- berjalan (Arah); isWalkAvaliable ();
- mengisi bahan bakar (); isRefuelAvailable ();
- Penghancuran diri(); isSelfDestructAvailable ();
- jatuh(); isCrashAvailable ();
- isOperational (); isOn (); getFuelLevel (); getCurrentPassenger ();
- ...
Masing-masing (Robot dan Mobil) diimplementasikan sebagai mesin negara, di mana beberapa tindakan mungkin di beberapa negara dan beberapa tidak. Tindakan mengubah status objek. Metode tindakan melempar IllegalStateException
ketika dipanggil dalam keadaan tidak valid dan isXXXAvailable()
metode mengetahui apakah tindakan itu mungkin dilakukan saat itu. Meskipun beberapa mudah disimpulkan dari keadaan (misalnya, dalam kondisi tidur, tersedia terjaga), beberapa tidak (untuk menembak, itu harus terjaga, hidup, memiliki amunisi dan tidak mengendarai mobil).
Lebih lanjut, interaksi antara objek juga rumit. Misalnya, Mobil hanya dapat menampung satu penumpang Robot, jadi jika yang lain mencoba masuk, pengecualian harus dilemparkan; Jika mobil menabrak, penumpang harus mati; Jika robot mati di dalam kendaraan, dia tidak bisa keluar, bahkan jika mobil itu sendiri ok; Jika Robot ada di dalam Mobil, dia tidak bisa masuk yang lain sebelum melangkah keluar; dll.
Hasilnya, seperti yang sudah saya katakan, kelas-kelas ini menjadi sangat kompleks. Untuk memperburuk keadaan, ada ratusan skenario yang mungkin terjadi ketika Robot dan Mobil berinteraksi. Lebih jauh, banyak dari logika itu perlu mengakses data jarak jauh di sistem lain. Hasilnya adalah unit-testing menjadi sangat sulit dan kami memiliki banyak masalah pengujian, satu menyebabkan yang lain dalam lingkaran setan:
- Pengaturan testcases sangat kompleks, karena mereka perlu menciptakan dunia yang sangat kompleks untuk berolahraga.
- Jumlah tes sangat besar.
- Test suite membutuhkan waktu beberapa jam untuk berjalan.
- Cakupan pengujian kami sangat rendah.
- Kode pengujian cenderung ditulis berminggu-minggu atau berbulan-bulan lebih lambat daripada kode yang mereka uji, atau tidak pernah sama sekali.
- Banyak tes yang rusak juga, terutama karena persyaratan kode yang diuji berubah.
- Beberapa skenario sangat kompleks, sehingga gagal pada batas waktu selama penyiapan (kami mengonfigurasi batas waktu dalam setiap pengujian, dalam kasus terburuk yang berlangsung 2 menit dan bahkan selama ini mereka kehabisan waktu, kami memastikan bahwa itu bukan loop yang tidak terbatas).
- Bug secara teratur masuk ke lingkungan produksi.
Skenario Robot dan Mobil itu terlalu menyederhanakan apa yang kita miliki dalam kenyataan. Jelas, situasi ini tidak dapat dikelola. Jadi, saya meminta bantuan dan saran untuk: 1, Mengurangi kompleksitas kelas; 2. Sederhanakan skenario interaksi antara objek saya; 3. Kurangi waktu pengujian dan jumlah kode yang akan diuji.
EDIT:
Saya pikir saya tidak jelas tentang mesin negara. Robot itu sendiri adalah mesin negara, dengan negara "tidur", "terjaga", "mengisi ulang", "mati", dll. Mobil adalah mesin negara lain.
EDIT 2: Jika Anda ingin tahu tentang apa sebenarnya sistem saya, kelas-kelas yang berinteraksi adalah hal-hal seperti Server, Alamat IP, Disk, Cadangan, Pengguna, SoftwareLicense, dll. Skenario Robot dan Mobil hanyalah kasus yang saya temukan itu akan cukup sederhana untuk menjelaskan masalah saya.
sumber
Jawaban:
The Negara pola desain mungkin berguna, jika Anda belum menggunakannya.
Ide utama adalah bahwa Anda membuat kelas batin untuk setiap negara berbeda - sehingga untuk melanjutkan contoh, Anda
SleepingRobot
,AwakeRobot
,RechargingRobot
danDeadRobot
semua akan kelas, menerapkan antarmuka umum.Metode pada
Robot
kelas (sepertisleep()
danisSleepAvaliable()
) memiliki implementasi sederhana yang mendelegasikan ke kelas batin saat ini.Perubahan status diimplementasikan dengan menukar kelas batin saat ini dengan yang berbeda.
Keuntungan dengan pendekatan ini adalah bahwa masing-masing kelas negara secara dramatis lebih sederhana (karena hanya mewakili satu negara yang mungkin), dan dapat diuji secara independen. Bergantung pada bahasa implementasi Anda (tidak ditentukan), Anda mungkin masih terkendala untuk memiliki semuanya dalam file yang sama, atau Anda mungkin dapat membagi hal-hal menjadi file sumber yang lebih kecil.
sumber
Saya tidak tahu kode Anda, tetapi dengan mengambil contoh metode "sleep", saya akan menganggap itu adalah sesuatu seperti kode "simplistic" berikut:
Saya pikir Anda harus membuat perbedaan antara tes integrasi dan tes unit . Menulis tes yang berjalan melalui seluruh kondisi mesin Anda tentu merupakan tugas besar. Menulis tes unit yang lebih kecil yang menguji apakah metode tidur Anda berfungsi dengan benar lebih mudah. Pada titik ini Anda tidak perlu tahu apakah status mesin telah diperbarui dengan benar atau apakah "mobil" dengan benar merespons kenyataan bahwa keadaan mesin telah diperbarui oleh "robot" ... dll. Anda mendapatkannya.
Dengan kode di atas, saya akan mengejek objek "machineState" dan tes pertama saya adalah:
Pendapat pribadi saya adalah menulis tes unit sekecil itu harus menjadi hal pertama yang harus dilakukan. Anda menulis itu:
Menjalankan tes kecil ini harus sangat cepat, dan Anda seharusnya tidak memiliki apa pun untuk diinisialisasi sebelumnya seperti "dunia kompleks" Anda. Misalnya, jika ini adalah aplikasi yang didasarkan pada wadah IOC (katakanlah, Spring), Anda tidak perlu menginisialisasi konteks selama tes unit.
Setelah Anda menyelesaikan persentase yang baik dari kode kompleks Anda dengan tes unit, Anda dapat mulai membangun tes integrasi yang lebih memakan waktu dan lebih kompleks.
Akhirnya, ini bisa dilakukan apakah kode Anda rumit (seperti yang Anda katakan sekarang), atau setelah Anda refactored.
sumber
Saya sedang membaca bagian "Asal" dari artikel Wikipedia tentang Prinsip Segregasi Antarmuka , dan saya teringat akan pertanyaan ini.
Saya akan mengutip artikel itu. Masalahnya: "... satu kelas Pekerjaan utama .... kelas gemuk dengan banyak metode khusus untuk berbagai klien yang berbeda." Solusinya: "... lapisan antarmuka antara kelas Pekerjaan dan semua kliennya ..."
Masalah Anda tampaknya permutasi dari yang dimiliki Xerox. Alih-alih satu kelas gemuk Anda memiliki dua, dan dua kelas gemuk ini berbicara satu sama lain, bukan banyak klien.
Bisakah Anda mengelompokkan metode berdasarkan jenis interaksi dan kemudian membuat kelas antarmuka untuk setiap jenis? Misalnya: RobotPowerInterface, RobotNavigationInterface, RobotAlarmInterface kelas?
sumber