Bir pasör dönüşü “bu” yapmak kötü bir uygulama mudur?


249

Java seters "bu" dönmek yapmak iyi ya da kötü bir fikir mi?

public Employee setName(String name){
   this.name = name;
   return this;
}

Bu desen yararlı olabilir çünkü daha sonra bu gibi ayarlayıcıları zincirleyebilirsiniz:

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

bunun yerine:

Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e);

... ama standart sözleşmeye aykırı. Sanırım bu ayarlayıcının başka bir şey yapmasını sağlayabilir. Bu kalıbın bazı yerlerde (ör. JMock, JPA) kullanıldığını gördüm, ancak nadir görünüyor ve genellikle bu kalıbın her yerde kullanıldığı çok iyi tanımlanmış API'ler için kullanılıyor.

Güncelleme:

Açıkladığım şey açıkça geçerli, ama gerçekten aradığım şey bunun genel olarak kabul edilebilir olup olmadığı ve herhangi bir tuzak veya ilgili en iyi uygulamalar olup olmadığı hakkında bazı düşünceler. Builder desenini biliyorum ama ne tarif ettiğimden biraz daha ilgili - Josh Bloch'un tanımladığı gibi, nesne oluşturma için ilişkili bir statik Builder sınıfı var.


1
Bu tasarım desenini bir süre önce gördüğümden beri, mümkün olan her yerde yapıyorum. Bir yöntemin işini yapmak için açıkça bir şey döndürmesi gerekmiyorsa, şimdi geri döner this. Bazen, bir değer döndürmek yerine, nesnenin bir üyesi üzerinde çalışacak şekilde işlevi değiştiririm, sadece bunu yapabilirim. Bu harika. :)
Inversus

4
Kendini döndüren teleskopik ayarlayıcılar ve inşaatçılar için setName (String name) yerine setName (String name) kullanmayı tercih ederim. İşaret ettiğiniz gibi, ortak bir uygulama ve belirleyici için beklenti, geri dönüşü bırakmaktır. "Standart olmayan" ayarlayıcılar, JPA varlık yöneticileri, Bahar vb. Gibi mevcut çerçevelerle iyi çalışmayabilir.
o

Lütfen her çağrıdan önce satır sonları verin :) Ve IDE'nizi yapılandırın veya buna uymuyorsa uygun bir tane alın.
MauganRa

Yaygın olarak kullanılan çerçeveler (örneğin Bahar ve Hazırda Bekletme), geçersiz ayarlayıcılar sözleşmesine kesinlikle (en azından
eskiden

Yanıtlar:


83

Özellikle yanlış bir şey olduğunu sanmıyorum, sadece bir stil meselesi. Aşağıdaki durumlarda yararlıdır:

  • Aynı anda birçok alan ayarlamanız gerekir (inşaat dahil)
  • kodu yazarken hangi alanları ayarlamanız gerektiğini biliyorsunuz ve
  • hangi alanları ayarlamak istediğinizin birçok farklı kombinasyonu vardır.

Bu yönteme alternatifler şunlar olabilir:

  1. Bir mega yapıcı (olumsuz: çok sayıda boş değer veya varsayılan değer iletebilirsiniz ve hangi değerin neye karşılık geldiğini bilmek zorlaşır)
  2. Birkaç aşırı yüklenici (olumsuz: birkaç taneden fazlasına sahip olduğunuzda kullanışsız olur)
  3. Fabrika / statik yöntemler (olumsuz: aşırı yüklü inşaatçılar ile aynı - birkaç taneden fazla olduğunda kötüleşir)

Bir seferde sadece birkaç özellik ayarlayacaksanız, 'bu' döndürmeye değmez diyebilirim. Daha sonra bir durum / başarı göstergesi / mesajı gibi başka bir şey döndürmeye karar verirseniz kesinlikle düşer.


1
Genel olarak, bir pasörden hiçbir şey konvansiyondan geri dönmezsiniz.
Ken Liu

17
Belki başlamamalı, ancak bir pasör orijinal amacını korumaz. Bir değişken olarak kullanılan, birkaç değişkeni kapsayan veya başka yan etkileri olan bir duruma dönüşebilir. Bazı ayarlayıcılar önceki bir değeri döndürebilir, diğerleri bir istisna için hata çok yaygınsa bir hata göstergesi döndürebilir. Yine de ilginç bir nokta daha ortaya çıkıyor: Kullandığınız bir araç / çerçeve, dönüş değerlerine sahip olan ayarlayıcılarınızı tanımıyorsa ne olur?
Tom Clift

12
@ İyi bir nokta, bunu yapmak alıcılar ve ayarlayıcılar için "Java fasulye" konvansiyonu kırıyor.
Andy White

2
@TomClift "Java Bean" kuralını ihlal etmek herhangi bir soruna neden oluyor mu? Dönüş türüne veya yalnızca yöntem parametrelerine ve yöntem adına bakarak "Java Bean" kuralını kullanan kütüphanelerdir.
Theo Briscoe

3
Bu yüzden Oluşturucu deseni var, ayarlayıcılar bir şey döndürmemeli, bunun yerine, daha iyi görünüyorsa ve daha az kod alırsa bir oluşturucu oluşturun :)
RicardoE

