Apakah menggabungkan dengan string “lebih longgar” daripada dengan metode kelas?


18

Saya memulai proyek grup sekolah di Jawa, menggunakan Swing. Ini adalah GUI langsung pada aplikasi desktop Database.

Profesor memberi kami kode dari proyek tahun lalu sehingga kami bisa melihat bagaimana dia melakukan sesuatu. Kesan awal saya adalah bahwa kode ini jauh lebih rumit dari yang seharusnya, tetapi saya membayangkan programmer sering berpikir ini ketika melihat kode yang tidak hanya mereka tulis.

Saya berharap menemukan alasan mengapa sistemnya baik atau buruk. (Saya bertanya kepada profesor dan dia berkata saya akan melihat nanti mengapa itu lebih baik, yang tidak memuaskan saya.)

Pada dasarnya, untuk menghindari sambungan antara objek, model (logika bisnis), dan pandangannya yang bertahan lama, semuanya dilakukan dengan string. Objek yang bertahan yang disimpan dalam database adalah hashtables dari string, dan model dan tampilan "berlangganan" satu sama lain, memberikan kunci string untuk "peristiwa" yang mereka ikuti.

Ketika suatu peristiwa dipicu, tampilan atau model mengirimkan string ke semua pelanggannya yang memutuskan apa yang harus dilakukan untuk peristiwa itu. Misalnya, dalam salah satu metode pendengar tindakan tampilan (saya percaya ini hanya menetapkan bicycleMakeField pada objek yang dapat bertahan):

    else if(evt.getSource() == bicycleMakeField)
    {
        myRegistry.updateSubscribers("BicycleMake", bicycleMakeField.getText());
    }

Panggilan itu akhirnya sampai ke metode ini dalam model Kendaraan:

