Java'da Asla Dizeler kullanmayın mu? [kapalı]


73

Kodunuzun anlamsallıktan yoksun olması için Java'da Strings kullanımından vazgeçen bir blog girişine rastladım, bunun yerine ince sarmalayıcı sınıfları kullanmanız gerektiğini önerdim. Bu, söz konusu girişin konuyu açıklamak için sunduğu önceki ve sonraki örneklerdir:

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);

Tecrübelerime göre, programlama bloglarını okudum,% 90’ın saçmalık olduğu sonucuna vardım, ancak bunun geçerli bir nokta olup olmadığını merak ediyorum. Her nasılsa bana doğru gelmiyor ama tam olarak programlama tarzına neyin yanlış geldiğini tam olarak belirleyemedim.


Orijinal Wiki ile ilgili tartışma: "Bu toString () nedir?"
Christoffer Hammarström

5
Vay, bu blog yazısı beni fiziksel olarak hasta hissettirdi
Rob

5
Tüm ilkel türlerinizin problem etki alanından adlarını yazmasını tavsiye eden en az bir kitap okudum (bu, Kod Tamamlama'nın ilk basımı olabilir). Aynı fikir.
user16764 4

9
"asla" ile başlayan bir ifade "her zaman" yanlıştır!
Florents Tselai

1
O adam CTO mu?
Hyangelo

Yanıtlar:


88

Kapsülleme programı değişime karşı korumak için var . Bir ismin temsili değişecek mi? Olmazsa, zamanınızı boşa harcıyorsunuz ve YAGNI başvuruyor.

Düzenleme: Blog gönderisini okudum ve temelde iyi bir fikri var. Sorun şu ki, onu çok fazla ölçeklendirdi. Gibi bir String orderId şey gerçekten kötü, çünkü muhtemelen "!"£$%^&*())ADAFVFgeçerli değil orderId. Bu String, geçerli orderIds değerlerinden çok daha olası değerleri temsil ettiği anlamına gelir . Ancak, a gibi bir şey için name, geçerli bir adın ne olabileceğini ya da olmayacağını tahmin edemezsiniz ve herhangi Stringbiri geçerli olabilir name.

İlk örnekte, olası girdileri yalnızca geçerli olanlara indirgiyorsunuz (doğru). İkinci durumda, olası geçerli girdilerin daraltılmasını sağlayamadınız.

Tekrar düzenle: Geçersiz giriş durumunu düşünün. "Gareth Gobulcoque" u sizin adınız olarak yazarsanız, aptalca görünecek, dünyanın sonu olmayacak. Geçersiz bir OrderID girerseniz, bunun basit bir şekilde işe yaramayacağına dair ihtimaller var.


5
Sipariş kimlikleri ile muhtemelen kimlikleri kabul eden kodlara bir çek eklemeyi ve Dizeden ayrılmayı tercih ederim. Sınıfların veriyle çalışmak için yöntemler sağlaması gerekiyordu. Bazı sınıflar sadece bazı verilerin geçerliliğini kontrol eder ve sonra hiçbir şey yapmazsa, bu bana uygun görünmüyor. OOP iyidir, fakat aşırıya kaçmamalısınız.
Malcolm

13
@Malcolm: Fakat onları tekrar tekrar doğrulamak dışında hangi Dizelerin onaylandığını bilmenin hiçbir yolu yok.
DeadMG

7
"[...] geçerli bir adın ne olabileceğini veya olmayacağını tahmin edemezsiniz ve herhangi bir Dize geçerli bir addır." Sanırım bu tekniğin bir gücü, eğer parametreniz tipse, Nameyanlışlıkla başka bir ilgisiz String değerini geçemezsiniz. Beklediğiniz yerde Name, sadece bir Namederleme olacak ve "Sana 5 $ borcum var" dizesi yanlışlıkla kabul edilemez. (Bunun herhangi bir ad doğrulamasının alakası olmadığını unutmayın!)
Andres F.