106

Kötü bir uygulama değil. Giderek yaygınlaşan bir uygulamadır. Çoğu dil, "normal" ayarlayıcı kullanım sözdizimini değiştirmez, ancak ayarlayıcıları birbirine zincirlemenize izin verirse, döndürülen nesneyle ilgilenmenizi gerektirmez.

Buna genellikle bir yapıcı modeli veya akıcı bir arayüz denir .

Java API'sında da yaygındır:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();

26
Genellikle inşaatçılarda kullanılır , ancak "buna ... İnşaatçı deseni denir" demezdim.
Laurence Gonsalves

10
Akıcı arayüzler için bazı gerekçelerin, kodun okunmasını kolaylaştırmaları benim için komik. Yazmanın daha uygun olduğunu görebiliyordum, ama okumam daha zor gibi görünüyor. Bununla ilgili tek gerçek anlaşmazlık bu.
Brent

30
Tren kazası antipatterni olarak da bilinir. Sorun, bir boş gösterici özel durum yığını izlemesi böyle bir satır içerdiğinde, hangi çağrının null döndürdüğüne dair hiçbir fikrinizin olmamasıdır. Bu, zincirlemenin her ne pahasına olursa olsun kaçınılması gerektiği anlamına gelmez, ancak kötü kütüphanelere (özellikle evde yetiştirilen) dikkat edin.
ddimitrov

18
@ddimitrov geri dönen bunu sınırlamak aslong bu bir sorun (sadece ilk invokation NPE atabilseydin) asla
Stefan

4
okunabilirlik ve okunabilirliğin başka türlü acı çekeceği yerlere çizgi ve girinti koyduğunuz varsayılırsa, yazmak ve okumak daha kolaydır ! (çünkü bla.foo.setBar1(...) ; bla.foo.setBar2(...)ne zaman yazabileceğiniz gibi kodun tekrarlanmasının gereksiz dağınıklığını önler (böyle bla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)bir SO yorumunda linebreaks kullanamazsınız :-( ... bu tür ayarlayıcıları veya daha karmaşık çağrıları dikkate alarak noktayı elde edersiniz)
Andreas Dietrich

90

Özetlemek:

  • buna "akıcı arayüz" veya "yöntem zinciri" denir.
  • Bu "standart" Java değildir, ancak bu günlerde daha fazla görüyorsunuz (jQuery'de harika çalışıyor)
  • JavaBean spesifikasyonlarını ihlal ettiğinden, özellikle JSP oluşturucuları ve Spring olmak üzere çeşitli araçlar ve kütüphanelerle kırılacaktır.
  • JVM'nin normalde yapacağı bazı optimizasyonları engelleyebilir
  • bazı insanlar kodları temizlediğini, bazıları ise "korkunç" olduğunu düşünüyor

Bahsetilmeyen birkaç nokta daha:

  • Bu, her işlevin bir (ve yalnızca bir) şey yapması gerektiği ilkesini ihlal eder. Buna inanabilir veya inanmayabilirsiniz, ancak Java'da iyi çalıştığına inanıyorum.

  • IDE'ler bunları sizin için oluşturmayacak (varsayılan olarak).

  • Sonunda, işte gerçek dünya veri noktası. Böyle bir kütüphane kullanmakta sorunlar yaşadım. Hazırda Beklet'in sorgu oluşturucusu, varolan bir kitaplıkta buna bir örnektir. Query'nin set * yöntemleri sorguları döndürdüğünden, yalnızca imzayı nasıl kullanacağına bakarak söylemek imkansızdır. Örneğin:

    Query setWhatever(String what);
  • Bir belirsizlik getirir: yöntem geçerli nesneyi (deseninizi) değiştirir mi, yoksa Query gerçekten değişmezdir (çok popüler ve değerli bir model) ve yöntem yenisini döndürür. Sadece kütüphanenin kullanımını zorlaştırır ve birçok programcı bu özellikten yararlanmaz. Seterler seters olsaydı, nasıl kullanılacağı daha açık olurdu.


5
btw, "akıcı", "akıcı" değil ... İçinde konuşulan dil gibi bir dizi yöntem çağrısı yapılandırmanıza izin verir.
Ken Liu

1
Değişmezlik ile ilgili son nokta çok önemlidir. Bunun en basit örneği Dize'dir. Java devs, String'de yöntemleri kullanırken aynı ancak değiştirilmiş örneği değil tamamen yeni bir örnek almasını bekler. Akıcı arayüzde, yöntem dokümantasyonunda, döndürülen nesnenin yeni örnek yerine 'bu' olduğu belirtilmelidir.
MeTTeO

7
Genel olarak kabul etsem de, bunun "tek bir şey yap" prensibini ihlal ettiğini kabul etmiyorum. Geri dönmek thispek karmaşık bir roket bilimidir. :-)
user949300

ek nokta: Komut-sorgu ayırma ilkesini de ihlal eder.
Marton_hun

84

Bunun için 'ile' yöntemleri kullanmayı tercih ediyorum:

public String getFoo() { return foo; }
public void setFoo(String foo) { this.foo = foo; }
public Employee withFoo(String foo) {
  setFoo(foo);
  return this;
}

Böylece:

list.add(new Employee().withName("Jack Sparrow")
                       .withId(1)
                       .withFoo("bacon!"));

Uyarı : Bu withXsözdizimi, değişmez nesneler için "ayarlayıcılar" sağlamak için yaygın olarak kullanılır; Belki daha makul bir ifade şöyle olabilir:

list.add(new Employee().chainsetName("Jack Sparrow")
                       .chainsetId(1)
                       .chainsetFoo("bacon!"));

ChainsetXyz () adlandırma kuralıyla neredeyse herkes mutlu olmalıdır.


18
+1 İlginç toplantılar için. Şimdi her sınıf alanı için a get, a setve a sahip gibi görünüyor çünkü kendi kodumu benimsemek değil with. Yine de hala ilginç bir çözüm. :)
Paul Manta