public void stateChangeRequest(String key, Object value) {

            ... bunch of else ifs ...

    else
    if (key.equals("BicycleMake") == true)
    {
                ... do stuff ...

Profesor itu mengatakan bahwa cara melakukan hal-hal ini lebih dapat diperluas dan dipelihara daripada memiliki pandangan cukup memanggil metode pada objek logika bisnis. Dia mengatakan tidak ada hubungan antara pandangan dan model karena mereka tidak tahu keberadaan satu sama lain.

Saya pikir ini adalah jenis sambungan yang lebih buruk, karena tampilan dan model harus menggunakan string yang sama agar dapat bekerja. Jika Anda menghapus tampilan atau model, atau membuat kesalahan ketik dalam string, Anda tidak mendapatkan kesalahan kompilasi. Itu juga membuat kode jauh lebih lama daripada yang saya pikir perlu.

Saya ingin membahas hal ini dengannya, tetapi dia menggunakan pengalaman industrinya untuk membantah setiap argumen yang mungkin saya, siswa yang tidak berpengalaman, buat. Apakah ada beberapa keuntungan dari pendekatannya yang saya lewatkan?


Untuk memperjelas, saya ingin membandingkan pendekatan di atas, dengan memiliki pandangan yang jelas digabungkan dengan model. Misalnya, Anda bisa meneruskan objek model kendaraan ke tampilan, lalu mengubah "make" kendaraan, lakukan ini:

vehicle.make = bicycleMakeField.getText();

Ini akan mengurangi 15 baris kode yang saat ini HANYA digunakan untuk mengatur merek kendaraan di satu tempat, menjadi satu baris kode yang dapat dibaca. (Dan karena operasi semacam ini dilakukan ratusan kali di seluruh aplikasi, saya pikir ini akan menjadi kemenangan besar untuk keterbacaan serta keamanan.)


Memperbarui

Pemimpin tim saya dan saya merestrukturisasi kerangka kerja seperti yang kami inginkan dengan menggunakan pengetikan statis, memberi tahu profesor, dan akhirnya memberinya demo. Dia cukup dermawan untuk membiarkan kita menggunakan kerangka kerja kita selama kita tidak meminta bantuannya, dan jika kita dapat menjaga kecepatan tim kita hingga kecepatan - yang tampaknya adil bagi kita.


12
PL. Saya pikir profesor Anda harus memperbarui dirinya sendiri dan tidak menggunakan kode lama. Di akademisi tidak ada alasan untuk menggunakan versi JAVA dari 2006 saat ini 2012.
Farmor

3
Tolong terus kami kirim ketika Anda akhirnya mendapatkan jawabannya.
JeffO

4
Terlepas dari kualitas kode atau kebijaksanaan teknik, Anda harus menjatuhkan kata "sihir" dari deskripsi Anda tentang string yang digunakan sebagai pengidentifikasi. "Senar ajaib" menyiratkan bahwa sistem ini tidak logis, dan itu akan mewarnai pandangan Anda dan meracuni setiap diskusi yang Anda lakukan dengan instruktur Anda. Kata-kata seperti "kunci," "nama," atau "pengidentifikasi" lebih netral, mungkin lebih deskriptif, dan lebih cenderung menghasilkan percakapan yang bermanfaat.
Caleb

1
Benar. Saya belum menyebut mereka string ajaib saat berbicara dengannya, tapi Anda benar, tidak perlu membuatnya terdengar lebih buruk dari itu.
Philip

2
Pendekatan yang dijelaskan oleh profesor Anda (memberi tahu pendengar melalui acara yang disebut string) bisa bagus. Sebenarnya enum bisa lebih masuk akal, tetapi ini bukan masalah terbesar. Masalah sebenarnya yang saya lihat di sini adalah bahwa objek yang menerima pemberitahuan adalah model itu sendiri, dan kemudian Anda harus mengirim secara manual berdasarkan jenis acara. Dalam bahasa di mana fungsi adalah kelas satu Anda akan mendaftarkan fungsi , bukan objek ke acara. Di Jawa, saya kira orang harus menggunakan semacam objek yang membungkus fungsi tunggal
Andrea

Jawaban:


32

Pendekatan yang disarankan profesor Anda paling baik diketik dengan ketat dan salah di hampir setiap level.

Kopling sebaiknya dikurangi dengan inversi ketergantungan , yang melakukannya dengan pengiriman polimorfik, daripada memasang kabel pada sejumlah kasing.


3
Anda menulis "hampir setiap level", tetapi Anda belum menulis mengapa itu (menurut Anda) tidak cocok dalam contoh ini. Akan menarik untuk diketahui.
hakre

1
@akre: Tidak ada kasus yang cocok, setidaknya tidak di Jawa. Saya katakan, "salah di hampir setiap level", karena lebih baik daripada tidak menggunakan tipuan sama sekali dan hanya membuang semua kode di satu tempat. Namun menggunakan konstanta string sihir (global) sebagai dasar untuk pengiriman manual hanyalah cara berbelit-belit dalam menggunakan objek global pada akhirnya.
back2dos

Jadi argumen Anda adalah: Tidak pernah ada kasus yang cocok, jadi ini bukan satu juga? Itu bukan argumen dan sebenarnya tidak terlalu membantu.
hakre

1
@akre: Argumen saya adalah, bahwa pendekatan ini buruk karena sejumlah alasan (paragraf 1 - cukup alasan untuk menghindari ini diberikan dalam penjelasan yang diketik secara ketat) dan tidak boleh digunakan, karena ada yang lebih baik (paragraf 2). ).
back2dos

3
@ hakre Saya bisa membayangkan situasi yang akan menjamin pendekatan ini akan terjadi jika seorang pembunuh berantai menyadap Anda ke kursi Anda dan memegang gergaji di atas kepala Anda, tertawa mania, dan memerintahkan Anda untuk menggunakan string literal di mana-mana untuk pengiriman logika. Singkatnya, mengandalkan fitur bahasa untuk melakukan hal-hal semacam ini lebih disukai. Sebenarnya, saya bisa memikirkan satu kasus lain yang melibatkan buaya dan ninja, tapi itu sedikit lebih terlibat.
Rob

19

Profesor Anda melakukan kesalahan . Dia mencoba sepenuhnya memisahkan pandangan dan model dengan memaksa komunikasi untuk bepergian melalui string di kedua arah (tampilan tidak tergantung pada model dan model tidak tergantung pada tampilan) , yang benar-benar mengalahkan tujuan desain berorientasi objek dan MVC. Kedengarannya seperti bukannya menulis kelas dan merancang arsitektur berbasis OO, dia menulis modul berbeda yang hanya menanggapi pesan berbasis teks.

Agar lebih adil bagi profesor, ia mencoba membuat di Jawa apa yang tampak sangat mirip dengan antarmuka .NET yang disebut INotifyPropertyChanged, yang memberi tahu pelanggan tentang peristiwa perubahan dengan cara melewati string yang menentukan properti mana yang telah berubah. Dalam. NET setidaknya, antarmuka ini terutama digunakan untuk berkomunikasi dari model data untuk melihat objek dan elemen GUI (mengikat).

Jika dilakukan dengan benar , mengambil pendekatan ini dapat memiliki beberapa manfaat¹:

  1. Tampilan tergantung pada Model, tetapi Model tidak perlu bergantung pada Tampilan. Ini adalah Pembalikan Kontrol yang tepat .
  2. Anda memiliki satu tempat di kode Tampilan Anda yang mengelola merespons untuk mengubah pemberitahuan dari model, dan hanya satu tempat untuk berlangganan / berhenti berlangganan, mengurangi kemungkinan kebocoran memori referensi menggantung.
  3. Ada sedikit cara pengkodean overhead ketika Anda ingin menambah atau menghapus acara dari penerbit acara. Di .NET, ada mekanisme terbitkan / berlangganan built-in yang disebut eventstetapi di Jawa Anda harus membuat kelas atau objek dan menulis kode berlangganan / berhenti berlangganan untuk setiap acara.
  4. Kelemahan terbesar dengan pendekatan ini adalah bahwa kode berlangganan dapat tidak sinkron dengan kode penerbitan. Namun, ini juga bermanfaat di beberapa lingkungan pengembangan. Jika acara baru dikembangkan dalam model dan tampilan belum diperbarui untuk berlangganan, maka tampilan masih akan berfungsi. Demikian juga, jika suatu peristiwa dihapus, Anda memiliki beberapa kode mati, tetapi masih bisa dikompilasi dan dijalankan.
  5. Di .NET setidaknya, Reflection digunakan dengan sangat efektif di sini untuk secara otomatis memanggil getter dan setter tergantung pada string yang diteruskan ke pelanggan.

Jadi, singkatnya (dan untuk menjawab pertanyaan Anda), itu bisa dilakukan, tetapi pendekatan yang diambil profesor Anda adalah kesalahan karena tidak membiarkan setidaknya ketergantungan satu arah antara Tampilan dan Model. Hanya saja tidak praktis, terlalu akademis, dan bukan contoh yang baik tentang bagaimana menggunakan alat yang ada.


¹ Cari di Google untuk INotifyPropertyChangedmenemukan sejuta cara yang ditemukan orang untuk mencoba menghindari penggunaan string sihir saat menerapkannya.


Sepertinya Anda menggambarkan SABUN di sini. : D ... (tapi ya, saya mengerti maksud Anda dan Anda benar)
Konrad Rudolph

11

enums (atau public static final Strings) harus digunakan sebagai pengganti String sihir.

Profesor Anda tidak mengerti OO . Misalnya memiliki kelas Kendaraan konkret?
Dan agar kelas ini memahami cara membuat sepeda?

Kendaraan harus abstrak dan harus ada subkelas beton yang disebut Sepeda. Saya akan bermain sesuai aturannya untuk mendapatkan nilai bagus dan kemudian melupakan pengajarannya.


3
Kecuali inti dari latihan ini adalah meningkatkan kode menggunakan prinsip OO yang lebih baik. Dalam hal ini, refactor pergi.
joshin4colours

2
@ joshin4colours Jika StackExchange menilai itu, Anda akan benar; tetapi karena siswa kelas adalah orang yang sama dengan ide dia akan memberi kita nilai buruk karena dia tahu implementasinya lebih baik.
Dan Neely

7

Saya pikir Anda memiliki poin bagus, string sihirnya buruk. Seseorang di tim saya harus men-debug masalah yang disebabkan oleh string sihir beberapa minggu yang lalu (tapi itu dalam kode mereka sendiri yang mereka tulis jadi saya tutup mulut). Dan itu terjadi ... di industri !!

Keuntungan dari pendekatannya adalah bahwa mungkin lebih cepat untuk kode, terutama untuk proyek demo kecil. Kerugiannya adalah bahwa ia cenderung untuk kesalahan ketik dan semakin sering string digunakan semakin besar kemungkinan kesalahan ketik. Dan jika Anda ingin mengubah string ajaib, string refactoring lebih sulit daripada variabel refactoring karena alat refactoring mungkin juga menyentuh string yang berisi string ajaib (sebagai substring) tetapi bukan dirinya sendiri string ajaib, dan tidak boleh disentuh. Juga, Anda mungkin perlu menyimpan kamus atau indeks string sihir agar pengembang baru tidak akan mulai menemukan string sihir baru untuk tujuan yang sama, dan tidak akan membuang waktu mencari string sihir yang ada.

Dalam konteks ini, sepertinya Enum mungkin lebih baik daripada string sihir atau bahkan konstanta string global. Juga, IDE akan sering memberi Anda semacam bantuan kode (lengkapi-otomatis, refactor, temukan-semua-referensi, dll ...) ketika Anda menggunakan string atau Enums yang konstan. IDE tidak akan menawarkan banyak bantuan jika Anda menggunakan string ajaib.


Saya setuju bahwa enum lebih baik daripada string literal. Tetapi meskipun begitu, apakah lebih baik memanggil "stateChangeRequest" dengan kunci enum, atau langsung memanggil metode pada model secara langsung? Either way, model dan tampilan digabungkan di satu tempat itu.
Philip

@ Pilip: Kalau begitu, saya kira untuk memisahkan model dan melihat pada titik itu, Anda mungkin perlu semacam kelas kontroler untuk melakukan itu ...
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner - Ya. Arsitektur MVC adalah yang paling terpisah
cdeszaq

Benar, jika lapisan lain akan membantu memisahkan mereka, saya tidak akan membantahnya. Tapi layer itu masih bisa diketik secara statis dan menggunakan metode aktual daripada string atau pesan enum untuk menghubungkannya.
Philip

6

Menggunakan 'pengalaman industri' adalah argumen yang buruk. Profesor Anda perlu mengajukan argumen yang lebih baik dari itu :-).