6
Belirli bir kullanıma özgü türler oluşturmak, anlamsal zenginlik katar ve güvenlik eklemenize yardımcı olur. Ayrıca, belirli değerleri temsil etmek için türler oluşturmak, sınıflarınızı otomatik düzenlemek için bir IoC kabı kullanırken çok yardımcı olur - belirli bir sınıfa kayıtlı tek fasulye olduğunda doğru fasulyeyi çözmek kolaydır. Kayıtlı birçok karakterden sadece biri olduğunda çok daha fazla çaba gerekir.
Dathan

3
@DeadMG Bu tartışmacı ve yorumlarda çözebileceğimiz bir şey değil. İlkel türlerin yanlış yer değiştirmesinin gerçekleştiğini söyleyebilirim ve arayüzlerinizi sertleştirmek durumu iyileştirmenin bir yoludur.
Andres F.

46

Bu sadece delilik :)


2
Benim düşüncem de bu, ama mesele şu ki, "Kod Tamamlandı" konusundaki öneriyi hatırlıyorum. Elbette bu kitaptaki her şey tartışılmaz değil, ama en azından bir fikri reddetmeden önce iki kez düşündürmemi sağlıyor.
DPM

8
Az önce herhangi bir gerçek gerekçe göstermeden sloganlardan söz ettin. Fikrinizi detaylandırabilir misiniz? Bazı kabul edilen kalıplar ve dil özellikleri, örneğin, daha fazla karmaşıklık gibi görünebilir, ancak karşılığında değerli bir şey sunabilir (örneğin: statik yazarak)
Andres F.

12
Sağduyu, ortak olan herhangi bir şeydir.
Kaz Ejderi

1
+1, buna ek olarak bir dil adlandırılmış parametreleri desteklediğinde ve IDE iyi olduğunda, C # ve VS2010'da olduğu gibi, o zaman dilin özelliklerinin eksikliğini çılgın desenlerle telafi etmek zorunda kalmayacağını ekleyeceğim. . X adında bir sınıfa ve Y adında bir sınıfa gerek yoktur, eğer biri yazabiliyorsa var p = new Point(x: 10, y:20);, Cinema'nın bir dizgeden farklı olduğu gibi değildir. Birinin farklı ve bazılarının negatif olamayacağı, basınç, sıcaklık, enerji gibi fiziksel büyüklüklerle uğraşacak mı diye anlarım. Blogun yazarı işlevsel olarak denenmeli.
İş

1
"Aşırı mühendislik yapma!"
Ivan

23

Çoğunlukla yazar ile aynı fikirdeyim. Bir alana özgü herhangi bir davranış varsa, sipariş kimliğinin doğrulanması gibi, o zaman bu türü temsil edecek bir sınıf oluşturacağım. İkinci noktası daha da önemlidir: eğer bir adres gibi bazı kavramları temsil eden bir alan kümeniz varsa, o zaman bu kavram için bir sınıf oluşturun. Java ile programlama yapıyorsanız, statik yazım için yüksek bir fiyat ödersiniz. Elinden geldiğince tüm değerleri alabilirsin.


2
Redüktör yorum yapabilir mi?
kevin cline

Statik yazım için ödediğimiz yüksek fiyat nedir?
Richard Tingle,

@RichardTingle: Her kod değişikliği için kodu tekrar derleme ve JVM'yi yeniden başlatma zamanı. Kaynağa uyumlu, ancak ikili uyumsuz bir değişiklik yaptığınızda ve yeniden derlenmesi gereken şeyler yaptığınızda kaybedilen zaman değildir ve "MissingMethodException" alırsınız.
Kevin Cline

Java uygulamaları ile ilgili herhangi bir sorun yaşamadım (sürekli derleme ile genellikle kaçtığımda çoktan derlendi) ama bu yeniden dağıtmanın biraz daha yavaş göründüğü web WARS ile ilgili adil bir nokta
Richard Tingle

16