1
Ayarlayıcıları ne sıklıkta çağırdığınıza bağlıdır. Bu ayarlayıcılar çok çağrılırsa, başka bir yere kodu basitleştirdiği için bunları eklemek için ekstra zahmete değer olduğunu buldum. YMMV
qualidafial

2
Eğer bu ilave Ve eğer Project Lombok'ın @Getter/@Setterek açıklamalar ... bu zincirleme için harika olurdu. Veya JQuery ve Javascript tasarımcılarının kullandığı Kestrelbirleştirici ( github.com/raganwald/Katy ) gibi bir şey kullanabilirsiniz.
Ehtesh Choudhury

4
@ AlikElzin-kilaka Aslında Java 8'deki java.time değişmez sınıflarının bu kalıbı kullandığını fark ettim, örneğin LocalDate.withMonth, withYear, vb
qualidafial

4
withönek farklı bir sözleşmedir. @qualidafial bir örnek verdi. önekine sahip yöntemler withdöndürülmemeli this, geçerli örnek gibi yeni bir örnek değil, withbu değişiklik olmalıdır. Bu, nesnelerinizin değişmez olmasını istediğiniz zaman yapılır. Önden eklenmiş bir yöntem withgördüğümde, aynı nesneyi değil, yeni bir nesne alacağımı varsayıyorum.
tempcke

26

'this'Ayarlayıcıdan dönmek istemiyorsanız ancak ikinci seçeneği kullanmak istemiyorsanız, özellikleri ayarlamak için aşağıdaki sözdizimini kullanabilirsiniz:

list.add(new Employee()
{{
    setName("Jack Sparrow");
    setId(1);
    setFoo("bacon!");
}});

Bir kenara ben C # biraz temiz düşünüyorum:

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});

16
çift ​​ayracı başlatma, anonim bir iç sınıf oluşturduğundan eşitlerle ilgili sorunlar yaşayabilir; bkz. c2.com/cgi/wiki?DoubleBraceInitialization
Csaba_H

@Csaba_H Açıkçası bu sorun, equalsyöntemi bung eden kişinin hatasıdır . equalsNe yaptığınızı biliyorsanız , anonim sınıflarla başa çıkmanın çok temiz yolları vardır.
AJMansfield

bunun için yeni (anonim) bir sınıf mı oluşturuyorsunuz? her örnek için?
user85421

11

Yalnızca alıcıların / ayarlayıcıların kurallarını ihlal etmekle kalmaz, aynı zamanda Java 8 yöntemi referans çerçevesini de ihlal eder. MyClass::setMyValuebir BiConsumer<MyClass,MyValue>, ve myInstance::setMyValuebir Consumer<MyValue>. Setter dönüşünüz varsa this, artık geçerli bir örneği Consumer<MyValue>değil, a yerine geçer Function<MyValue,MyClass>ve bu ayarlayıcılara (geçersiz yöntemler oldukları varsayılarak) yöntem referanslarını kullanan herhangi bir şeyin bozulmasına neden olur.


