Tidak pernah menggunakan Strings di Java? [Tutup]

73

Saya menemukan entri blog yang mengecilkan penggunaan Strings di Java untuk membuat kode Anda kurang semantik, menyarankan bahwa Anda harus menggunakan kelas bungkus tipis sebagai gantinya. Ini adalah contoh sebelum dan sesudah entri yang disediakan untuk menggambarkan masalah:

public void bookTicket(
  String name,
  String firstName,
  String film,
  int count,
  String cinema);

public void bookTicket(
  Name name,
  FirstName firstName,
  Film film,
  Count count,
  Cinema cinema);

Dalam pengalaman saya membaca blog pemrograman saya sampai pada kesimpulan bahwa 90% adalah omong kosong, tapi saya bertanya-tanya apakah ini poin yang valid. Entah bagaimana rasanya tidak benar bagi saya, tetapi saya tidak bisa menunjukkan dengan tepat apa yang salah dengan gaya pemrograman.

DPM
sumber
Diskusi yang relevan pada Wiki asli: "apa itu toString () dari?"
Christoffer Hammarström
5
Oh wow, posting blog itu membuat saya merasa sakit secara fisik
Rob
5
Saya telah membaca setidaknya satu buku (yang mungkin merupakan edisi pertama dari Kode Lengkap) yang merekomendasikan mengetik semua tipe primitif Anda untuk memiliki nama dari domain masalah. Ide yang sama
user16764
9
pernyataan yang dimulai dengan "tidak pernah" adalah "selalu" salah!
Florents Tselai
1
Orang itu adalah CTO ?!
Hyangelo

Jawaban:

88

Enkapsulasi ada untuk melindungi program Anda dari perubahan . Apakah representasi Nama akan berubah? Jika tidak, maka Anda membuang-buang waktu dan YAGNI berlaku.

Sunting: Saya sudah membaca posting blog dan dia punya ide bagus secara fundamental. Masalahnya adalah dia terlalu jauh. Sesuatu seperti String orderId itu benar - benar buruk, karena mungkin "!"£$%^&*())ADAFVFitu tidak valid orderId. Ini berarti bahwa Stringmewakili nilai yang jauh lebih mungkin daripada yang ada orderIds. Namun, untuk sesuatu seperti name, maka Anda tidak dapat memperkirakan nama yang valid atau tidak, dan ada Stringyang valid name.

Pada contoh pertama, Anda (benar) mengurangi input yang mungkin menjadi input yang valid saja. Dalam contoh kedua, Anda tidak mencapai penyempitan input yang valid.

Edit lagi: Pertimbangkan kasus input yang tidak valid. Jika Anda menulis "Gareth Gobulcoque" sebagai nama Anda, itu akan terlihat konyol, itu tidak akan menjadi akhir dari dunia. Jika Anda memasukkan OrderID yang tidak valid, kemungkinan besar itu tidak akan berfungsi.

DeadMG
sumber
5
Dengan id pesanan, saya mungkin masih lebih suka menambahkan tanda centang pada kode yang menerima id dan meninggalkan String. Kelas seharusnya menyediakan metode untuk operasi dengan data. Jika beberapa kelas hanya memeriksa validitas beberapa data dan kemudian tidak melakukan apa-apa, menurut saya ini tidak tepat. OOP itu bagus, tapi jangan berlebihan.
Malcolm
13
@ Malcolm: Tapi kemudian Anda tidak memiliki cara untuk mengetahui String mana yang divalidasi kecuali untuk memvalidasi mereka lagi dan lagi.
DeadMG
7
"[...] Anda tidak dapat memprediksi nama yang valid atau tidak, dan String apa pun adalah nama yang valid." Saya kira salah satu kekuatan dari teknik ini adalah jika parameter Anda bertipe Name, Anda tidak dapat secara tidak sengaja melewatkan nilai String yang tidak terkait. Di mana Anda mengharapkan Name, hanya Nameakan mengkompilasi, dan String "Saya berutang $ 5" tidak dapat diterima secara tidak sengaja. (Catat ini tidak terkait dengan validasi nama apa pun!)
Andres F.
6
Membuat jenis yang spesifik untuk penggunaan tertentu menambah kekayaan semantik, dan membantu menambah keamanan. Plus, membuat jenis untuk mewakili nilai-nilai spesifik sangat membantu ketika menggunakan wadah IoC untuk autowire kelas Anda - mudah untuk menyelesaikan kacang yang tepat untuk digunakan ketika itu adalah satu-satunya kacang yang terdaftar untuk kelas tertentu. Lebih banyak upaya diperlukan ketika itu hanya salah satu dari banyak string terdaftar.
Dathan
3
@DeadMG Itu argumentatif, dan bukan sesuatu yang bisa kita selesaikan dalam komentar. Saya akan mengatakan kesalahan penempatan nilai tipe primitif memang terjadi, dan pengerasan antarmuka Anda adalah salah satu cara untuk memperbaiki situasi.
Andres F.
46