Yapma; işleri karmaşıklaştıracak ve ihtiyacın olmayacak

... 2 yıl önce buraya yazacağım cevap. Şimdi, yine de çok emin değilim; Aslında, son aylarda eski kodu bu formata taşımaya başladım, yapacak daha iyi bir şeyim olmadığı için değil, yeni özellikleri uygulamak veya var olanları değiştirmek için gerçekten gerekli olan şeylerdi. Buradaki diğer kişilerin bu kodu gördüğü otomatik isteksizlikleri anlıyorum, ancak bunun ciddi düşünceyi hak eden bir şey olduğunu düşünüyorum.


Yararları

Avantajlardan biri , kodu değiştirme ve uzatma yeteneğidir . Eğer kullanırsan

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

etrafa birkaç tamsayı geçirmek yerine - ki maalesef birçok arayüzün yaptığı bir şey - o zaman daha sonra başka bir boyut eklemek çok daha kolay hale gelir. Veya türünü değiştirin double. Kullanırsanız List<Author> authorsveya List<Person> authorsonun yerine List<String> authorsdaha sonra yazarın temsil ettiği şeye daha fazla bilgi eklemek daha kolay hale gelir. Bu şekilde yazmak, açık bir şekilde belirttiğimi hissediyormuş gibi hissettiriyor, ancak pratikte, özellikle başlangıçta bariz olmadığı durumlarda, kendimden bu kadar çok tel kullandım. dizi.

Şu anda kodum boyunca iç içe geçmiş bazı dize listelerini yeniden düzenlemeye çalışıyorum çünkü orada daha fazla bilgiye ihtiyacım var ve acıyı hissediyorum: \

Bunun ötesinde, blog yazarıyla daha semantik bilgi taşıdığı ve okuyucunun anlamasını kolaylaştırdığı konusunda hemfikirim . Parametrelere genellikle anlamlı adlar verilmiş ve özel bir dokümantasyon satırı elde edilmiş olsa da, bu genellikle alanlar veya yerliler için geçerli değildir.

Nihai yarar, bariz nedenlerden dolayı tür güvenliğidir , ancak gözlerim burada küçük bir şey.

Dezavantajları

Yazması daha uzun sürer . Küçük bir sınıf yazmak , özellikle bu derslerin çoğuna ihtiyacınız varsa , hızlı ve kolaydır ancak zahmetsiz değildir . Kendinizi her 3 dakikada bir yeni sarmalayıcı sınıfı yazmak için durdurarak bulursanız, konsantrasyonunuza da zarar verebilir. Bununla birlikte, bu çaba durumunun genellikle herhangi bir kod parçasını yazmanın ilk aşamasında gerçekleşeceğini düşünmek isterim; Varlıkların dahil olması gerekenler hakkında genellikle çok iyi bir fikir edinebilirim.

Çok fazla gereksiz ayarlayıcı (veya inşaat) ve alıcı içerebilir . Blog yazarı, new Point(x(10), y(10))bunun yerine gerçekten çirkin bir örnek veriyor ve new Point(10, 10)bir kullanımın Math.max(p.x.get(), p.y.get())bunun yerine bunun gibi şeyleri de içerebileceğini eklemek istiyorum Math.max(p.x, p.y). Ve uzun kod genellikle okunması daha zor ve adil bir şekilde kabul edilir. Ancak tüm dürüstlük içerisinde, nesnelerin etrafından dolaştığı bir sürü kod hissi duyuyorum ve yalnızca bazı yöntemler yaratıyor ve daha da azının detaylarına (hatta OOPy değil) erişmesi gerekiyor.

tartışılabilir

Bunun kodun okunabilirliği konusunda yardımcı olup olmadığını tartışmalı olarak söyleyebilirim . Evet, daha fazla anlamsal bilgi, fakat daha uzun kod. Evet, her bir yerel halkın rolünü anlamak daha kolaydır, ancak belgelerini okumadığınız sürece onunla neler yapabileceğinizi anlamak zordur.