Seperti yang telah dinyatakan orang lain, penggunaan String sihir tentu disukai, menggunakan enum dianggap jauh lebih aman. Enum memiliki makna dan ruang lingkup , string sihir tidak. Selain itu, cara di mana profesor Anda memisahkan masalah dianggap sebagai teknik yang lebih tua dan lebih rapuh daripada yang digunakan saat ini.

Saya melihat bahwa @backtodos telah membahas hal ini ketika saya menjawab ini, jadi saya hanya akan menambahkan pendapat saya bahwa menggunakan Inversion of Control (yang merupakan bentuk Ketergantungan Injeksi, Anda akan menjalankan kerangka Spring dalam industri sebelum terlalu lama. ..) dianggap sebagai cara yang lebih modern untuk menangani jenis decoupling ini.


2
Sepenuhnya setuju akademisi harus memiliki persyaratan yang jauh lebih tinggi daripada industri. Akademisi == cara teoretis terbaik; Industri == cara praktis terbaik
Farmor

3
Sayangnya, beberapa dari mereka yang mengajarkan hal ini di akademisi melakukannya karena mereka tidak dapat memotongnya di industri. Sisi positifnya, beberapa dari mereka di dunia akademis cemerlang dan tidak boleh disembunyikan di kantor perusahaan.
FrustratedWithFormsDesigner

