Bagaimana cara terbaik menghindari penulisan kode GUI yang membengkak?


48

Saya menemukan setiap kali saya bekerja dengan kode GUI, kode cenderung menggembung lebih cepat daripada jenis kode lainnya. Tampaknya juga lebih sulit untuk refactor. Sedangkan dalam jenis kode lain saya dapat melakukan refactor dengan mudah - Saya menemukan saya dapat menguraikan kelas yang lebih besar menjadi bagian-bagian kecil dari fungsionalitas - dengan sebagian besar kerangka kerja GUI saya sering terikat pada kerangka kerja yang membutuhkan widget / kontrol / kelas apa saja untuk mengimplementasikan lebih banyak hal lebih langsung di widget / control / apa pun. Kadang-kadang ini karena perlu (a) mewarisi dari beberapa widget dasar / kontrol / hal atau (b) membutuhkan akses ke metode yang dilindungi.

Saya biasanya juga harus, misalnya, menanggapi berbagai macam input melalui sinyal / peristiwa / apa pun dari kerangka kerja untuk menerapkan semua mode berinteraksi dengan pengguna. Saya mungkin perlu dalam satu widget / kontrol GUI untuk menangani berbagai macam input / output yang mungkin termasuk:

  1. klik kanan / menu konteks
  2. bereaksi terhadap pilihan dari menu konteks - yang mungkin banyak
  3. cara khusus untuk melukis GUI
  4. bereaksi terhadap input keyboard
  5. tombol, kotak centang,
  6. dll

... sambil mengatur kelas di bawah GUI yang mewakili logika bisnis.

GUI lurus ke depan sederhana dapat memiliki kode tumbuh cukup cepat, bahkan ketika memisahkan logika bisnis dan menggunakan MVC, saya menemukan kode GUI adalah magnet besar untuk perubahan.

Apakah ada cara untuk mengelola kode GUI dengan cara yang waras dan menghindari membiarkannya menjadi jendela yang rusak? Atau apakah massa penangan kejadian acak / metode yang ditimpa benar-benar yang terbaik yang bisa kita lakukan untuk kode GUI?


4
Apa definisi tepat Anda tentang "mengasapi"?

Jawaban:


36

Yang perlu diingat tentang kode GUI adalah bahwa itu adalah event-driven, dan kode event-driven selalu akan memiliki penampilan massa penangan acara yang diorganisir secara acak. Di mana itu menjadi sangat berantakan adalah ketika Anda mencoba untuk memasukkan kode non-event-driven ke dalam kelas. Tentu, ini memiliki tampilan memberikan dukungan untuk penangan acara dan Anda dapat menjaga penangan acara Anda baik dan kecil, tetapi semua kode dukungan tambahan yang melayang-layang membuat sumber GUI Anda tampak membengkak dan berantakan.

Jadi apa yang dapat Anda lakukan tentang hal ini, dan bagaimana Anda dapat membuat hal-hal lebih mudah untuk diperbaiki? Yah, pertama-tama saya akan mengubah definisi refactoring saya dari sesuatu yang saya lakukan sesekali menjadi sesuatu yang saya lakukan terus menerus saat saya membuat kode. Mengapa? Karena Anda ingin refactoring memungkinkan Anda untuk lebih mudah memodifikasi kode Anda, dan bukan sebaliknya. Saya tidak hanya meminta Anda untuk mengubah semantik di sini, tetapi meminta Anda untuk melakukan sedikit senam mental agar dapat melihat kode Anda secara berbeda.

Tiga teknik refactoring yang saya temukan paling sering saya gunakan adalah Rename , Extract Method , dan Extract Class . Jika saya tidak pernah mempelajari satu pun refactoring lainnya, ketiganya akan tetap memungkinkan saya untuk menjaga kode saya tetap bersih dan terstruktur dengan baik, dan dari isi pertanyaan Anda, sepertinya saya mungkin akan menemukan diri Anda menggunakan tiga refactoring yang sama hampir secara konstan di Untuk menjaga kode GUI Anda tetap tipis dan bersih.

Anda dapat memiliki pemisahan terbaik GUI dan logika Bisnis di dunia, dan masih kode GUI dapat terlihat seperti kode yang telah diledakkan di tengahnya. Saran saya adalah bahwa tidak ada salahnya untuk memiliki kelas ekstra atau dua untuk membantu Anda untuk mengelola GUI Anda dengan benar, dan ini tidak perlu harus Anda View kelas jika Anda menerapkan pola MVC - meskipun sering Anda akan menemukan kelas perantara sangat mirip dengan pandangan Anda sehingga Anda akan sering merasakan keinginan untuk menggabungkannya demi kenyamanan. Menurut saya, tidak ada salahnya menambahkan lapisan khusus GUI tambahan untuk mengelola semua logika visual, namun Anda mungkin ingin mempertimbangkan manfaat dan biaya untuk melakukannya.