2
Java'nın sadece JVM'yi değil, dönüş türüne göre aşırı yüklenmenin bir yolu olsaydı harika olurdu. Bu kırılma değişikliklerinin çoğunu kolayca atlayabilirsiniz.
Mart'ta Adowrath

1
Her zaman her ikisini de genişleten Consumer<A>ve Function<A,B>varsayılan bir uygulamasını sağlayarak işlevsel bir arabirim tanımlayabilirsiniz void accept(A a) { apply(a); }. Daha sonra kolayca biri olarak kullanılabilir ve belirli bir form gerektiren herhangi bir kodu kırmaz.
Steve K

Ahh! Bu sadece yanlış! Buna geçersiz uyumluluk denir. Bir değer döndüren ayarlayıcı, Tüketici gibi davranabilir. ideone.com/ZIDy2M
Michael

8

Java bilmiyorum ama bunu C ++ ile yaptım. Diğer insanlar satırları okumayı gerçekten uzun ve gerçekten zorlaştırdığını söylediler, ama bunu defalarca yaptım:

list.add(new Employee()
    .setName("Jack Sparrow")
    .setId(1)
    .setFoo("bacon!"));

Bu daha da iyi:

list.add(
    new Employee("Jack Sparrow")
    .Id(1)
    .foo("bacon!"));

en azından bence. Ama sen beni alçaltıp bana korkunç bir programcı diyebilirsin. Ve bunu Java'da bile yapmanıza izin verilip verilmediğini bilmiyorum.


"Daha da iyisi", modern IDE'lerde bulunan Format Source kod işlevselliğine iyi bir uyum sağlamaz. Ne yazık ki.
Thorbjørn Ravn Andersen

muhtemelen haklısın ... Kullandığım tek otomatik biçimlendirici emacs'ın otomatik girintisidir.
Carson Myers

2
Kaynak kod formatlayıcıları, zincirdeki her yöntem çağrısından sonra basit bir // ile zorlanabilir. Kodunuzu biraz çirkinleştirir, ancak dikey ifade dizinizin yatay olarak yeniden biçimlendirilmesi kadar değil.
qualidafial

@ qualidafial IDE'nizi //zaten sarılmış satırlara katılmayacak şekilde yapılandırırsanız, her yöntemden sonra ihtiyacınız yoktur (örn. Eclipse> Özellikler> Java> Kod Stili> Formatlayıcı> Satır Sarma> Asla sarılmış satırlara katılmayın).
DJDaveMark

6

'Akıcı arayüz' olarak adlandırılan bu şema (cinayet amaçlı) şimdi oldukça popüler hale geliyor. Kabul edilebilir, ama gerçekten benim fincan çayım değil.


6

Hiçlik döndürmediği için, artık geçerli bir JavaBean özellik ayarlayıcısı değil. Görsel "Bean Builder" araçlarını kullanan dünyanın yedi kişisinden veya JSP-bean-setProperty öğelerini kullanan 17 kişiden biri olmanız önemli olabilir.


Ayrıca Spring gibi fasüla duyarlı çerçeveler kullanırsanız da önemlidir.
ddimitrov

6

En azından teoride , çağrılar arasında yanlış bağımlılıklar ayarlayarak JVM'nin optimizasyon mekanizmalarına zarar verebilir.

Sözdizimsel şeker olması gerekiyordu, ancak aslında süper akıllı Java 43'ün sanal makinesinde yan etkiler yaratabilir.

Bu yüzden hayır oyu veriyorum, kullanma.


10
İlginç ... bunu biraz genişletebilir misiniz?
Ken Liu

3
Süperskalar işlemcilerin paralel yürütme ile nasıl başa çıktıklarını düşünün. İkinci setyöntemi uygulayacak nesne set, programcı tarafından bilinmesine rağmen , birinci yönteme bağlıdır .
Marian

2
Hala takip etmiyorum. Foo ve ardından Bar'ı iki ayrı ifadeyle ayarlarsanız, Çubuğu ayarladığınız nesnenin Foo ayarladığınız nesneden farklı bir durumu olur. Yani derleyici de bu ifadeleri paralelleştiremedi. En azından, haksız bir varsayım getirmeden nasıl olabileceğini görmüyorum. (Bu konuda hiçbir fikrim olmadığından, Java 43'ün aslında bir kerede paralelleştirme yaptığını, diğerini değil ve tanıttığını inkâr etmeyeceğim. bir durumda garantisiz varsayım, diğerinde değil).
masonk

