Apakah praktik yang buruk untuk mengembalikan setter ke "ini"?

249

Apakah ini ide yang baik atau buruk untuk membuat setter di java mengembalikan "ini"?

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

Pola ini dapat bermanfaat karena Anda dapat mengatur setter seperti ini:

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

alih-alih ini:

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

... tapi itu agak bertentangan dengan konvensi standar. Saya kira itu mungkin bermanfaat hanya karena dapat membuat setter itu melakukan sesuatu yang bermanfaat. Saya telah melihat pola ini menggunakan beberapa tempat (misalnya JMock, JPA), tetapi tampaknya tidak umum, dan hanya umumnya digunakan untuk API yang didefinisikan dengan sangat baik di mana pola ini digunakan di mana-mana.

Memperbarui:

Apa yang saya jelaskan jelas valid, tetapi yang benar-benar saya cari adalah beberapa pemikiran tentang apakah ini secara umum dapat diterima, dan jika ada jebakan atau praktik terbaik terkait. Saya tahu tentang pola Builder tetapi sedikit lebih terlibat daripada apa yang saya jelaskan - seperti yang dijelaskan oleh Josh Bloch, ada kelas Builder statis terkait untuk pembuatan objek.

Ken Liu
sumber
1
Karena saya sudah melihat pola desain ini beberapa waktu lalu, saya melakukan ini sedapat mungkin. Jika suatu metode tidak secara eksplisit perlu mengembalikan sesuatu untuk melakukan tugasnya, itu sekarang kembali this. Kadang-kadang, saya bahkan mengubah fungsi sehingga alih-alih mengembalikan nilai, itu beroperasi pada anggota objek, supaya saya bisa melakukan ini. Itu luar biasa. :)
Inversus
4
Untuk setter teleskopik kembali sendiri dan dalam pembangun saya lebih suka menggunakan withName (String name) daripada setName (String name). Ketika Anda menunjukkan praktik umum dan harapan untuk setter adalah kembali batal.
Setters
Harap perkenalkan jeda baris sebelum setiap pemanggilan :) Dan konfigurasikan IDE Anda atau dapatkan yang tepat jika tidak menghargai ini.
MauganRa
Kerangka kerja yang banyak digunakan (misalnya Spring dan Hibernate) akan secara ketat (setidaknya mereka dulu) mematuhi konvensi void-setters
Legna

Jawaban:

83

Saya tidak berpikir ada sesuatu yang salah dengan itu, itu hanya masalah gaya. Ini berguna saat:

  • Anda perlu mengatur banyak bidang sekaligus (termasuk pada konstruksi)
  • Anda tahu bidang mana yang perlu Anda atur saat Anda menulis kode, dan
  • ada banyak kombinasi berbeda untuk bidang mana yang ingin Anda atur.

Alternatif untuk metode ini mungkin:

  1. Satu mega konstruktor (sisi negatifnya: Anda mungkin melewatkan banyak nilai nol atau standar, dan semakin sulit untuk mengetahui nilai mana yang sesuai dengan apa)
  2. Beberapa konstruktor kelebihan beban (sisi negatifnya: menjadi sangat berat setelah Anda memiliki lebih dari beberapa)
  3. Metode pabrik / statis (downside: sama dengan konstruktor yang kelebihan beban - menjadi berat saat ada lebih dari beberapa)

Jika Anda hanya akan menetapkan beberapa properti pada satu waktu saya akan mengatakan itu tidak layak mengembalikan 'ini'. Itu pasti jatuh jika Anda kemudian memutuskan untuk mengembalikan sesuatu yang lain, seperti status / indikator keberhasilan / pesan.

Tom Clift
sumber
1
baik, umumnya Anda tidak mengembalikan apa pun dari setter, dengan konvensi.
Ken Liu
17
Mungkin tidak memulainya, tetapi seorang setter tidak serta-merta menjaga tujuan aslinya. Apa yang dulunya variabel dapat berubah menjadi keadaan yang mencakup beberapa variabel atau memiliki efek samping lainnya. Beberapa setter mungkin mengembalikan nilai sebelumnya, yang lain mungkin mengembalikan indikator kegagalan jika kegagalan terlalu umum untuk pengecualian. Itu menimbulkan poin menarik lain: bagaimana jika alat / kerangka kerja yang Anda gunakan tidak mengenali setter Anda ketika mereka memiliki nilai kembali?
Tom Clift
12
@Tom poin bagus, melakukan ini melanggar konvensi "Java Bean" untuk getter dan setter.
Andy White
2
@TomClift Apakah melanggar konvensi "Java Bean" menyebabkan masalah? Apakah perpustakaan yang menggunakan konvensi "Java Bean" melihat jenis kembali atau hanya parameter metode dan nama metode.
Theo Briscoe
3
itulah sebabnya pola Builder ada, setter tidak boleh mengembalikan sesuatu, sebagai gantinya, buat builder jika yang dibutuhkan terlihat lebih baik dan kode lebih sedikit :)
RicardoE
106