Diğer programlama okullarında olduğu gibi, bunu da aşırılıklara götürmenin sağlıksız olduğunu düşünüyorum. Kendimi hiç x ve y koordinatlarını farklı bir tip olarak ayırırken görmüyorum. Bir yeterli Countolduğunda gerekli olduğunu sanmıyorum int. unsigned intC'deki kullanımdan hoşlanmıyorum - teorik olarak iyi olsa da, sadece size yeterli bilgi vermez ve bu büyülü -1'i desteklemek için kodunuzun daha sonra uzatılmasını yasaklar. Bazen sadelik gerekir.

Bence blog yazısı aşırı tarafta. Fakat genel olarak, acı verici deneyimlerden arkasındaki temel fikrin doğru şeylerden yapıldığını öğrendim.

Aşırı tasarlanmış kod için derin bir isteksizlik var. Ben gerçekten yapıyorum. Fakat doğru kullanılırsa, bu fazla mühendislik olduğunu sanmıyorum.


5

Her ne kadar fazla abartılsa da, çoğu zaman gördüğüm şeylerin yeterince geliştirilmediğini düşünüyorum.

Sadece "Güvenlik" değil, Java ile ilgili gerçekten güzel şeylerden biri, herhangi bir kütüphane yöntemi çağrısının ihtiyaç duyduğu / beklediği şeyleri hatırlama / çözme konusunda size çok yardımcı olmasıdır.

WORST (bugüne kadar) Çalıştığım Java kütüphanesi, Smalltalk'a çok düşkün olan bir kişi tarafından yazılmış ve GUI kütüphanesini daha sonra smalltalk gibi hareket etmesini sağlayacak şekilde modellemiştir - her yöntemin olması aynı temel nesneyi kullanmakta, ancak temel nesnenin kullanabileceği her şeyi KULLANABİLEMEZDİ, bu yüzden neyin işe yarayacağını tahmin etmeye başladınız ve çalışma zamanına kadar başarısız olup olmadığınızı bilmiyordunuz (Eskiden her zaman başa çıkmam gereken bir şey) C) çalışıyordu.