12
Bilmiyorsanız, test edin. -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Java7 jdk kesinlikle zincirleme yöntemleri sıralar ve geçersiz ayarlayıcıları sıcak olarak işaretlemek ve bunları satır içi yapmak için gereken sayıda yinelemeyi yapar. Methinks, JVM'nin opcode budama algoritmalarının gücünü hafife almanızı sağlar; Eğer bunu iade ettiğinizi biliyorsa, jrs (java return deyimi) op ​​kodunu atlayacak ve bunu yığının üzerinde bırakacaktır.
Ajax

1
Andreas, katılıyorum ama verimsiz kod katmanı katmanı olduğunda sorunlar ortaya çıkıyor. Zamanın% 99'u, burada çok şey söylenen netlik için kod yazmalısınız. Ancak, gerçekçi olmanız ve yıllık deneyiminizi genel bir mimari anlamda erken optimize etmek için kullanmanız gereken zamanlar da vardır.
LegendLength

6

Hiç de kötü bir uygulama değil. Ancak JavaBeans Spec ile uyumlu değildir .

Ve bu standart erişimcilere bağlı çok fazla özellik var.

Onları her zaman birbirine birlikte yaşayabilirsiniz.

public class Some {
    public String getValue() { // JavaBeans
        return value;
    }
    public void setValue(final String value) { // JavaBeans
        this.value = value;
    }
    public String value() { // simple
        return getValue();
    }
    public Some value(final String value) { // fluent/chaining
        setValue(value);
        return this;
    }
    private String value;
}

Şimdi onları birlikte kullanabiliriz.

new Some().value("some").getValue();

İşte değişmez nesne için başka bir sürüm geliyor.

public class Some {

    public static class Builder {

        public Some build() { return new Some(value); }

        public Builder value(final String value) {
            this.value = value;
            return this;
        }

        private String value;
    }

    private Some(final String value) {
        super();
        this.value = value;
    }

    public String getValue() { return value; }

    public String value() { return getValue();}

    private final String value;
}

Şimdi bunu yapabiliriz.

new Some.Builder().value("value").build().getValue();

2
Düzenlemem reddedildi, ancak Oluşturucu örneğiniz doğru değil. İlk olarak, .value () hiçbir şey döndürmez ve somealanı bile ayarlamaz . İkinci olarak, bir koruma eklemeniz ve build () olarak ayarlamanız somegerekir null, bu yüzden Somegerçekten değişmezdir, aksi takdirde builder.value()aynı Builder örneğini tekrar çağırabilirsiniz . Ve son olarak, evet, bir kurucunuz var, ancak Somehala bir kamu kurucunuz var, yani Oluşturucunun kullanımını açıkça savunmuyorsunuz, yani kullanıcı, özel bir ayarlamak için bir yöntem denemek veya aramaktan başka bir şey bilmiyor valuehiç.
Mart'ta Adowrath

@Yanıt cevap yanlışsa, kendi cevabınızı yazmalı, başkasının şeklini düzenlemeye
çalışmamalısınız

1
@JinKwon Harika. Teşekkürler! Ve daha önce kaba göründüysem özür dilerim.
Adowrath

1
@Adowrath Lütfen geliştirmelerle ilgili ek yorum yapmaktan çekinmeyin. Bilginize, düzenlemenizi reddeden kişi ben değilim. :)
Jin Kwon

1
Biliyorum biliyorum. ^^ Ve şimdi yeni sürüm için teşekkürler, bu şimdi gerçek bir değişebilir "Immutable-Some" oluşturucu sağlar. Bu, düzenleme girişimlerinden daha karmaşık bir kod benimseyen daha akıllı bir çözüm.
Ağustos'ta Adowrath

4

Tüm uygulamada aynı sözleşmeyi kullanırsanız iyi görünüyor.

Uygulamanızın mevcut kısmı standart kural kullanıyorsa, buna bağlı kalacağım ve daha karmaşık sınıflara inşaatçılar ekleyeceğim

public class NutritionalFacts {
    private final int sodium;
    private final int fat;
    private final int carbo;

    public int getSodium(){
        return sodium;
    }

    public int getfat(){
        return fat;
    }

    public int getCarbo(){
        return carbo;
    }

    public static class Builder {
        private int sodium;
        private int fat;
        private int carbo;

        public Builder sodium(int s) {
            this.sodium = s;
            return this;
        }

        public Builder fat(int f) {
            this.fat = f;
            return this;
        }

        public Builder carbo(int c) {
            this.carbo = c;
            return this;
        }

        public NutritionalFacts build() {
            return new NutritionalFacts(this);
        }
    }

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}