Karenanya saran saya adalah:

  • Jangan melakukan apa pun secara langsung di belakang GUI Anda kecuali untuk memohon dan menentukan bagaimana GUI akan terhubung ke View (atau lapisan perantara).
  • Jangan coba-coba setiap hal terkait tampilan menjadi satu kelas - atau bahkan satu kelas per jendela GUI - kecuali Anda masuk akal untuk melakukannya. Alternatif Anda adalah membuat banyak kelas yang kecil dan mudah dikelola untuk mengelola logika GUI Anda.
  • Ketika metode Anda mulai terlihat sedikit lebih besar dari 4-5 baris kode, periksa apakah ini perlu dan apakah mungkin untuk mengekstrak satu atau dua metode sehingga Anda dapat menjaga metode Anda ramping, bahkan jika ini berarti kelas dengan lebih banyak metode.
  • Jika kelas Anda mulai terlihat sangat besar, mulailah dengan menghapus SEMUA fungsi yang diduplikasi, dan kemudian lihat apakah Anda dapat mengelompokkan metode Anda secara logis sehingga Anda dapat mengekstraksi satu atau dua kelas lain.
  • Pikirkan tentang refactoring setiap kali Anda menulis satu baris kode. Jika Anda mendapatkan satu baris kode untuk bekerja, lihat apakah Anda bisa melakukan refactor untuk menghindari duplikasi fungsi, atau membuatnya sedikit lebih ramping tanpa mengubah perilaku.
  • Terimalah yang tak terhindarkan, bahwa Anda akan selalu merasa bahwa satu atau lain bagian dalam sistem Anda akan mulai merasa sedikit kembung, terutama jika Anda mengabaikan refactoring saat Anda pergi. Bahkan dengan basis kode yang diperhitungkan dengan baik, Anda masih dapat merasakan seolah-olah ada lebih banyak yang dapat Anda lakukan. Ini adalah kenyataan menulis perangkat lunak, bahwa Anda akan selalu merasa bahwa sesuatu yang lebih bisa dilakukan "lebih baik", jadi Anda perlu menemukan keseimbangan antara melakukan pekerjaan profesional, dan menyepuh emas.
  • Terima bahwa semakin bersih Anda mencoba dan menjaga kode Anda, semakin sedikit kode Anda yang kembung.

3
+1 Suka atau tidak, GUI menangani jutaan operasi terperinci dan itu artinya kode.
Patrick Hughes

Pengembang harus belajar menggunakan pengkodean event-driven untuk GUI.
David Gao

23

Saya pikir banyak masalah yang Anda alami dapat ditelusuri kembali ke penyebab sederhana. Sebagian besar pengembang tidak memperlakukan kode GUI seperti kode 'nyata'. Saya tidak punya bukti atau statistik di sini, hanya perasaan saya.

Mungkin mereka berpikir itu hanya ' presentasi ' dan tidak penting. ' Tidak ada logika bisnis di sana ', kata mereka, ' mengapa unit mengujinya '? Mereka tertawa ketika Anda menyebutkan orientasi objek dan menulis kode bersih. Mereka bahkan tidak MENCOBA untuk membuat segalanya lebih baik. Tidak ada struktur untuk memulai, mereka hanya menampar beberapa kode dan membiarkannya membusuk ketika orang lain menambahkan sentuhan mereka sendiri dari waktu ke waktu. Kekacauan yang indah, kode grafiti.

Kode GUI memiliki tantangan unik sehingga harus diperlakukan secara berbeda dan dengan rasa hormat. Perlu cinta dan pengembang yang ingin menulisnya. Yang akan membuatnya tipis dan memberinya struktur yang baik dan pola yang benar.


2
+1 untuk menyinggung persepsi kode GUI yang diperlakukan berbeda dengan kode non-gui. Saya telah kehilangan hitungan berapa kali saya mendengar seseorang berkata, "jangan repot-repot menguji GUI karena tidak hemat biaya dan selain itu sangat sulit untuk dilakukan". Saya biasanya menerjemahkan ke "Itu sulit dan saya terlalu malas untuk belajar bagaimana melakukannya!".
S.Robins

1
+1 Di tempat saya bekerja, kita sering tidak meninjau kode GUI - "itu hanya GUI, lewati saja". Dan aku sama bersalahnya dengan siapa pun. Yang aneh adalah bahwa dalam proyek pribadi saya, saya menghabiskan banyak waktu jika mencoba untuk mendapatkan kode GUI bersih yang bagus. Kira itu hanya masalah budaya.
HappyCat

8