Itu bukan praktik yang buruk. Ini adalah praktik yang semakin umum. Sebagian besar bahasa tidak mengharuskan Anda untuk berurusan dengan objek yang dikembalikan jika Anda tidak ingin sehingga itu tidak mengubah sintaks penggunaan setter "normal" tetapi memungkinkan Anda untuk rantai setter bersama-sama.

Ini biasa disebut pola pembangun atau antarmuka yang lancar .

Ini juga umum di API Java:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();
cletus
sumber
26
Ini sering digunakan dalam pembangun, tapi saya tidak akan mengatakan "ini ... disebut pola Builder".
Laurence Gonsalves
10
Lucu bagi saya bahwa beberapa alasan untuk antarmuka yang lancar adalah mereka membuat kode lebih mudah dibaca. Saya bisa melihatnya lebih nyaman untuk menulis, tetapi menurut saya lebih sulit untuk dibaca. Itulah satu-satunya ketidaksepakatan nyata yang saya miliki dengannya.
Brent Menulis Kode
30
Ini juga dikenal sebagai antipattern train-wreck. Masalahnya adalah bahwa ketika jejak tumpukan pengecualian null-pointer berisi garis seperti ini, Anda tidak tahu permintaan mana yang dikembalikan nol. Itu bukan untuk mengatakan bahwa merantai harus dihindari dengan cara apa pun, tetapi berhati-hatilah terhadap perpustakaan yang buruk (khususnya rumah-brewn).
ddimitrov
18
@ddimitrov selama Anda membatasi untuk mengembalikan ini, itu tidak akan menjadi masalah (hanya invokasi pertama yang dapat melempar NPE)
Stefan
4
lebih mudah untuk menulis DAN membaca dengan asumsi bahwa Anda meletakkan linebreak dan indentasi di mana keterbacaan akan menderita sebaliknya! (karena itu menghindari kekacauan berulang kode berulang seperti bla.foo.setBar1(...) ; bla.foo.setBar2(...)ketika Anda bisa menulis bla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)(tidak dapat menggunakan linebreak dalam komentar SO seperti ini :-( ... harap Anda mendapatkan intinya dengan mempertimbangkan 10 setter atau panggilan yang lebih kompleks)
Andreas Dietrich
90

Untuk meringkas:

  • itu disebut "antarmuka lancar", atau "metode chaining".
  • ini bukan Java "standar", meskipun Anda melihatnya lebih banyak hari ini (berfungsi dengan baik di jQuery)
  • itu melanggar spesifikasi JavaBean, jadi itu akan pecah dengan berbagai alat dan perpustakaan, terutama pembangun JSP dan Spring.
  • ini dapat mencegah beberapa optimasi yang biasanya dilakukan JVM
  • beberapa orang berpikir itu membersihkan kode, yang lain berpikir itu "mengerikan"

Beberapa poin lain yang tidak disebutkan:

  • Ini melanggar prinsip bahwa setiap fungsi harus melakukan satu (dan hanya satu) hal. Anda mungkin atau mungkin tidak percaya pada hal ini, tetapi di Jawa saya percaya itu bekerja dengan baik.

  • IDE tidak akan menghasilkan ini untuk Anda (secara default).

  • Saya akhirnya, inilah titik data dunia nyata. Saya punya masalah menggunakan perpustakaan yang dibangun seperti ini. Pembuat kueri Hibernate adalah contohnya di pustaka yang ada. Karena metode set * Query mengembalikan kueri, mustahil untuk mengetahui hanya dengan melihat tanda tangan bagaimana menggunakannya. Sebagai contoh:

    Query setWhatever(String what);
  • Ini memperkenalkan ambiguitas: apakah metode memodifikasi objek saat ini (pola Anda) atau, mungkin Query benar-benar abadi (pola yang sangat populer dan berharga), dan metode ini mengembalikan yang baru. Itu hanya membuat perpustakaan lebih sulit untuk digunakan, dan banyak programmer tidak memanfaatkan fitur ini. Jika setter adalah setter, akan lebih jelas bagaimana menggunakannya.

ndp
sumber
5
btw, itu "lancar", bukan "cair" ... karena di dalamnya memungkinkan Anda menyusun serangkaian panggilan metode seperti bahasa lisan.
Ken Liu
1
Poin terakhir tentang imutabilitas sangat penting. Contoh paling sederhana adalah String. Pengembang Java berharap bahwa ketika menggunakan metode pada String, mereka mendapatkan instance yang sama sekali baru dan bukan instance yang sama tetapi dimodifikasi. Dengan antarmuka yang lancar harus disebutkan dalam dokumentasi metode bahwa objek yang dikembalikan adalah 'ini' dan bukan contoh baru.
MeTTeO
7
Meskipun saya setuju secara keseluruhan, saya tidak setuju bahwa ini melanggar prinsip "lakukan hanya satu hal". Kembali thisbukanlah ilmu roket yang rumit. :-)
user949300
poin tambahan: itu juga melanggar prinsip pemisahan Command-query.
Marton_hun
84