1
Builder deseninin düzeltmek için tasarlandığı tam olarak budur. Java 8'de lambdasları kırmaz, titiz JavaBeans araçlarını kırmaz ve JVM'de herhangi bir optimizasyon sorununa neden olmaz (nesne yalnızca örnekleme sırasında var olduğundan). Ayrıca, çift oluşturucu anonim sınıflardan yığın kirliliğini ortadan kaldırmanın yanı sıra, sadece inşaatçılar kullanmadan elde ettiğiniz "yol çok fazla kurucu" sorununu da çözer.
ndm13

İlginç nokta - Sınıfınızdaki neredeyse hiçbir şey değişmezse (örneğin, son derece yapılandırılabilir bir GUI), o zaman muhtemelen Builder'dan tamamen kaçınabileceğinizi fark ediyorum.
Philip Guin

4

Paulo Abrantes , JavaBean ayarlayıcılarını akıcı hale getirmenin başka bir yolunu sunar: her JavaBean için bir iç oluşturucu sınıfı tanımlayın. Değerleri döndüren ayarlayıcılar tarafından yanıklanan araçlar kullanıyorsanız, Paulo'nun deseni yardımcı olabilir.


2
@ cdunn2001, wayback makinesi kullanın, Luke
msangel

3

Ben "bu" döner seters lehine. Fasulye uyumlu olup olmadığı umurumda değil. Bana göre, "=" ifadesine / ifadesine sahip olmak uygunsa, değer döndüren ayarlayıcılar iyidir.


2

Bu yaklaşımı tercih ederdim ama buna karşı karar verdim.

Nedenleri:

  • Okunabilirlik. Her setFoo () işlevinin ayrı bir satıra sahip olması kodu daha okunaklı hale getirir. Kodu genellikle yazdığınız tek seferden çok, çok daha fazla okursunuz.
  • Yan etki: setFoo () sadece alan foo ayarlamalıdır, başka bir şey değil. Bu iade ekstra bir "NE vardı".

Gördüğüm Oluşturucu deseni setFoo (foo) .setBar (bar) kuralını değil, daha foo (foo) .bar (bar) kullanmayın. Belki de tam da bu nedenlerden dolayı.

Her zaman olduğu gibi bir lezzet meselesidir. Sadece "en az sürpriz" yaklaşımını seviyorum.


2
Yan etkiye katılıyorum. Bir şeyler döndüren ayarlayıcılar isimlerini ihlal ediyor. Foo koyuyorsunuz, ama bir nesneyi geri mi alıyorsunuz? Bu yeni bir nesne mi yoksa eskiyi mi değiştirdim?
crunchdog

2

Bu özel desene Yöntem Zincirleme denir. Wikipedia bağlantısı , bunun çeşitli programlama dillerinde nasıl yapıldığına dair daha fazla açıklama ve örnek var.

Not: Özel adı aradığım için burada bırakmayı düşündüm.


1

İlk bakışta: "Korkunç!"

Daha fazla düşünce üzerine

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

aslında hatadan daha az hata yapar

Employee anEmployee = new Employee();
anEmployee.setName("xxx");
...
list.add(anEmployee);

Çok ilginç. Alet çantasına fikir ekleniyor ...


1
Hayır, hala korkunç. Bakım açısından bakıldığında, ikincisi daha iyidir çünkü okunması daha kolaydır. Ayrıca, CheckStyle gibi otomatik kod denetleyicileri satırları varsayılan olarak 80 karakter olarak zorlar - kod yine de sarılır ve okunabilirlik / bakım sorununu birleştirir. Ve son olarak - Java; yine de bayt koduna derlenecek her şeyi tek bir satıra yazmanın bir yararı yoktur.
OMG Ponies

1
kişisel olarak, özellikle de bu şekilde birkaç nesne oluşturuyorsanız, eskisinin okunması daha kolay olduğunu düşünüyorum.
Ken Liu

@Ken: Bir yöntemi kodlayın. Bir kopyayı akıcı biçimde yazın; diğerinde başka bir kopya. Şimdi iki kopyayı birkaç kişiye verin ve hangisini daha kolay okuduklarını sorun. Daha hızlı okunabilir, daha hızlı kodlanabilir.
OMG Ponies

Çoğu araç gibi, aşırı kullanımı kolay olabilir. JQuery bu tekniğe odaklanmıştır ve bu nedenle bulduğum uzun çağrı zincirlerine eğilimlidir.
staticsan

2
Her noktadan önce çizgi girintileri varsa ve girintili olsa sorun olmazdı. Sonra ikinci versiyon kadar okunabilir olacaktı. Aslında daha fazlası, çünkü listbaşlangıçta fazlalık yoktur .
MauganRa

1

Evet, bence iyi bir fikir.

Bir şey ekleyebilseydim, bu sorun ne olacak?

class People
{
    private String name;
    public People setName(String name)
    {
        this.name = name;
        return this;
    }
}