Untuk beberapa alasan kode GUI menciptakan titik buta pada pengembang tentang pemisahan masalah. Mungkin itu karena semua tutorial menggabungkan semuanya menjadi satu kelas. Mungkin itu karena representasi fisik membuat segala sesuatu tampak lebih dekat daripada mereka. Mungkin itu karena kelas dibangun perlahan-lahan sehingga orang tidak menyadari bahwa mereka perlu refactoring, seperti katak pepatah yang direbus dengan perlahan menyalakan panas.

Apa pun alasannya, solusinya adalah membuat kelas Anda jauh lebih kecil. Saya melakukan ini dengan terus bertanya pada diri sendiri apakah mungkin untuk memasukkan apa yang saya ketikkan ke dalam kelas yang terpisah. Jika mungkin untuk dimasukkan ke kelas lain, dan saya bisa memikirkan nama yang masuk akal dan sederhana untuk kelas itu, maka saya melakukannya.


6

Anda mungkin ingin melihat pola Model View Presenter / Passive View. Ray Ryan memberi ceramah yang bagus di Google IO tentang praktik arsitektur terbaik untuk GWT.

http://www.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html

Sangat mudah untuk mengabstraksi gagasan ke kerangka kerja dan bahasa lain. Manfaat utama MVP (menurut saya) adalah unit-testability. Dan Anda hanya mendapatkan itu, jika kode Anda tidak membengkak dan tidak spageti (menilai dari pertanyaan Anda, inilah yang Anda inginkan). Ia bekerja dengan memperkenalkan lapisan logika tampilan yang disebut presenter. Tampilan sebenarnya dipisahkan dari ini melalui antarmuka (dan dengan demikian dapat dengan mudah diejek dalam unit test). Sekarang, karena layer logika tampilan Anda (presenter) dibebaskan dari internal kerangka kerja GUI yang konkret, Anda dapat mengaturnya seperti kode biasa dan Anda tidak terikat dengan misalnya hierarki warisan Swings. Idealnya Anda bisa beralih implementasi GUI dalam kerangka kerja yang berbeda selama mereka sesuai dengan antarmuka yang sama.


1
+1. MVP berfokus tepat pada bagaimana mengekstrak logika GUI ke dalam kelas yang terpisah, yang seringkali sangat berbeda dari apa yang orang pahami ketika mereka berbicara tentang MVC.
Doc Brown

5

Jawaban saya terdiri dari empat bagian: struktur, kesederhanaan, pengujian, dan sintaksis.

Tiga yang pertama sangat sulit dilakukan!

Struktur berarti membayar banyak perhatian untuk menggunakan jumlah kode paling sedikit dan jumlah maksimum kerangka kerja, perpustakaan dll.

Kesederhanaan berarti menjaga segala sesuatunya sederhana dari desain awal hingga implementasi aktual. Menjaga navigasi tetap sederhana, menggunakan plugin sederhana, menjaga tata letak cukup 'polos' semua akan membantu di sini. Mereka sekarang dapat 'dijual' ke klien / pengguna yang dapat dengan cepat melihat keuntungan halaman yang berfungsi pada pc, ipad, ponsel dan perangkat lainnya.

Pengujian berarti termasuk alat pengujian peramban (webrat dan capybara muncul dalam pikiran dengan pekerjaan rel saya) yang menangkap masalah lintas-browser di muka ketika kode yang lebih baik dapat dirancang untuk menghadapinya di awal sebagai lawan dari sering 'tambalan' kode oleh pengembang yang berbeda karena 'ditemukan' oleh pengguna browser yang berbeda.

Sintaksis. Sangat membantu untuk menggunakan pemeriksa kode / IDE / editor-plugin, dll. Untuk HTML, CSS, Javascript, dll. Keuntungan yang diperoleh browser dengan bisa menangani HTML yang tidak berfungsi dengan baik akan merugikan Anda ketika browser yang berbeda bekerja secara berbeda dengan itu, jadi alat yang memeriksa format HTML Anda sangat penting. Memiliki HTML yang terbentuk dengan baik sangat membantu dalam memiliki HTML yang tidak diblokan sebagai kode yang buruk harus memiliki visibilitas lebih.


4

Solusi yang saya temukan adalah kode deklaratif. Menggunakan kode prosedural yang adil adalah resep untuk kode GUI spaghetti. Tentu, "cara khusus untuk melukis widget" mungkin akan tetap kode. Tetapi ini adalah kode yang diisolasi dalam sebuah kelas. Penangan acara, pintasan keyboard, ukuran jendela - semua hal berantakan paling baik dinyatakan.


4

Ada banyak jawaban bagus di sini.

Satu hal yang telah membantu saya menyederhanakan kode GUI adalah memastikan bahwa GUI memiliki model data sendiri.