Saya lebih suka menggunakan metode 'dengan' untuk ini:

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

Jadi:

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

Peringatan : withXsintaks ini biasanya digunakan untuk menyediakan "setter" untuk objek yang tidak dapat diubah, jadi penelepon dari metode ini mungkin mengharapkan mereka untuk membuat objek baru daripada bermutasi pada instance yang ada. Mungkin kata-kata yang lebih masuk akal adalah sesuatu seperti:

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

Dengan konvensi penamaan chainsetXyz () hampir semua orang pasti senang.

kualidafial
sumber
18
+1 Untuk konvensi yang menarik. Saya tidak akan mengadopsinya dalam kode saya sendiri karena sepertinya sekarang Anda harus memiliki get,, setdan withuntuk setiap bidang kelas. Ini masih merupakan solusi yang menarik. :)
Paul Manta
1
Itu tergantung pada seberapa sering Anda memanggil setter. Saya telah menemukan bahwa jika setter itu sering dipanggil, layak untuk menambahkannya karena menyederhanakan kode di tempat lain. YMMV
qualidafial
2
Dan jika Anda menambahkan ini ke Project Lombok's @Getter/@Setterpenjelasan ... yang akan fantastis untuk chaining. Atau Anda bisa menggunakan sesuatu yang sangat mirip dengan Kestrelkombinator ( github.com/raganwald/Katy ) yang digunakan oleh JQuery dan Javascript.
Ehtesh Choudhury
4
@ AlikElzin-kilaka Sebenarnya saya hanya memperhatikan bahwa kelas java.time yang tidak dapat diubah di Java 8 menggunakan pola ini, misal LocalDate.withMonth, withYear, etc.
qualidafial
4
yang withprefix adalah konvensi yang berbeda. sebagai @qualidafial memberi contoh. metode yang diawali dengan withseharusnya tidak mengembalikan thistetapi lebih sebagai contoh baru seperti contoh saat ini tetapi withperubahan itu. Ini dilakukan ketika Anda ingin benda Anda tidak berubah. Jadi ketika saya melihat metode diawali dengan withsaya berasumsi saya akan mendapatkan objek baru, bukan objek yang sama.
tempcke
26

Jika Anda tidak ingin kembali 'this'dari setter tetapi tidak ingin menggunakan opsi kedua, Anda dapat menggunakan sintaks berikut untuk mengatur properti:

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

Sebagai tambahan saya pikir itu sedikit lebih bersih di C #:

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});
Luke Quinane
sumber
16
inisialisasi penjepit ganda mungkin memiliki masalah dengan sama karena menciptakan kelas batin anonim; lihat c2.com/cgi/wiki?DoubleBraceInisialisasi
Csaba_H
@Csaba_H Jelas bahwa masalahnya adalah kesalahan orang yang melakukan ceroboh pada equalsmetode ini. Ada cara yang sangat bersih untuk berurusan dengan kelas anonim equalsjika Anda tahu apa yang Anda lakukan.
AJMansfield
membuat kelas (anonim) baru hanya untuk itu? untuk setiap contoh?
user85421
11