class Friend extends People
{
    private String nickName;
    public Friend setNickName(String nickName)
    {
        this.nickName = nickName;
        return this;
    }
}

Bu çalışacak :

new Friend().setNickName("Bart").setName("Barthelemy");

Eclipse tarafından kabul edilmeyecek! :

new Friend().setName("Barthelemy").setNickName("Bart");

Bunun nedeni setName () öğesinin Arkadaş değil Kişi döndürmesi ve PeoplesetNickName olmamasıdır.

Sınıfın adı yerine SELF sınıfına dönmek için ayarlayıcıları nasıl yazabiliriz?

Böyle bir şey iyi olurdu (SELF anahtar sözcüğü varsa). Bu zaten var mı?

class People
{
    private String name;
    public SELF setName(String name)
    {
        this.name = name;
        return this;
    }
}

1
Eclipse dışında bunu kabul etmeyen birkaç Java derleyicisi daha var :). Temel olarak, Java tür sistemindeki kümes hayvanı çalıştırıyorsunuz (statik, bazı komut dosyası dilleri gibi dinamik değil): setName () öğesinden gelenleri setNickName () öğesini ayarlayabilmeniz için bir Arkadaşa yayınlamanız gerekir. Dolayısıyla, maalesef, kalıtım hiyerarşileri için bu, okunabilirlik avantajının çoğunu ortadan kaldırır ve potansiyel olarak faydalı olan bu tekniği çok kullanışlı değildir.
Cornel Masson

5
Jenerikler kullanın. class Chainable <Kendini genişletir Chainable> {public Self doSomething () {return (Self) this;}} Teknik olarak güvenli bir tür değildir (sınıfı, kullanamayacağınız bir Self türüyle uygularsanız sınıf dökümünü yapar), ancak doğru gramerdir ve alt sınıflar kendi türlerini döndürür.
Ajax

Buna ek olarak: Baptiste'nin kullandığı bu "KENDİ KENDİNE", birçok dilde mevcut olmayan kendinden tür olarak adlandırılır (Scala gerçekten düşünebildiğim tek kişi), burada Ajax'ın Generics kullanımı "Merakla özyinelemenin eksikliğiyle başa çıkmaya çalışan yinelenen şablon deseni ", ancak bazı dezavantajları vardır: ( class C<S extends C<S>>bir düzlükten daha güvenli olan S extends C. a) A extends Bve B extends C<B>ve A'ya sahipseniz, yalnızca A, her yöntemi geçersiz kılmadığı sürece B döndürülür. b) Yerel, ham olmayan bir ürünü gösteremezsiniz C<C<C<C<C<...>>>>>.
Adowrath

1

Genel olarak bu iyi bir uygulamadır, ancak set tipi işlevlerin, işlemin başarıyla tamamlanıp tamamlanmadığını belirlemek için Boolean tipini kullanmanız gerekebilir, bu da bir yöntemdir. Genel olarak, bunun iyi veya yatak olduğunu söyleyen hiçbir dogma yoktur, elbette durumdan gelir.


2
Hata durumunu belirtmek için istisnalar kullanmaya ne dersiniz? Birçok C programcısı acı verici şekilde öğrendiği için hata kodları kolayca göz ardı edilebilir. İstisnalar, yığını işlenebilecekleri noktaya kadar kabartabilir.
ddimitrov

İstisnalar genellikle tercih edilir, ancak istisnaları kullanamadığınızda hata kodları da yararlıdır.
Narek

1
Merhaba @Narek, belki hangi durumlarda istisnalar yerine ayarlayıcılarda hata kodlarının kullanılması tercih edilebilir ve neden?
ddimitrov

0

Açıklamadan

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

iki şey görüyorum

1) Anlamsız ifade. 2) Okunabilirlik eksikliği.


0

Bu daha az okunabilir olabilir

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!")); 

veya bu

list.add(new Employee()
          .setName("Jack Sparrow")
          .setId(1)
          .setFoo("bacon!")); 

Bu çok daha okunabilir:

Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!")); 
list.add(employee); 

8
Tüm kodunuzu bir satıra koymaya çalışmazsanız oldukça okunabilir olduğunu düşünüyorum.
Ken Liu

0

Seterlerimi bir süredir yapıyorum ve tek gerçek sorun, fasulye okuyucu / yazar fasulye erişimcilerini almak için katı getPropertyDescriptors ile yapışan kütüphaneler. Bu durumlarda, java "fasulye" beklediğiniz yazarları olmayacak.

Örneğin, kesin olarak test etmedim, ancak Jackson'ın json / maps'den java nesneleri oluştururken bunları ayarlayıcı olarak tanımayacağına şaşırmam. Umarım bu konuda yanılıyorum (yakında test edeceğim).