Untuk mengambil contoh sederhana, jika saya memiliki GUI dengan 4 bidang entri teks, maka saya memiliki kelas data terpisah yang menyimpan konten dari 4 bidang entri teks tersebut. GUI yang lebih rumit membutuhkan lebih banyak kelas data.

Saya mendesain GUI sebagai model - view. Model GUI dikendalikan oleh pengontrol aplikasi dari model aplikasi - view - controller. Tampilan aplikasi adalah model GUI, bukan kode GUI itu sendiri.


2

Aplikasi seperti Pengolah Kata, editor Grafik, dll. Memiliki antarmuka yang kompleks dan kodenya tidak bisa sederhana. Namun, untuk aplikasi bisnis, GUI tidak harus terlalu rumit tetapi tetap bagaimana caranya.

Beberapa kunci untuk menyederhanakan GUI adalah (sebagian besar berlaku untuk .NET):

  1. Berusaha keras untuk desain yang lebih sederhana bila memungkinkan. Hindari perilaku mewah jika tidak dipanggil oleh bisnis.

  2. Gunakan penyedia kontrol yang baik.

  3. Jangan membuat fungsi kontrol kustom di kode klien itu sendiri. Alih-alih, buat kontrol pengguna yang memperluas kontrol asli sedemikian rupa sehingga Anda bisa mencerminkan perilaku spesifik Anda di kontrol daripada dalam kode formulir penggunaan / halaman.

  4. Gunakan kerangka kerja (bahkan rumah yang dikembangkan) untuk menangani internasionalisasi, manajemen sumber daya, gaya, dll. Sehingga Anda tidak mengulangi kode ini di setiap UI.

  5. Gunakan komponen (atau kerangka kerja) untuk navigasi.

  6. Buat dialog standar untuk kesalahan, peringatan, konfirmasi, dll.


1

Terapkan Desain Berorientasi Objek ke kode Anda, dan untuk pengembangan UI:

  1. Pisahkan presentasi dan model Gunakan pustaka / kerangka kerja MV-apa pun, atau tulis sendiri, untuk membantu memisahkan logika tampilan / pengontrol dari model data. Semua komunikasi dengan backend harus dilakukan di dalam model, dan status model harus selalu sinkron dengan backend.
  2. Decoupling Jika objek A tahu tentang objek B, maka A dapat memanggil metode pada B, tetapi B tidak boleh tahu tentang A. Sebaliknya A dapat mendengarkan acara dari B. Itu memastikan tidak ada ketergantungan melingkar. Jika aplikasi Anda memiliki banyak acara di antara komponen, maka buat EventBus, atau manfaatkan kerangka kerja yang digerakkan oleh acara seperti Penerbangan Twitter.
  3. Render parsial vs penuh Jika tampilan Anda adalah tabel atau daftar item, Anda mungkin tergoda untuk membuat metode seperti "tambah", "hapus" untuk menyisipkan / menghapus satu item ke / dari koleksi. Kode Anda dapat dengan mudah mengasapi ketika Anda harus mendukung penyortiran dan pagination. Jadi saran saya adalah: cukup render ulang seluruh tampilan bahkan ketika ada perubahan parsial. Bagaimana dengan kinerja? baik jika koleksimu besar, maka kamu harus tetap melakukan pagination. Pengembang web: pastikan event handler Anda didelegasikan ke elemen root tampilan yang tidak berubah.
  4. Lihat model Saat kondisi tampilan Anda menjadi terlalu rumit untuk dipertahankan, misalnya, tampilan Tabel harus melacak data baris, data kolom, urutan, baris yang saat ini diperiksa (jika mendukung multi-periksa), dll, Anda mungkin harus buat objek ViewModel untuk kondisi tersebut. Objek tampilan Anda harus memanggil setter pada Model View jika ada perubahan pada UI (misalnya: pengguna memeriksa baris); dan itu harus menanggapi acara perubahan ViewModel dengan memperbarui UI. Biasanya Anda harus menghindari memperbarui UI jika acara perubahan dipicu oleh UI.

Ini adalah aplikasi kecil tapi non-sepele untuk membantu mengilustrasikan beberapa poin saya. Anda dapat menemukan kode dan diagram interaksi tampilan / model di sini: https://github.com/vanfrankie/pushpopbox


0

Anda ingin melihat konsep "penyatuan data" . Ini adalah cara menghubungkan elemen UI ke elemen model abstrak dengan cara deklaratif sehingga elemen model secara otomatis disinkronkan dengan konten UI. Ada banyak manfaat dari pendekatan ini, misalnya tidak harus menulis event handler sendiri untuk menyinkronkan data.

Ada dukungan penyatuan data untuk banyak kerangka kerja UI, misalnya .NET dan Eclipse / JFace .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.