Itu tidak hanya merusak konvensi getter / setter, tetapi juga merusak kerangka referensi metode Java 8. MyClass::setMyValueadalah BiConsumer<MyClass,MyValue>, dan myInstance::setMyValuemerupakan Consumer<MyValue>. Jika Anda memiliki setter kembali this, maka itu bukan lagi contoh yang valid Consumer<MyValue>, tetapi lebih dari Function<MyValue,MyClass>, dan akan menyebabkan apa pun menggunakan referensi metode untuk setter tersebut (dengan asumsi mereka adalah metode batal) untuk istirahat.

Steve K.
sumber
2
Akan luar biasa jika Java memiliki beberapa cara untuk membebani dengan tipe kembali, bukan hanya JVM. Anda bisa mem-bypass banyak perubahan yang rusak ini dengan mudah.
Adowrath
1
Anda selalu dapat mendefinisikan antarmuka fungsional yang memperluas keduanya Consumer<A>dan Function<A,B>dengan memberikan implementasi standar void accept(A a) { apply(a); }. Maka dapat dengan mudah digunakan sebagai salah satu dan tidak akan merusak kode apa pun yang membutuhkan bentuk tertentu.
Steve K
Argh! Ini benar-benar salah! Ini disebut void-kompatibilitas. Setter yang mengembalikan nilai dapat bertindak sebagai Konsumen. ideone.com/ZIDy2M
Michael
8

Saya tidak tahu Java tapi saya sudah melakukan ini di C ++. Orang lain mengatakan itu membuat garis sangat panjang dan sangat sulit dibaca, tapi saya sudah sering melakukannya seperti ini:

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

Ini bahkan lebih baik:

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

setidaknya, saya pikir. Tapi Anda dipersilakan untuk menurunkan saya dan memanggil saya seorang programmer yang buruk jika Anda mau. Dan saya tidak tahu apakah Anda diizinkan untuk melakukan ini di Jawa.

Carson Myers
sumber
"Bahkan lebih baik" tidak cocok untuk fungsionalitas kode Sumber Format yang tersedia di IDE modern. Sayangnya.
Thorbjørn Ravn Andersen
Anda mungkin benar ... Satu-satunya formatter otomatis yang saya gunakan adalah indentasi otomatis emacs.
Carson Myers
2
Pemformat kode sumber dapat dipaksa dengan // sederhana setelah setiap metode memanggil dalam rantai. Itu sedikit jelek kode Anda, tetapi tidak sebanyak seri vertikal Anda diformat ulang secara horizontal.
qualidafial
@ equalidafial Anda tidak perlu //setelah setiap metode jika Anda mengkonfigurasi IDE Anda untuk tidak bergabung dengan baris yang sudah dibungkus (misalnya Eclipse> Properties> Java> Style Code> Formatter> Line Wrapping> Jangan pernah bergabung dengan baris yang sudah dibungkus).
DJDaveMark
6

Skema ini (pun intended), yang disebut 'antarmuka lancar', menjadi cukup populer sekarang. Itu bisa diterima, tapi itu bukan cangkir teh saya.

Sutra Siang
sumber
6

Karena tidak mengembalikan batal, itu bukan lagi setter properti JavaBean yang valid. Itu mungkin penting jika Anda salah satu dari tujuh orang di dunia menggunakan alat visual "Bean Builder", atau salah satu dari 17 orang menggunakan elemen JSP-bean-setProperty.

Ken
sumber
Ini juga penting jika Anda menggunakan kerangka kerja sadar kacang seperti Spring.
ddimitrov
6

Setidaknya secara teori , ini dapat merusak mekanisme optimisasi JVM dengan mengatur dependensi palsu antara panggilan.

Seharusnya gula sintaksis, tetapi sebenarnya dapat membuat efek samping di mesin virtual Java 43 super-cerdas.

Itu sebabnya saya memilih tidak, jangan menggunakannya.