4

Mari kita perjelas; ada pasti beberapa kopling di sana. Fakta bahwa ada topik yang dapat dilanggankan seperti itu merupakan titik penggabungan, sebagaimana juga sifat dari pesan yang lain (seperti "string tunggal"). Karena itu, pertanyaannya kemudian menjadi apakah kopling lebih besar bila dilakukan melalui string atau tipe. Jawabannya tergantung pada apakah Anda khawatir tentang kopling yang didistribusikan dari waktu ke waktu atau ruang: apakah pesan akan disimpan dalam file sebelum dibaca nanti, atau akan dikirim ke proses lain (terutama jika itu tidak ditulis dalam bahasa yang sama), menggunakan string dapat membuat segalanya lebih sederhana. Kelemahannya adalah tidak ada pemeriksaan tipe; kesalahan tidak akan terdeteksi sampai runtime (dan kadang-kadang bahkan tidak).

Strategi mitigasi / kompromi untuk Java bisa menggunakan antarmuka yang diketik, tetapi di mana antarmuka yang diketik itu dalam paket terpisah. Ini harus terdiri dari hanya Java interfacedan tipe nilai dasar (mis., Enumerasi, pengecualian). Seharusnya tidak ada implementasi antarmuka dalam paket itu. Kemudian setiap orang menerapkan menentang itu dan, jika menyampaikan pesan dengan cara yang kompleks diperlukan, implementasi tertentu dari delegasi Java dapat menggunakan string ajaib yang diperlukan. Itu juga membuatnya lebih mudah untuk memigrasi kode ke lingkungan yang lebih profesional seperti JEE atau OSGi, dan sangat membantu hal-hal kecil seperti pengujian ...


