Yalnızca bir kez kullanılacaksa bir dize sabiti tanımlanmalı mı?


24

Uygulamamızın veri modeline erişmek için XPath'ı kullanmamızı sağlayan Jaxen (Java için bir XPath kütüphanesi) için bir adaptör uyguluyoruz.

Bu, (Jaxen'den bize geçen) dizgileri veri modelimizin öğelerine eşleyen sınıflar uygulayarak yapılır. Toplamda 1000'den fazla dizi karşılaştırmasıyla 100'e yakın sınıfa ihtiyacımız olacağını tahmin ediyoruz.

Bence bunu yapmanın en iyi yolunun, / stringleri doğrudan koda yazılan ifadelerle ifade etmek - her dizeyi sabit olarak tanımlamak yerine, basit olduğunu düşünüyorum. Örneğin:

public Object getNode(String name) {
    if ("name".equals(name)) {
        return contact.getFullName();
    } else if ("title".equals(name)) {
        return contact.getTitle();
    } else if ("first_name".equals(name)) {
        return contact.getFirstName();
    } else if ("last_name".equals(name)) {
        return contact.getLastName();
    ...

Ancak her zaman dize değerlerini doğrudan koda gömmememiz, bunun yerine dize sabitleri oluşturmamız gerektiği öğretildi. Bunun gibi bir şey olurdu:

private static final String NAME = "name";
private static final String TITLE = "title";
private static final String FIRST_NAME = "first_name";
private static final String LAST_NAME = "last_name";

public Object getNode(String name) {
    if (NAME.equals(name)) {
        return contact.getFullName();
    } else if (TITLE.equals(name)) {
        return contact.getTitle();
    } else if (FIRST_NAME.equals(name)) {
        return contact.getFirstName();
    } else if (LAST_NAME.equals(name)) {
        return contact.getLastName();
    ...

Bu durumda bence bu kötü bir fikir. Sabit, getNode()yöntemde yalnızca bir kez kullanılacaktır . Dizeleri doğrudan kullanmak, sabitleri kullanmak kadar okunması ve anlaşılması kolaydır ve bize en az bin satırlık bir kod yazmamızı sağlar.

Tek bir kullanım için dize sabitlerini tanımlamanın bir nedeni var mı? Veya doğrudan dizeleri kullanmak kabul edilebilir mi?


PS. Herkes bunun yerine enums kullanmasını önermeden önce prototip yaptık ancak enum dönüşümü, basit dize karşılaştırmasından 15 kat daha yavaş olduğu için dikkate alınmadı.


Sonuç: Aşağıdaki cevaplar bu sorunun kapsamını sadece string sabitlerinin ötesine genişletti, bu yüzden iki sonucum var:

  • Bu senaryoda dizeleri doğrudan kullanmak yerine dizeleri kullanmak muhtemelen uygundur, ancak
  • Dizeleri kullanmaktan kaçınmanın yolları vardır, ki bunlar daha iyi olabilir.

Bu yüzden dizeleri tamamen önleyen sarma tekniğini deneyeceğim. Ne yazık ki henüz string 7'yi kullanamadığımız için string switch deyimini kullanamıyoruz. Sonuçta, sanırım, bizim için en iyi cevap, her tekniği denemek ve performansını değerlendirmektir. Gerçek şu ki, eğer bir teknik açıkça daha hızlıysa, güzelliği veya konvansiyona bağlılığından bağımsız olarak muhtemelen seçeceğiz.


3
İfadeler yaparsanız 1000'i elle klavyeyle planlamıyor musunuz?
JeffO

1
Bu kadar basit bir şeyin bazı dillerde ne kadar nahoş olabileceğini çok üzücü buluyorum…
Jon Purdy

5
Java 7, Dizelerin switchetiket olarak kullanılmasını sağlar . ifBasamaklar yerine bir anahtar kullanın .
Monica'yı yeniden kurun - M. Schröder

3
Bir dizeyi enum değerine dönüştürürseniz enum dönüşüm işlemi 15 kat daha yavaştır! Doğrudan enum iletin ve aynı tipteki başka bir enum değeri ile karşılaştırın!
Neil

2
HashMap gibi kokmak bir çözüm olabilir.
MarioDS

Yanıtlar:


5

Bunu dene. İlk yansıma kesinlikle pahalıdır, ancak bunu birçok kez kullanacaksanız, ki bence bunu yapacaksınız, bu kesinlikle önerdiğiniz şeyi daha iyi bir çözümdür. Yansıma kullanmayı sevmiyorum, ancak yansıma alternatifini sevmediğimde kendimi kullanırken buluyorum. Bunun takımınıza çok fazla baş ağrısı kazandıracağını düşünüyorum, ancak yöntemin adını (küçük harflerle) geçmelisiniz.

Başka bir deyişle, "isim" yerine, "tam isim" değerini geçersiniz, çünkü get yönteminin adı "getFullName ()" olur.

Map<String, Method> methodMapping = null;

public Object getNode(String name) {
    Map<String, Method> methods = getMethodMapping(contact.getClass());
    return methods.get(name).invoke(contact);
}

public Map<String, Method> getMethodMapping(Class<?> contact) {
    if(methodMapping == null) {
        Map<String, Method> mapping = new HashMap<String, Method>();
        Method[] methods = contact.getDeclaredMethods();
        for(Method method : methods) {
            if(method.getParameterTypes().length() == 0) {
                if(method.getName().startsWith("get")) {
                    mapping.put(method.getName().substring(3).toLower(), method);
                } else if (method.getName().startsWith("is"))) {
                    mapping.put(method.getName().substring(2).toLower(), method);
                }
            }
        }
        methodMapping = mapping;
    }
    return methodMapping;
}

İrtibat üyeleri içinde yer alan verilere erişmeniz gerekiyorsa, gerekli tüm bilgilere erişmek için tüm yöntemlere sahip olan bir iletişim sınıfı için bir sarıcı sınıf oluşturmayı düşünebilirsiniz. Bu, erişim alanlarının adlarının her zaman aynı kalacağını garanti etmek için de faydalı olacaktır (sarmalayıcı sınıf getFullName () ise ve tam adla çağırıyorsanız, kişi getFullName () yeniden adlandırılmış olsa bile her zaman çalışacaktır - bu Bunu yapmanıza izin vermeden önce derleme hatasına neden olur).

public class ContactWrapper {
    private Contact contact;

    public ContactWrapper(Contact contact) {
        this.contact = contact;
    }

    public String getFullName() {
        return contact.getFullName();
    }
    ...
}

Bu çözüm beni birkaç kez kurtardı, yani jsf veri tablolarında kullanmak üzere tek bir veri temsiline sahip olmak istediğimde ve bu verilerin jasper kullanılarak bir rapora aktarılması gerektiğinde (ki bu benim deneyimimde karmaşık nesne erişimcilerini iyi işlemiyor) .


Bir sarmalayıcı nesnesi fikrini, üzerinden çağrılan yöntemlerle seviyorum .invoke(), çünkü tamamen sabit dizelerle devam ediyor. Haritayı ayarlamak için çalışma zamanının yansımasını çok istemiyorum, ancak getMethodMapping()bir staticblokta çalıştırmak Tamam olacaktır, böylece sistem bir kez yerine başlangıçta olur.
gutch

@gutch, sarmalayıcı deseni, sık kullandığım bir modeldir, çünkü arabirim / denetleyiciyle ilgili birçok sorunu çözme eğilimindedir. Arayüz her zaman sargısını kullanabilir ve bundan mutlu olabilir ve kontrolör bu arada ters çevrilebilir. Bilmeniz gereken tek şey, arayüzde hangi verileri kullanmak istediğiniz. Ve yine, vurgu için şunu söylüyorum, normalde yansımadan hoşlanmıyorum, ancak bir web uygulamasıysa, başlangıçta müşteri bunu beklediğiniz zaman görmeyeceği için yaparsanız tamamen kabul edilebilir.
Neil

@Neil Neden BeanUtils'i Apache Commons'dan kullanmıyorsunuz? Ayrıca gömülü nesneleri de destekler. Obj.attrA.attrB.attrN'ın tüm veri yapısını gözden geçirebilirsiniz ve daha birçok seçeneğe sahip olabilir :-)
Laiv

Haritalar ile eşlemeler yerine @ Ek Açıklamalar için giderdim. JPA gibi bir şey yapar. Denetleyici girişlerini (dize) belirli bir attr veya alıcı ile eşlemek için kendi Ek Notumu tanımlamak için. Annotation ile çalışmak oldukça kolaydır ve Java 1.6'dan edinilebilir (Sanırım)
Laiv,

5

Mümkünse, Dizeleri switchifadelerde kullanmanıza izin veren Java 7'yi kullanın .

Gönderen http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html

public class StringSwitchDemo {

    public static int getMonthNumber(String month) {

        int monthNumber = 0;

        if (month == null) {
            return monthNumber;
        }

        switch (month.toLowerCase()) {
            case "january":
                monthNumber = 1;
                break;
            case "february":
                monthNumber = 2;
                break;
            case "march":
                monthNumber = 3;
                break;
            case "april":
                monthNumber = 4;
                break;
            case "may":
                monthNumber = 5;
                break;
            case "june":
                monthNumber = 6;
                break;
            case "july":
                monthNumber = 7;
                break;
            case "august":
                monthNumber = 8;
                break;
            case "september":
                monthNumber = 9;
                break;
            case "october":
                monthNumber = 10;
                break;
            case "november":
                monthNumber = 11;
                break;
            case "december":
                monthNumber = 12;
                break;
            default: 
                monthNumber = 0;
                break;
        }

        return monthNumber;
    }

    public static void main(String[] args) {

        String month = "August";

        int returnedMonthNumber =
            StringSwitchDemo.getMonthNumber(month);

        if (returnedMonthNumber == 0) {
            System.out.println("Invalid month");
        } else {
            System.out.println(returnedMonthNumber);
        }
    }
}

Ölçmedim, ancak switch ifadelerinin uzun bir karşılaştırma listesi yerine bir atlama tablosuna derlendiğine inanıyorum. Bu daha hızlı olmalı.

Asıl sorunuzla ilgili: Bunu yalnızca bir kez kullanırsanız , bir sabit haline getirmenize gerek yoktur. Bununla birlikte, bir sabitin Javadoc'ta belgelenebileceğini ve gösterilebileceğini düşünün . Bu önemsiz olmayan dize değerleri için önemli olabilir.


2
Atlama masası ile ilgili. Dize anahtarı, anahtarlarla değiştirilir, ilk önce hashcode (aynı hashcode ile tüm sabitler için eşitlik kontrol edilir) temel alır ve branş indeksini, ikinci branş indeks anahtarını seçer ve orijinal branş kodunu seçer. Sonuncusu bir dal tablosu için açıkça uygundur, birincisi karma fonksiyonunun dağılımından kaynaklanmamaktadır. Bu nedenle, herhangi bir performans avantajı muhtemelen karma tabanlı gerçekleştirmeden kaynaklanmaktadır.
Fularlı

Çok iyi bir nokta; iyi performans gösterirse, sadece bunun için Java 7'ye
geçmeye

4

Bunu devam ettirecekseniz (şimdiye kadar herhangi bir önemsiz değişiklik yapın), aslında herhangi bir ek açıklama odaklı kod oluşturma (belki de CGLib aracılığıyla ) veya tüm kodu sizin için yazan bir komut dosyası kullanmayı düşünebilirim . Aklınıza gelebilecek yaklaşım ile sürünebilecek yazım hatası ve hataların sayısını düşünün ...


Mevcut yöntemleri açıklamayı düşündük, ancak bazı eşlemeler object.getAddress().getCountry()ek açıklamalarla gösterilmesi zor olan birden çok nesneyi (örneğin, "ülke" eşlemesi ) çaprazlar . İf / else string karşılaştırmaları hoş değildir, ancak hızlı, esnek, anlaşılması kolay ve birim testi kolaydır.
gutch

1
Yazım hataları ve hatalar için potansiyel konusunda haklısınız; Tek savunmam orada birim testleri var. Tabii ki bu daha fazla kod demektir ...
gutch

2

Hala sınıflarının tepesinde tanımlanan sabitleri kullanırdım. Kodunuzu daha bakımlı hale getirir, çünkü daha sonra değiştirilebilecek olanı görmek daha kolaydır (gerekirse). Örneğin, bir süre sonra "first_name"olabilir "firstName".


Bununla birlikte, eğer bu kod otomatik olarak oluşturulacak ve sabitler başka yerde kullanılmayacaksa, bunun önemi yoktur (OP bunu 100 derste yapmaları gerektiğini söylüyor).
NoChance

5
Sadece "bakım yapılabilirlik" açısını görmüyorum, her iki durumda da bir kerede "first_name" 'i "givenName" olarak değiştirirsiniz. Bir dize "givenName" yani muhtemelen bunu değiştirmek istersiniz, bu yüzden şimdi iki yerde üç değişikliğiniz var
James Anderson

1
Doğru IDE ile bu değişiklikler önemsizdir. Savunduğum şey, bu değişikliklerin nerede yapılacağının daha belirgin olduğudur, çünkü sınıfın tepesinde sabitler bildirmek için zaman ayırdınız ve bunu yapmak için sınıftaki kodun geri kalanını okumak zorunda değilsiniz. bu değişiklikleri yap.
Bernard

Ancak if ifadesini okurken, geri dönüp sabitin içerdiğini düşündüğünüz dizgiyi içerdiğini kontrol etmeniz gerekir - burada kaydedilmiş hiçbir şey yok.
James Anderson,

1
Belki, ama bu yüzden sabitlerimi iyi adlandırıyorum.
Bernard

1

Adınız tutarlıysa (aka "some_whatever"her zaman eşlenir getSomeWhatever()) get yöntemini belirlemek ve yürütmek için yansıma kullanabilirsiniz.


Daha iyi getSome_whatever (). Deve kasasını kırıyor olabilirsiniz, ancak yansımanın çalıştığından emin olmak çok daha önemlidir. Artı, "Neden halt böyle yaptım .. oh bekle .. George! Bu yöntemin adını değiştirmeyin!"
Neil

0

Bence ek açıklama işleme, ek açıklamalar olmadan bile çözüm olabilir. Tüm sıkıcı kodları sizin için üretebilecek olan şey budur. Dezavantajı, N modeli sınıfları için N tarafından oluşturulan sınıfları elde etmenizdir. Ayrıca, mevcut bir sınıfa hiçbir şey ekleyemezsiniz, ancak

public Object getNode(String name) {
    return SomeModelClassHelper.getNode(this, name);
}

ders başına bir kez sorun olmamalıdır. Alternatif olarak, gibi bir şey yazabilirsiniz

public Object getNode(String name) {
    return getHelper(getClass()).getNode(this, name);
}

ortak bir üst sınıfta.


Kod oluşturma için açıklama işleme yerine yansıma kullanabilirsiniz. Dezavantajı ise, yansıma kullanmadan önce derlemek için kodunuzun olması. Bu, bazı saplamalar oluşturmadığınız sürece, model sınıflarınızda üretilen koda güvenemeyeceğiniz anlamına gelir.


Ayrıca doğrudan yansıma kullanımını da düşünürdüm. Tabii, yansıma yavaş, ama neden yavaş? Yapmanız gereken her şeyi yapması gerektiği içindir, örneğin alan adını açın.

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.