Marian
sumber
10
Menarik ... bisakah Anda sedikit mengembangkannya?
Ken Liu
3
Pikirkan saja bagaimana prosesor superscalar menangani eksekusi paralel. Objek untuk mengeksekusi setmetode kedua tergantung pada setmetode pertama meskipun diketahui oleh programmer.
Marian
2
Saya masih tidak mengikuti. Jika Anda mengatur Foo dan kemudian Bar dengan dua pernyataan terpisah, objek yang Anda atur Bar memiliki keadaan yang berbeda dari objek yang Anda atur Foo. Jadi kompiler juga tidak bisa memparalelkan pernyataan itu. Setidaknya, saya tidak melihat bagaimana hal itu bisa terjadi tanpa memperkenalkan asumsi yang tidak beralasan. (Karena saya tidak tahu tentang hal itu, saya tidak akan menyangkal bahwa Java 43 memang melakukan paralelisasi dalam satu kasus tetapi bukan yang lain dan memperkenalkan asumsi yang tidak beralasan dalam satu kasus tetapi tidak yang lain).
masonk
12
Jika Anda tidak tahu, tes. -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Java7 jdk jelas sebaris metode chained, dan melakukan sekitar jumlah iterasi yang sama yang diperlukan untuk menandai setter kosong seperti panas dan sebaris pula. Berpikir Anda meremehkan kekuatan algoritma pemangkasan opcode JVM; jika ia tahu Anda mengembalikan ini, ia akan melewatkan opcode jrs (pernyataan pengembalian java) dan biarkan ini di stack.
Ajax
1
Andreas, saya setuju tetapi masalah muncul ketika Anda memiliki kode demi kode yang tidak efisien. 99% dari waktu Anda harus kode untuk kejelasan yang dikatakan banyak di sini. Tetapi ada juga saat-saat ketika Anda harus realistis dan menggunakan pengalaman bertahun-tahun untuk mengoptimalkan secara prematur dalam pengertian arsitektur umum.
LegendLength
6

Itu sama sekali bukan praktik buruk. Tapi itu tidak kompatibel dengan JavaBeans Spec .

Dan ada banyak spesifikasi tergantung pada pengakses standar tersebut.

Anda selalu dapat membuat mereka hidup berdampingan satu sama lain.

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

Sekarang kita bisa menggunakannya bersama.

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

Di sinilah versi lain untuk objek abadi.

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

Sekarang kita bisa melakukan ini.

new Some.Builder().value("value").build().getValue();
Jin Kwon
sumber
2
Hasil edit saya ditolak, tetapi contoh Pembuat Anda tidak benar. Pertama, .value () tidak mengembalikan apa pun, dan bahkan tidak menyetel somebidang. Kedua, Anda harus menambahkan perlindungan dan diatur someke nulldalam build () sehingga Somebenar-benar tidak dapat diubah, jika tidak Anda dapat memanggil builder.value()instance Builder yang sama lagi. Dan Terakhir, ya Anda memiliki pembangun, tetapi Anda Somemasih memiliki konstruktor publik, artinya Anda tidak secara terbuka menganjurkan penggunaan Builder, yaitu pengguna tidak mengetahuinya selain dengan mencoba atau mencari metode untuk menetapkan kebiasaan valuesama sekali.
Adowrath
@Adrathrath jika jawabannya salah, Anda harus menulis jawaban Anda sendiri, jangan mencoba dan mengedit bentuk orang lain
CalvT
1
@ JinKwon Hebat. Terima kasih! Dan maaf jika aku tampak kasar sebelumnya.
Adowrath
1
@Adowrath Jangan ragu untuk komentar tambahan apa pun untuk peningkatan. FYI, saya bukan orang yang menolak suntingan Anda. :)
Jin Kwon
1
Saya tahu saya tahu. ^^ Dan terima kasih untuk versi baru, yang sekarang menyediakan pembangun "Immutable-Some" yang bisa berubah. Dan ini solusi yang lebih pintar daripada upaya edit saya yang, sebagai perbandingan, mengacaukan kode.
Adowrath
4

Jika Anda menggunakan konvensi yang sama di seluruh aplikasi, sepertinya tidak masalah.

Di sisi lain jika bagian yang ada dari aplikasi Anda menggunakan konvensi standar saya akan tetap menggunakannya dan menambahkan pembangun ke kelas yang lebih rumit

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;
    }
}
Marcin Szymczak
sumber
1
Inilah tepatnya yang dirancang untuk diperbaiki oleh pola Builder. Itu tidak merusak lambdas di Java 8, itu tidak merusak alat JavaBeans licik, dan itu tidak menyebabkan masalah optimasi di JVM (karena objek hanya ada selama instantiation). Ini juga memecahkan masalah "terlalu banyak konstruktor" yang Anda dapatkan dengan tidak menggunakan pembangun, serta menghilangkan polusi timbunan dari kelas anonim penjepit ganda.
ndm13
Poin menarik - Saya memperhatikan bahwa jika hampir tidak ada di kelas Anda yang tidak dapat diubah, (misalnya GUI yang sangat dapat dikonfigurasi), maka Anda mungkin dapat menghindari Builder sama sekali.
Philip Guin
4