Itu hanya gila :)

Makach
sumber
2
Itu pendapat saya juga, tetapi masalahnya adalah saya ingat saran ini di "Kode Lengkap". Tentu saja tidak semua yang ada di buku itu tidak dapat disangkal, tetapi setidaknya itu membuat saya berpikir dua kali sebelum menolak sebuah ide.
DPM
8
Anda baru saja menyebutkan beberapa slogan tanpa pembenaran nyata. Bisakah Anda menguraikan pendapat Anda? Beberapa pola dan fitur bahasa yang diterima, misalnya, mungkin terlihat seperti kompleksitas tambahan, namun menawarkan sesuatu yang berharga sebagai balasannya (misalnya: pengetikan statis)
Andres F.
12
Akal sehat sama sekali tidak umum.
Kaz Dragon
1
+1, untuk ini saya akan menambahkan bahwa ketika sebuah bahasa mendukung parameter bernama, dan IDEnya bagus, seperti halnya dengan C # dan VS2010, maka orang tidak harus mengimbangi kurangnya fitur dalam bahasa dengan pola gila . Tidak perlu untuk kelas bernama X dan kelas bernama Y, jika orang dapat menulis var p = new Point(x: 10, y:20);Juga, tidak seperti Bioskop yang berbeda dari string. Saya akan mengerti jika seseorang berurusan dengan kuantitas fisik, seperti tekanan, suhu, energi, di mana unit berbeda dan beberapa tidak bisa negatif. Penulis blog perlu mencoba fungsional.
Pekerjaan
1
+1 untuk "Don't over-engineer!"
Ivan
23

Saya setuju dengan penulis, kebanyakan. Jika ada setiap perilaku aneh ke lapangan, seperti validasi dari perintah id, maka saya akan membuat kelas untuk mewakili jenis itu. Poin keduanya bahkan lebih penting: jika Anda memiliki seperangkat bidang yang mewakili beberapa konsep seperti alamat, buat kelas untuk konsep itu. Jika Anda memprogram di Jawa, Anda harus membayar mahal untuk pengetikan statis. Anda mungkin juga mendapatkan semua nilai yang Anda bisa.

kevin cline
sumber
2
Bisakah komentar yang downvoter?
kevin cline
Berapa harga tinggi yang kami bayarkan untuk pengetikan statis?
Richard Tingle
@RichardTingle: Waktu untuk mengkompilasi ulang kode dan memulai ulang JVM untuk setiap perubahan kode. Waktu yang hilang ketika Anda membuat perubahan yang kompatibel dengan sumber tetapi tidak kompatibel biner dan hal-hal yang perlu dikompilasi tidak dan Anda mendapatkan "MissingMethodException".
kevin cline
Saya tidak pernah mengalami masalah dengan aplikasi java (apa dengan kompilasi berkelanjutan biasanya sudah dikompilasi pada saat saya menjalankannya) tetapi ini adalah poin yang adil dengan PERANG web yang pengalihan pekerjaan tampaknya sedikit lebih lambat
Richard Tingle
16

Jangan lakukan itu; itu akan terlalu rumit dan Anda tidak akan membutuhkannya