Aslında, hafif bir SQL merkezli ORM geliştiriyorum ve bunu döndüren tanınmış ayarlayıcılara getPropertyDescriptors beyong bazı kod eklemek zorunda.


0

Uzun zaman önce cevap, ama iki sent ... Güzel. Keşke bu akıcı arayüz daha sık kullanılsaydı.

'Fabrika' değişkenini tekrarlamak, daha fazla bilgi eklemez:

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Foo.class);
factory.setFilter(new MethodFilter() { ...

Bu daha temiz, imho:

ProxyFactory factory = new ProxyFactory()
.setSuperclass(Properties.class);
.setFilter(new MethodFilter() { ...

Tabii ki, daha önce bahsedilen cevaplardan biri olarak, Java API'sı, kalıtım ve araçlar gibi bazı durumlar için bunu doğru bir şekilde yapmak için ayarlanmalıdır.


0

Varsa diğer dil yapılarını kullanmak daha iyidir. Örneğin, Kotlin'de, birlikte kullanır , uygular veya izin verirsiniz . Bu yaklaşımı kullanıyorsanız , ayarlayıcınızdan bir örnek döndürmeniz gerekmez .

Bu yaklaşım, istemci kodunuzun:

  • Dönüş türüne kayıtsız
  • Bakımı daha kolay
  • Derleyici yan etkilerinden kaçının

İşte bazı örnekler.

val employee = Employee().apply {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
with(employee) {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
employee.let {
   it.name = "Jack Sparrow"
   it.id = 1
   it.foo = "bacon"
}

0

Bir API yazıyorum, yalnızca bir kez ayarlanacak değerleri ayarlamak için "return this" kullanıyorum. Kullanıcının değiştirebilmesi gereken başka değerlerim varsa, bunun yerine standart bir void ayarlayıcı kullanırım.

Ancak, bu bir tercih meselesi ve zincirleme setçileri bence oldukça havalı görünüyor.


0

Bu JavaBeans spec sonları iddia tüm posterleri kabul ediyorum. Bunu korumak için nedenler var, ama aynı zamanda bu Builder Pattern'in (buna ayrılmış) kullanımının da yeri olduğunu hissediyorum; her yerde kullanılmadığı sürece kabul edilebilir olmalıdır. Bana göre "It's Place", bitiş noktasının bir "build ()" yöntemine çağrı olduğu yerdir.

Elbette tüm bu şeyleri ayarlamanın başka yolları da vardır, ancak buradaki avantaj, 1) çok parametreli kamu kurucularından ve 2) kısmen belirtilen nesnelerden kaçınmasıdır. Burada, yapıcıya ihtiyaç duyulan şeyleri toplayın ve sonunda "build ()" olarak adlandırın. Alternatif "parametre nesneleri" olacaktır, ancak IMHO sorunu sadece bir seviyeye iter.

Birçok parametre yapıcılarını sevmiyorum, çünkü yanlış argümanların parametrelere iletilmesini kolaylaştırabilecek aynı tür argümanlarının çoğunun geçirilmesini daha olası hale getiriyorlar. Nesne tamamen yapılandırılmadan önce kullanılabileceğinden, ayarlayıcıların çoğunu kullanmaktan hoşlanmıyorum. Ayrıca, önceki seçimlere dayalı varsayılan değerlere sahip olma fikri bir "build ()" yöntemi ile daha iyi sunulur.

Kısacası, doğru kullanılırsa iyi bir uygulama olduğunu düşünüyorum.


-4

Kötü alışkanlık: bir pasör bir alıcı olsun

bir yöntemi açıkça bildirmeye ne dersiniz, bunu U için yapar

setPropertyFromParams(array $hashParamList) { ... }

otomatik tamamlama ve yeniden düzenleme ve okunabilirlik için iyi değil anb kazan plakası kodu
Andreas Dietrich

Çünkü: 1) Siparişi hatırlamanız veya dokümanlardan okumalısınız, 2) derleme zamanı türünün tüm güvenliğini kaybedersiniz, 3) değerleri (bu ve 2) kullanmak elbette bir dinamikte sorun değil tabii dili), 4) yapabilirsiniz değildir ) her çağrı üzerine dizi uzunlukta kontrol daha bu faydalı bir şekilde başka genişletmek ve 5 eğer Herhangi bir değişiklik bu sipariş veya belirli değerlerin varlığı bile olabilir şunları yapabilirsiniz değil , ne çalışma zamanı olmadığını kontrol ne de timewithout şüphe derlemek hiç . Ayarlayıcılarla: 1), 2), 3): Sorun değil. 4) Yeni yöntemler eklemek hiçbir şeyi bozmaz. 5) açık hata mesajı.
Mart'ta Adowrath
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.