Başka bir konu - Dizeleri, girişleri, koleksiyonları ve dizileri nesneler olmadan geçirirseniz, sahip olduğunuz tek şey anlamsız veri toplarıdır. Bu, "bazı uygulamaların" kullanacağını kütüphaneler açısından düşündüğünüzde doğal görünür, ancak bir uygulamanın tamamını tasarlarken, verilerin tanımlandığı ve yalnızca düşündüğü yerdeki tüm verilerinize anlam (kod) atamak çok daha yararlı olur. Bu üst düzey nesnelerin nasıl etkileşime girdiği ile ilgili terimler Nesneler yerine ilkellerin etrafından geçiyorsanız, o zaman - tanım gereği - verileri tanımlandığı yerden farklı bir yerde değiştiriyorsunuzdur (Bu, aynı zamanda Setters ve Getters'ı gerçekten sevmiyorum - aynı konsept, sizin olmayan verilerde).

Son olarak, her şey için ayrı nesneler tanımlarsanız, her şeyi doğrulamak için her zaman harika bir yeriniz vardır - örneğin, posta kodu için bir nesne oluşturursanız ve daha sonra, posta kodunun her zaman sahip olduğunuz 4 basamaklı uzantıyı içermesini sağlamalısınız. koymak için mükemmel bir yer.

Bu aslında kötü bir fikir değil. Bunu düşününce aşırı yapılandırıldığını bile söyleyeceğime bile emin değilim, neredeyse her şekilde çalışmak daha kolay - bir istisna küçük sınıfların çoğalması olmasına rağmen, Java sınıfları çok hafif ve kolaydır bunun neredeyse zor bir maliyet olduğunu yazmak (üretilebilirler bile).

Gerçekten çok fazla sınıf tanımlanmış iyi yazılmış bir java projesi görmek isterdim (Bunu programlamayı zorlaştırdı), çok fazla ders almanın mümkün olmadığını düşünmeye başladım.


3

Bu konsepte başka bir başlangıç ​​noktasından bakmanız gerektiğini düşünüyorum. Bir veritabanı tasarımcısının bakış açısına bir göz atın: ilk örnekte geçen türler, parametrelerinizi benzersiz bir şekilde tanımlamaz, tek başına yararlı bir yoldur.

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

Bilet rezervasyonu yapan asıl müşteriyi belirtmek için iki parametre gerekir, aynı isimlerle iki farklı filminiz olabilir (örn. Remake), farklı isimlerle aynı filme sahip olabilirsiniz (örneğin çeviriler). Belirli bir sinema salonu zincirinin farklı şubeleri olabilir, bu yüzden bununla nasıl bir ipte ve tutarlı bir şekilde başa çıkacaksınız (örneğin kullanıyorsunuz $chain ($city)ya $chain in $cityda hatta başka bir şey kullanıyorsunuz ve bunun nasıl olduğundan emin olacaksınız? Aslında en kötüsü patronunuzu iki parametre ile belirtmektir, hem ad hem de soyadının belirtilmiş olması geçerli bir müşteriyi garanti etmez (ve iki John Doeayı ayırt edemezsiniz ).

Bunun cevabı türleri bildirmektir, ancak bunlar yukarıda gösterdiğim gibi nadiren ince ambalajlar olacaktır. Büyük olasılıkla, veri depolama alanınız olarak işlev görürler veya bir tür veritabanına bağlanırlar. Yani bir Cinemanesnenin büyük olasılıkla bir adı, yeri ve ... ve bu gibi belirsizliklerden kurtulacaksınız. Eğer ince sargılarsa, tesadüf eseridirler.

Bu yüzden, IMHO blog yazısı sadece "doğru tiplerden geçtiğinizden emin olun" diyor, yazarı özellikle temel veri tiplerinde (yanlış mesaj olan) seçim yapmak için aşırı kısıtlı bir seçim yaptı.

Önerilen alternatif daha iyidir:

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

Öte yandan, blog yazısının her şeyi sarmalamak için fazla ileri gittiğini düşünüyorum. Countçok genel, elma veya portakalları bununla sayabilir, ekleyebilir ve hala ellerimde tip sisteminin saçma sapan işlemler yapmama izin verdiği bir durum olabilir. Elbette blogdakiyle aynı mantığı uygulayabilir ve türleri tanımlayabilirsiniz CountOfOranges, vb.

Buna değer, aslında gibi bir şeyler yazmak

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

Uzun lafın kısası: saçma sapan değişkenleri geçmemelisiniz; gerçekte, gerçek nesneyi belirlemeyen bir değeri gerçekten belirlediğiniz zamanlar, bir sorgu çalıştırdığınızda (örn. public Collection<Film> findFilmsWithTitle(String title)) veya bir kavram kanıtı hazırladığınızda gerçekleşir. Böylece çok genel bir tür (a ile temsil örneğin bir film kullanmayın, temiz senin tipin sistemini tutun Stringyapmacık) ya da çok kısıtlayıcı / spesifik / (örn Countyerine int). Mümkün ve uygulanabilir olduğunda, nesnenizi benzersiz ve açık bir şekilde tanımlayan bir tür kullanın.

düzenleme : daha kısa bir özeti. Küçük uygulamalar için (örneğin kavramların ispatı): neden karmaşık bir tasarımla uğraşmıyorsunuz? Sadece kullanın Stringya da intdevam edin.

Büyük uygulamalar için: basit bir veri türüne sahip tek bir alandan oluşan çok sayıda sınıfınız olması muhtemel midir? Eğer böyle küçük sınıflarınız varsa, "normal" nesneleriniz var, orada özel bir şey yok.

Dizeleri kapsülleme fikrini hissediyorum ... ... tamamlanmamış bir tasarım: küçük uygulamalar için aşırı karmaşık, büyük uygulamalar için yeterince tamamlanmamış.


Amacınızı anlıyorum, ancak yazarın belirttiğinden daha fazlasını varsaydığınızı iddia ediyorum. Bildiğimiz kadarıyla, modelinde sadece bir kullanıcı için bir String, bir film için bir String ve benzeri var. Bu varsayımsal ekstra işlevsellik, sorunun asıl meselesidir, bu yüzden ya da dava açarken ya da tam anlamıyla daha fazla anlamsal güç vermemiz gerektiğini düşündüğü için "eksik düşünülmüş" oldu. Yine, herkesin bildiği gibi, aklındaki sonuncusu.
DPM

@Jubbat: Gerçekten de yazarın söylediklerinden daha fazlasını kabul ediyorum. Fakat benim açımdan, basit bir uygulamanız var, bu durumda her iki şekilde de aşırı karmaşık. Bu ölçek için sürdürülebilirlik bir sorun değildir ve anlamsal ayrım kodlama hızınızı engeller. Öte yandan, uygulamanız büyükse, türlerinizi doğru şekilde tanımlamanız faydalı olacaktır (ancak basit paketleyiciler olması pek mümkün değildir). IMHO, örnekleri ikna edici değil ya da yapmaya çalıştığı noktanın ötesinde büyük tasarım kusurlarına sahip.
Egon

2

Bana göre bunu yapmak C # bölgesindeki bölgeleri kullanmakla aynı. Genel olarak, kodunuzu okunabilir hale getirmek için bunun gerekli olduğunu düşünüyorsanız, zamanınızı harcamalısınız.


2
Sebep yerine semptomların tedavisini ima etmek için +1.
İş

2

Gerçekten iyi yazılmış bir fikir olduğunu ve çok iyi yazılmış bir typedef benzeri özelliğe sahip olduğunu söyleyebilirim.

Java'da buna sahip değilsiniz, bu nedenle bunlar için tamamen yeni bir sınıf oluşturmak, maliyetin muhtemelen faydadan ağır basacağı anlamına gelir. Ayrıca, değişken / parametre adlandırma konusunda dikkat ederek faydadan% 80 elde edebilirsiniz.


0

Bu iyi olurdu, IF String (tamsayı sonu ve ... sadece String hakkında konuşmak) son olamazdı, bu yüzden bu sınıflar bazı (sınırlı) bir dize anlamlı olabilirdi ve hala nasıl işleneceğini bilen bağımsız bir nesneye gönderilebilirdi. temel tür (ileri geri konuşmadan).

Ve örneğin "şerefsiz" ler artar. tüm isimlerdeki kısıtlamalar.

Ancak, bazı uygulamalar oluştururken (kütüphane değil), onu düzeltmek her zaman mümkün. Bu yüzden onsuz başlamayı tercih ederim.


0

Bir örnek vermek gerekirse: şu anda geliştirilen sistemimizde, bazen aynı varlık olsa bile, birden fazla tür tanımlayıcı ile (harici sistemlerin kullanımı nedeniyle) tanımlanabilecek birçok farklı varlık vardır. Tüm tanımlayıcılar Dizelerdir - eğer birileri parametre olarak hangi tür bir kimliğin geçmesi gerektiğini karıştırırsa, derleme zamanı hatası gösterilmez, ancak program çalışma zamanında patlayacaktır. Bu oldukça sık olur. Bu nedenle, bu ilkenin temel amacının değişime karşı (aynı zamanda hizmet etmesine rağmen) korunmak değil, kendimizi böceklerden korumak olduğunu söylemeliyim. Her neyse, eğer biri bir API tasarlarsa, etki alanı kavramlarını yansıtmak zorundadır, bu nedenle etki alanına özgü sınıfları tanımlamak kavramsal olarak yararlıdır - her şey geliştiricilerin aklında düzen olup olmamasına bağlıdır,


@ downvoter: Lütfen bir açıklama yapabilir misiniz?
thSoft
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.