... adalah jawaban yang saya tulis di sini 2 tahun yang lalu. Sekarang, saya tidak begitu yakin; sebenarnya, dalam beberapa bulan terakhir saya mulai memigrasikan kode lama ke format ini, bukan karena saya tidak punya yang lebih baik untuk dilakukan tetapi karena saya benar-benar membutuhkannya untuk mengimplementasikan fitur baru atau mengubah yang sudah ada. Saya mengerti keengganan otomatis yang orang lain di sini telah melihat kode itu, tapi saya pikir ini adalah sesuatu yang pantas dipikirkan.


Manfaat

Manfaat utama adalah kemampuan untuk memodifikasi dan memperluas kode. Jika Anda menggunakan

class Point {
    int x,y;
    // other point operations
}

daripada hanya melewati beberapa bilangan bulat di sekitar - yang merupakan sesuatu yang, sayangnya, banyak antarmuka lakukan - maka jauh lebih mudah untuk kemudian menambahkan dimensi lain. Atau ubah tipenya menjadi double. Jika Anda menggunakan List<Author> authorsatau List<Person> authorsalih-alih List<String> authorsnanti menjadi lebih mudah untuk menambahkan lebih banyak informasi ke apa yang penulis wakili. Menulisnya seperti ini rasanya seperti saya menyatakan yang jelas, tetapi dalam praktiknya saya telah bersalah menggunakan string dengan cara ini berkali-kali sendiri, terutama dalam kasus di mana itu tidak jelas pada awalnya maka saya akan membutuhkan lebih dari Sebuah benang.

Saat ini saya sedang mencoba untuk memperbaiki daftar string yang saling terkait di seluruh kode saya karena saya memerlukan lebih banyak informasi di sana, dan saya merasakan sakitnya: \

Di luar itu, saya setuju dengan penulis blog bahwa itu membawa lebih banyak informasi semantik , sehingga memudahkan pembaca untuk mengerti. Sementara parameter sering diberi nama yang bermakna dan mendapatkan jalur dokumentasi khusus, ini sering tidak terjadi dengan bidang atau penduduk setempat.

Manfaat akhirnya adalah keamanan jenis , untuk alasan yang jelas, tetapi di mata saya itu adalah hal kecil di sini.

Kekurangannya

Butuh waktu lebih lama untuk menulis . Menulis kelas kecil cepat dan mudah tetapi tidak mudah, terutama jika Anda membutuhkan banyak kelas ini. Jika Anda mendapati diri Anda berhenti setiap 3 menit untuk menulis beberapa kelas pembungkus baru, itu juga dapat merusak konsentrasi Anda. Saya ingin berpikir, bagaimanapun, bahwa upaya ini biasanya hanya akan terjadi pada tahap pertama dari penulisan kode apa pun; Saya biasanya bisa mendapatkan ide yang cukup bagus dengan cepat tentang entitas apa yang perlu dilibatkan.

Ini bisa melibatkan banyak setter redundan (atau konstruksi) dan pengambil . Penulis blog memberikan contoh yang benar-benar jelek dari new Point(x(10), y(10))bukan new Point(10, 10), dan saya ingin menambahkan bahwa penggunaan juga mungkin melibatkan hal-hal seperti Math.max(p.x.get(), p.y.get())bukan Math.max(p.x, p.y). Dan kode panjang sering dianggap lebih sulit untuk dibaca, dan memang begitu. Tetapi dalam semua kejujuran, saya punya perasaan banyak kode bergerak objek di sekitar, dan hanya metode tertentu yang membuatnya, dan bahkan lebih sedikit membutuhkan akses ke rincian berpasir nya (yang bukan OOPy toh).

Belum pasti

Saya akan mengatakan apakah ini membantu keterbacaan kode masih bisa diperdebatkan. Ya, lebih banyak informasi semantik, tetapi kode lebih panjang. Ya, lebih mudah untuk memahami peran masing-masing lokal, tetapi lebih sulit untuk memahami apa yang dapat Anda lakukan dengannya kecuali Anda pergi dan membaca dokumentasinya.