Paulo Abrantes menawarkan cara lain untuk membuat JavaBean setter fasih: mendefinisikan kelas pembangun batin untuk setiap JavaBean. Jika Anda menggunakan alat yang bingung oleh setter yang mengembalikan nilai, pola Paulo bisa membantu.

Jim Ferrans
sumber
2
@ cdunn2001, gunakan mesin
wayback
3

Saya mendukung pemukim memiliki "ini" kembali. Saya tidak peduli jika itu tidak sesuai dengan kacang. Bagi saya, jika boleh memiliki ekspresi / pernyataan "=", maka seters bahwa nilai pengembalian baik-baik saja.

Monty Hall
sumber
2

Saya dulu lebih suka pendekatan ini tetapi saya telah memutuskan untuk tidak melakukannya.

Alasan:

  • Keterbacaan. Itu membuat kode lebih mudah dibaca untuk membuat setiap setFoo () pada baris terpisah. Anda biasanya membaca kode berkali-kali, lebih banyak daripada saat Anda menuliskannya.
  • Efek samping: setFoo () seharusnya hanya mengatur field foo, tidak ada yang lain. Mengembalikan ini adalah tambahan "APA itu".

Pola Builder yang saya lihat tidak menggunakan konvensi setFoo (foo) .setBar (bar) tetapi lebih banyak foo (foo) .bar (bar). Mungkin karena alasan-alasan itu.

Memang, seperti biasa soal selera. Saya hanya menyukai pendekatan "kejutan kecil".

Thorbjørn Ravn Andersen
sumber
2
Saya setuju dengan efek sampingnya. Setter yang mengembalikan barang melanggar nama mereka. Anda mengatur foo, tetapi Anda mendapatkan objek kembali? Apakah ini objek baru atau sudahkah saya mengubah yang lama?
crunchdog
2

Pola khusus ini disebut Metode Chaining. Tautan Wikipedia , ini memiliki lebih banyak penjelasan dan contoh bagaimana melakukannya dalam berbagai bahasa pemrograman.

PS: Baru saja berpikir untuk meninggalkannya di sini, karena saya sedang mencari nama yang spesifik.

capt.swag
sumber
1

Pada pandangan pertama: "Mengerikan!".

Dipikirkan lebih lanjut

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

sebenarnya lebih sedikit kesalahan daripada

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

Sangat menarik. Menambahkan ide ke toolbag ...

djna
sumber
1
Tidak, ini masih mengerikan. Dari perspektif pemeliharaan, yang terakhir lebih baik karena lebih mudah dibaca. Selain itu, pemeriksa kode otomatis seperti CheckStyle akan menetapkan baris menjadi 80 karakter secara default - kode tetap akan dibungkus, menambah masalah keterbacaan / pemeliharaan. Dan akhirnya - Jawa; tidak ada untungnya menulis semuanya dalam satu baris ketika itu akan dikompilasi ke kode byte.
OMG Ponies
1
secara pribadi, saya pikir yang pertama lebih mudah dibaca, terutama jika Anda membuat beberapa objek dengan cara ini.
Ken Liu
@ Ken: Buat kode metode. Tulis satu salinan dalam format fasih; salinan lain di yang lain. Sekarang berikan dua salinan kepada beberapa orang, dan tanyakan yang mana yang menurut mereka lebih mudah dibaca. Lebih cepat dibaca, lebih cepat ke kode.
OMG Ponies
Seperti kebanyakan alat, ini bisa mudah digunakan secara berlebihan. JQuery berorientasi pada teknik ini dan karena itu rentan terhadap rantai panggilan lama yang saya temukan sebenarnya mengganggu keterbacaan.
staticsan
2
Tidak masalah jika ada linebreak sebelum setiap titik dan lekukan. Kemudian, itu akan dibaca sebagai versi kedua. Sebenarnya lebih, karena tidak ada yang mubazir listdi awal.
MauganRa
1

Ya, saya pikir itu ide yang bagus.

Jika saya bisa menambahkan sesuatu, bagaimana dengan masalah ini:

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

Ini akan berhasil:

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

Ini tidak akan diterima oleh Eclipse! :

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

Ini karena setName () mengembalikan Orang dan bukan Teman, dan tidak ada PeoplesetNickName.

Bagaimana kita bisa menulis setter untuk mengembalikan kelas SELF alih-alih nama kelas?

Sesuatu seperti ini akan baik-baik saja (jika kata kunci DIRI akan ada). Apakah ini ada?