4

Apa yang profesor Anda usulkan adalah penggunaan "string sihir". Sementara itu memang "longgar pasangan" antara satu sama lain, memungkinkan untuk perubahan yang lebih mudah, itu adalah anti-pola; string harus sangat, SANGAT jarang digunakan untuk berisi atau mengontrol instruksi kode, dan ketika benar-benar harus terjadi, nilai string yang Anda gunakan untuk mengendalikan logika harus ditentukan secara terpusat dan terus-menerus sehingga ada SATU otoritas pada nilai yang diharapkan dari string tertentu.

Alasan mengapa sangat sederhana; string tidak diperiksa kompiler untuk sintaksis atau konsistensi dengan nilai-nilai lain (paling baik, mereka diperiksa untuk form yang tepat mengenai karakter escape dan pemformatan khusus bahasa lainnya dalam string). Anda dapat meletakkan apa pun yang Anda inginkan dalam sebuah string. Dengan demikian, Anda bisa mendapatkan algoritma / program yang rusak putus asa untuk dikompilasi, dan itu tidak sampai berjalan bahwa kesalahan terjadi. Pertimbangkan kode berikut (C #):

private Dictionary<string, Func<string>> methods;

private void InitDictionary() //called elsewhere
{
   methods = new Dictionary<string, Func<string>> 
      {{"Method1", ()=>doSomething()},
       {"MEthod2", ()=>doSomethingElse()}} //oops, fat-fingered it
}

public string RunMethod(string methodName)
{
   //very naive, but even a check for the key would generate a runtime error, 
   //not a compile-time one.
   return methods[methodName](); 
}

...

//this is what we thought we entered back in InitDictionary for this method...
var result = RunMethod("Method2"); //error; no such key

... semua kode ini dikompilasi, percobaan pertama, tetapi kesalahannya jelas dengan tampilan kedua. Ada banyak contoh pemrograman jenis ini, dan mereka semua tunduk pada kesalahan semacam ini, BAHKAN JIKA Anda mendefinisikan string sebagai konstanta (karena konstanta dalam. NET ditulis untuk manifes setiap perpustakaan di mana mereka digunakan, yang harus maka semua akan dikompilasi ulang ketika nilai yang didefinisikan diubah agar nilai tersebut berubah "secara global"). Karena kesalahan tidak ditangkap pada waktu kompilasi, itu harus ditangkap selama pengujian run-time, dan itu hanya dijamin dengan cakupan kode 100% dalam pengujian unit dengan latihan kode yang memadai, yang biasanya hanya ditemukan pada kegagalan-aman yang paling absolut. , sistem waktu nyata.


2

Sebenarnya, kelas-kelas itu masih sangat erat. Kecuali sekarang mereka digabungkan sedemikian rupa sehingga kompiler tidak dapat memberi tahu Anda ketika ada masalah!

Jika seseorang mengubah "BicycleMake" menjadi "BicyclesMake" maka tidak ada yang akan mengetahui bahwa semuanya rusak sampai runtime.

Kesalahan runtime jauh lebih mahal untuk diperbaiki daripada kompilasi kesalahan waktu - ini semua jenis kejahatan.

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.