Seperti sebagian besar sekolah pemikiran lain, saya pikir tidak sehat untuk mengambil yang satu ini secara ekstrem. Saya tidak melihat diri saya pernah memisahkan koordinat x dan y untuk masing-masing dari tipe yang berbeda. Saya pikir Countitu tidak perlu ketika intharus mencukupi. Saya tidak suka unsigned intpenggunaan dalam C - sementara secara teori bagus, itu tidak memberi Anda informasi yang cukup, dan tidak melarang memperluas kode Anda nanti untuk mendukung -1 ajaib itu. Terkadang Anda memang membutuhkan kesederhanaan.

Saya pikir posting blog itu sedikit di sisi ekstrim. Tetapi secara keseluruhan, saya telah belajar dari pengalaman yang menyakitkan bahwa ide dasar di baliknya dibuat dari hal-hal yang benar.

Saya tidak menyukai kode over-engineered. Saya benar-benar melakukannya. Tetapi digunakan dengan benar, saya tidak berpikir ini over-engineering.

ek
sumber
5

Meskipun agak berlebihan, saya sering berpikir sebagian besar barang yang saya lihat kurang direkayasa.

Ini bukan hanya "Keamanan", Salah satu hal yang sangat baik tentang Java adalah bahwa hal itu banyak membantu Anda dengan mengingat / mencari tahu apa yang perlu / diharapkan metode panggilan perpustakaan apa pun.

Perpustakaan Java TERBURUK (sejauh ini) yang pernah saya kerjakan ditulis oleh seseorang yang sangat menyukai Smalltalk dan memodelkan perpustakaan GUI setelah itu untuk membuatnya bertindak lebih seperti smalltalk - masalahnya adalah setiap metode mengambil objek dasar yang sama, tetapi sebenarnya tidak bisa MENGGUNAKAN segala sesuatu yang objek dasar bisa dilemparkan, jadi Anda kembali menebak apa yang harus dilewatkan ke dalam metode dan tidak tahu jika Anda telah gagal sampai runtime (Sesuatu yang saya harus berurusan dengan setiap saat ketika saya sedang bekerja di C).

Masalah lain - Jika Anda menyebarkan string, int, koleksi, dan array tanpa objek, yang Anda miliki hanyalah bola data tanpa makna. Ini tampak alami ketika Anda berpikir dalam hal perpustakaan bahwa "beberapa aplikasi" akan digunakan, tetapi ketika merancang seluruh aplikasi itu jauh lebih membantu untuk menetapkan makna (kode) untuk semua data Anda di tempat di mana data didefinisikan dan hanya berpikir dalam dalam hal bagaimana objek tingkat tinggi ini berinteraksi. Jika Anda melewati primitif alih-alih objek maka Anda - menurut definisi - mengubah data di tempat yang berbeda dari yang ditentukan (Ini juga mengapa saya benar-benar tidak suka Setters dan Getters - konsep yang sama, Anda beroperasi pada data yang bukan milik Anda).

Akhirnya, jika Anda mendefinisikan objek yang terpisah untuk semuanya, Anda selalu memiliki tempat yang bagus untuk memvalidasi semuanya - misalnya jika Anda membuat objek untuk kode pos dan kemudian menemukan bahwa Anda harus memastikan bahwa kode pos selalu menyertakan ekstensi 4 digit, Anda memiliki tempat sempurna untuk mengatakannya.

Sebenarnya itu bukan ide yang buruk. Memikirkan hal itu, saya bahkan tidak yakin saya akan mengatakan itu over-engineered sama sekali, hanya saja lebih mudah untuk dikerjakan dalam hampir segala hal - satu-satunya pengecualian adalah menjamurnya kelas-kelas kecil tetapi kelas-kelas Java sangat ringan dan mudah untuk menulis bahwa ini bahkan bukan biaya (mereka bahkan dapat dihasilkan).

Saya akan sangat tertarik untuk melihat proyek java yang ditulis dengan baik yang sebenarnya memiliki terlalu banyak kelas yang didefinisikan (Di mana hal itu membuat program lebih sulit), saya mulai berpikir bahwa tidak mungkin memiliki terlalu banyak kelas.

Bill K
sumber
3