class People
{
    private String name;
    public SELF setName(String name)
    {
        this.name = name;
        return this;
    }
}
Baptiste
sumber
1
Ada beberapa kompiler Java lain selain Eclipse yang tidak akan menerimanya :). Pada dasarnya, Anda menjalankan unggas dari sistem tipe Java (yang statis, tidak dinamis seperti beberapa bahasa scripting): Anda harus membuang apa yang keluar dari setName () ke Teman sebelum Anda dapat menetapkanNickName () di atasnya. Jadi, sayangnya, untuk hierarki warisan ini menghilangkan banyak keuntungan keterbacaan dan menjadikan teknik yang berpotensi berguna ini tidak terlalu berguna.
Cornel Masson
5
Gunakan obat generik. class Chainable <Self extends Chainable> {public Self doSomething () {return (Self) this;}} Ini bukan tipe yang aman secara teknis (kelas akan dilemparkan jika Anda mengimplementasikan kelas dengan tipe Self yang tidak dapat Anda perhatikan), tetapi adalah tata bahasa yang benar, dan subclass akan mengembalikan tipenya sendiri.
Ajax
Selain itu: "DIRI" yang digunakan Baptiste ini disebut tipe mandiri, tidak ada dalam banyak bahasa (Scala adalah satu-satunya yang dapat saya pikirkan saat ini), di mana penggunaan Ajax tentang Generik adalah apa yang disebut "Anehnya" pola templat berulang ", yang mencoba mengatasi kekurangan tipe diri, tetapi ada beberapa kelemahan: (dari class C<S extends C<S>>, yang lebih aman daripada dataran S extends C. a) Jika A extends B, dan B extends C<B>, dan Anda memiliki nilai A, Anda hanya tahu bahwa B dikembalikan kecuali A menimpa masing-masing dan setiap metode. b) Anda tidak dapat menunjukkan lokal, non-mentah C<C<C<C<C<...>>>>>.
Adowrath
1

Secara umum ini adalah praktik yang baik, tetapi Anda mungkin perlu untuk fungsi tipe-set menggunakan tipe Boolean untuk menentukan apakah operasi berhasil diselesaikan atau tidak, itu juga salah satu caranya. Secara umum, tidak ada dogma untuk mengatakan bahwa ini baik atau ranjang, itu berasal dari situasi, tentu saja.

Narek
sumber
2
Bagaimana dengan menggunakan pengecualian untuk menunjukkan kondisi kesalahan? Kode kesalahan dapat dengan mudah diabaikan karena banyak programmer C belajar dengan susah payah. Pengecualian dapat menggelembungkan tumpukan ke titik di mana mereka dapat ditangani.
ddimitrov
Pengecualian biasanya lebih disukai, tetapi kode kesalahan juga berguna saat Anda tidak dapat menggunakan pengecualian.
Narek
1
Hai @Narek, mungkin Anda bisa menguraikan dalam kasus apa lebih baik menggunakan kode kesalahan dalam setter, daripada pengecualian dan mengapa?
ddimitrov
0

Dari pernyataan itu

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

saya melihat dua hal

1) Pernyataan tidak berarti. 2) Kurangnya keterbacaan.

Madhu
sumber
0

Ini mungkin kurang mudah dibaca

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

atau ini

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

Ini jauh lebih mudah dibaca daripada:

Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!")); 
list.add(employee); 
Scott
sumber
8
Saya pikir itu cukup mudah dibaca jika Anda tidak mencoba untuk meletakkan semua kode Anda pada satu baris.
Ken Liu
0

Saya telah membuat setter saya cukup lama dan satu-satunya masalah sebenarnya adalah dengan perpustakaan yang tetap dengan getPropertyDescriptors yang ketat untuk mendapatkan aksesor pembaca kacang / penulis bean. Dalam kasus itu, "kacang" java Anda tidak akan memiliki pembungkus yang Anda harapkan.

Sebagai contoh, saya belum mengujinya dengan pasti, tetapi saya tidak akan terkejut bahwa Jackson tidak akan mengenali mereka sebagai setter ketika membuat Anda objek java dari json / maps. Saya harap saya salah dalam hal ini (saya akan segera mengujinya).

Bahkan, saya sedang mengembangkan ORM SQL sentris ringan dan saya harus menambahkan beberapa kode getPropertyDescriptors beyong ke setter diakui yang mengembalikan ini.

Jeremy Chone
sumber
0