Saya pikir Anda harus melihat konsep ini dari titik awal yang lain. Lihatlah dari sudut pandang seorang perancang basis data: jenis-jenis yang dilewatkan dalam contoh pertama tidak mendefinisikan parameter Anda dengan cara yang unik, apalagi cara yang bermanfaat.

public void bookTicket(
  String name,
  String firstName,
  String film,
  int count,
  String cinema);

Dua parameter diperlukan untuk menentukan pelindung aktual yang memesan tiket, Anda mungkin memiliki dua film berbeda dengan nama yang identik (misalnya remake), Anda mungkin memiliki film yang sama dengan nama yang berbeda (misalnya terjemahan). Rantai bioskop tertentu mungkin memiliki kantor cabang yang berbeda, jadi bagaimana Anda akan mengatasinya dalam string dan dengan cara yang konsisten (mis. Anda menggunakan $chain ($city)atau $chain in $cityatau bahkan sesuatu yang lain dan bagaimana Anda akan memastikan bahwa ini adalah digunakan secara konsisten. Yang terburuk sebenarnya adalah menentukan patron Anda melalui dua parameter, fakta bahwa nama depan dan nama keluarga diberikan tidak menjamin pelanggan yang valid (dan Anda tidak dapat membedakan dua John Does).

Jawabannya adalah mendeklarasikan tipe, tetapi ini akan jarang menjadi pembungkus tipis seperti yang saya tunjukkan di atas. Kemungkinan besar, mereka akan berfungsi sebagai penyimpanan data Anda atau digabungkan dengan beberapa jenis database. Jadi suatu Cinemaobjek kemungkinan akan memiliki nama, lokasi, ... dan dengan cara itu Anda menghilangkan ambiguitas tersebut. Jika mereka pembungkus tipis, mereka kebetulan.

Jadi, IMHO posting blog hanya mengatakan "pastikan Anda melewati jenis yang benar", penulisnya hanya membuat pilihan yang terlalu dibatasi untuk memilih tipe data dasar khususnya (yang merupakan pesan yang salah).

Alternatif yang diusulkan lebih baik:

public void bookTicket(
  Name name,
  FirstName firstName,
  Film film,
  Count count,
  Cinema cinema);

Di sisi lain, saya pikir posting blog terlalu jauh dengan membungkus semuanya. Countterlalu umum, saya dapat menghitung apel atau jeruk dengan itu, menambahkannya dan masih memiliki situasi di tangan saya di mana sistem tipe memungkinkan saya untuk melakukan operasi yang tidak masuk akal. Anda tentu saja dapat menerapkan logika yang sama seperti di blog dan menentukan jenis CountOfOrangesdll, tetapi itu juga konyol.

Untuk apa nilainya, saya benar-benar menulis sesuatu seperti

public Ticket bookTicket(
  Person patron,
  Film film,
  int numberOfTickets,
  Cinema cinema);

Singkat cerita: Anda tidak boleh melewati variabel tidak masuk akal; satu-satunya saat Anda benar-benar menentukan objek dengan nilai yang tidak menentukan objek yang sebenarnya, adalah ketika Anda menjalankan kueri (misalnya public Collection<Film> findFilmsWithTitle(String title)) atau ketika Anda menyusun bukti konsep. Usahakan sistem tipe Anda bersih, jadi jangan gunakan tipe yang terlalu umum (misalnya film yang diwakili oleh a String) atau terlalu membatasi / spesifik / dibuat-buat (misalnya Countalih-alih int). Gunakan jenis yang mendefinisikan objek Anda secara unik dan tidak ambigu jika memungkinkan dan layak.

sunting : ringkasan yang bahkan lebih pendek. Untuk aplikasi kecil (misalnya bukti konsep): mengapa repot dengan desain yang rumit? Cukup gunakan Stringatau intterus saja.

Untuk aplikasi besar: apakah benar bahwa Anda memiliki banyak kelas yang terdiri dari satu bidang dengan tipe data dasar? Jika Anda memiliki sedikit kelas seperti itu, Anda hanya memiliki objek "normal", tidak ada yang istimewa terjadi di sana.

Saya merasakan ide merangkum string, ... hanyalah sebuah desain yang tidak lengkap: terlalu rumit untuk aplikasi kecil, tidak cukup lengkap untuk aplikasi besar.

Egon
sumber
Saya mengerti maksud Anda, tetapi saya berpendapat Anda mengasumsikan lebih dari yang penulis nyatakan. Sejauh yang kita tahu, modelnya hanya memiliki String untuk pelindung, String untuk film dan sebagainya. Fungsionalitas hipotetis tambahan itu sebenarnya adalah inti dari masalah sehingga dia sangat "linglung" untuk menghilangkan bahwa ketika membuat kasusnya atau dia pikir kita harus memberikan kekuatan semantik hanya karena itu. Sekali lagi, untuk semua yang kita tahu, itu yang dia pikirkan.
DPM
@ Jubbat: memang saya menganggap lebih dari apa yang penulis katakan. Tapi maksud saya adalah apakah Anda memiliki aplikasi sederhana, dalam hal ini terlalu rumit. Untuk skala itu, rawatan bukan masalah dan perbedaan semantik menghambat kecepatan pengkodean Anda. Jika di sisi lain, aplikasi Anda besar, menjadi bermanfaat untuk mendefinisikan tipe Anda dengan benar (tetapi tidak mungkin menjadi pembungkus sederhana). IMHO, contoh-contohnya tidak meyakinkan atau memiliki kekurangan desain utama di luar titik yang ia coba buat.
Egon
2

Bagi saya melakukan ini sama dengan menggunakan wilayah di C #. Umumnya jika Anda merasa ini diperlukan untuk membuat kode Anda dapat dibaca maka Anda memiliki masalah yang lebih besar Anda harus menghabiskan waktu Anda.

Tom Squires
sumber
2
+1 untuk menyiratkan pengobatan gejala daripada penyebabnya.
Pekerjaan
2

Saya akan mengatakan itu adalah ide yang sangat bagus dalam bahasa dengan fitur seperti typedef sangat diketik.

Di Jawa Anda tidak memiliki ini sehingga menciptakan seluruh kelas baru untuk hal-hal ini berarti biaya mungkin lebih besar daripada manfaatnya. Anda juga bisa mendapatkan 80% dari manfaat dengan berhati-hati tentang penamaan variabel / parameter Anda.

jk.
sumber
0

Akan lebih baik, JIKA String (end Integer, dan ... berbicara hanya tentang String) belum final, sehingga kelas-kelas ini bisa MENJADI beberapa (dibatasi) String dengan makna dan masih dapat dikirim ke beberapa objek independen yang tahu bagaimana menangani tipe dasar (tanpa berbicara bolak-balik).

Dan "selamat tinggal" dari itu meningkat ketika ada misalnya. pembatasan semua nama.

Tetapi ketika membuat beberapa aplikasi (bukan perpustakaan), selalu memungkinkan untuk memperbaikinya. Jadi saya lebih suka memulai tanpa itu.

pengguna470365
sumber
0

Sebagai contoh: dalam sistem yang kami kembangkan saat ini, ada banyak entitas berbeda yang dapat diidentifikasi oleh berbagai jenis pengidentifikasi (karena penggunaan sistem eksternal), kadang-kadang bahkan jenis entitas yang sama. Semua pengidentifikasi adalah Strings - jadi jika seseorang mencampur jenis id mana yang harus dilewatkan sebagai parameter, tidak ada kesalahan waktu kompilasi yang ditampilkan, tetapi program akan meledak saat runtime. Ini sering terjadi. Jadi saya harus mengatakan bahwa tujuan utama prinsip ini bukan untuk melindungi terhadap perubahan (walaupun juga berfungsi untuk perubahan), tetapi untuk melindungi diri dari bug. Lagi pula, jika seseorang mendesain API, itu harus mencerminkan konsep domain, sehingga secara konsep berguna untuk mendefinisikan kelas domain-spesifik - semuanya tergantung pada apakah ada keteraturan dalam pikiran pengembang,

thSoft
sumber
@ downvoter: bisakah Anda memberikan penjelasan?
thSoft