Dulu jawaban, tapi dua sen saya ... Tidak apa-apa. Saya berharap antarmuka yang lancar ini digunakan lebih sering.

Pengulangan variabel 'pabrik' tidak menambahkan info lebih lanjut di bawah ini:

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

Ini lebih bersih, ya:

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

Tentu saja, sebagai salah satu jawaban yang telah disebutkan, Java API harus di-tweak untuk melakukan ini dengan benar untuk beberapa situasi, seperti warisan dan alat.

Josef.B
sumber
0

Lebih baik menggunakan konstruksi bahasa lain jika tersedia. Misalnya, dalam Kotlin, Anda akan menggunakan dengan , menerapkan , atau biarkan . Jika menggunakan pendekatan ini, Anda tidak perlu mengembalikan instance dari setter Anda.

Pendekatan ini memungkinkan kode klien Anda menjadi:

  • Tidak peduli dengan tipe pengembalian
  • Lebih mudah dirawat
  • Hindari efek samping kompiler

Berikut ini beberapa contohnya.

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"
}
Steven Spungin
sumber
0

Jika saya menulis API, saya menggunakan "kembalikan ini" untuk menetapkan nilai yang hanya akan ditetapkan satu kali. Jika saya memiliki nilai lain yang harus dapat diubah oleh pengguna, saya menggunakan setter void standar sebagai gantinya.

Namun, itu benar-benar masalah preferensi dan pengaturan rantai memang terlihat cukup keren, menurut saya.

Kaiser Keister
sumber
0

Saya setuju dengan semua poster yang mengklaim bahwa ini melanggar spesifikasi JavaBeans. Ada alasan untuk melestarikan itu, tetapi saya juga merasa bahwa penggunaan Pola Pembangun ini (yang disinggung) memiliki tempatnya; selama itu tidak digunakan di mana-mana, itu harus dapat diterima. "It's Place", bagi saya, adalah titik akhir panggilan untuk metode "build ()".

Ada cara lain untuk mengatur semua hal ini tentu saja, tetapi keuntungan di sini adalah bahwa ia menghindari 1) banyak konstruktor publik parameter dan 2) objek yang ditentukan sebagian. Di sini, Anda memiliki pembangun mengumpulkan apa yang dibutuhkan dan kemudian memanggilnya "build ()" di akhir, yang kemudian dapat memastikan bahwa objek yang ditentukan sebagian tidak dibangun, karena operasi itu dapat diberikan visibilitas kurang dari-publik. Alternatifnya adalah "objek parameter", tetapi IMHO hanya mendorong masalah kembali satu tingkat.

Saya tidak suka konstruktor banyak-parameter karena mereka membuatnya lebih mungkin bahwa banyak argumen tipe-sama diteruskan, yang dapat membuatnya lebih mudah untuk menyampaikan argumen yang salah ke parameter. Saya tidak suka menggunakan banyak setter karena objek dapat digunakan sebelum sepenuhnya dikonfigurasi. Selanjutnya, gagasan memiliki nilai default berdasarkan pilihan sebelumnya lebih baik disajikan dengan metode "build ()".

Singkatnya, saya pikir ini adalah praktik yang baik, jika digunakan dengan benar.

Javaneer
sumber
-4

Kebiasaan buruk: seorang setter mengatur getter get

bagaimana dengan mendeklarasikan metode secara eksplisit, yang melakukannya untuk U

setPropertyFromParams(array $hashParamList) { ... }
Ulrich Tevi Horus
sumber
tidak baik untuk pelengkapan otomatis dan refactoring serta keterbacaan anb kode boilerplate
Andreas Dietrich
Karena: 1) Anda harus mengingat urutan atau membacanya dari docs, 2) Anda kehilangan semua jenis keamanan waktu kompilasi, 3) Anda harus memberikan nilai (ini dan 2) tentu saja bukan masalah dalam dinamika bahasa tentu saja), 4) Anda tidak dapat memperpanjang ini berguna selain memeriksa panjang array pada setiap doa, dan 5) jika Anda mengubah apa pun, baik itu memesan atau bahkan keberadaan nilai-nilai tertentu, Anda tidak dapat memeriksa bahwa tidak ada runtime atau mengkompilasi timew tanpa keraguan sama sekali . Dengan setter: 1), 2), 3): Tidak masalah. 4) Menambahkan metode baru tidak merusak apa pun. 5) pesan kesalahan eksplisit.
Adowrath