Mengelola konstruktor dengan banyak parameter di Java

105

Di beberapa proyek kami, ada hierarki kelas yang menambahkan lebih banyak parameter saat menuruni rantai. Di bagian bawah, beberapa kelas dapat memiliki hingga 30 parameter, 28 di antaranya baru saja diteruskan ke konstruktor super.

Saya akan mengakui bahwa menggunakan DI otomatis melalui sesuatu seperti Guice akan menyenangkan, tetapi karena beberapa alasan teknis, proyek khusus ini dibatasi untuk Java.

Konvensi pengorganisasian argumen menurut abjad menurut tipe tidak berfungsi karena jika sebuah tipe difaktor ulang (Lingkaran yang Anda berikan untuk argumen 2 sekarang menjadi Bentuk) itu bisa tiba-tiba rusak.

Pertanyaan ini mungkin spesifik dan penuh dengan kritik "Jika itu masalah Anda, Anda salah melakukannya pada tingkat desain", tetapi saya hanya mencari sudut pandang apa pun.

Steve Armstrong
sumber

Jawaban:

264

Pola Desain Builder mungkin bisa membantu. Perhatikan contoh berikut

public class StudentBuilder
{
    private String _name;
    private int _age = 14;      // this has a default
    private String _motto = ""; // most students don't have one

    public StudentBuilder() { }

    public Student buildStudent()
    {
        return new Student(_name, _age, _motto);
    }

    public StudentBuilder name(String _name)
    {
        this._name = _name;
        return this;
    }

    public StudentBuilder age(int _age)
    {
        this._age = _age;
        return this;
    }

    public StudentBuilder motto(String _motto)
    {
        this._motto = _motto;
        return this;
    }
}

Ini memungkinkan kita menulis kode seperti

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

Jika kita membiarkan field yang diperlukan (mungkin nama diperlukan) maka kita dapat meminta konstruktor Student untuk membuat pengecualian. Dan ini memungkinkan kita memiliki argumen default / opsional tanpa perlu melacak segala jenis urutan argumen, karena urutan panggilan tersebut akan bekerja sama baiknya.

Eli Courtwright
sumber
10
Tentu saja dengan impor statis Anda bahkan tidak perlu "melihat" "pembuat" ini sama sekali. Misalnya, Anda bisa memiliki nama metode statis (nama String) yang mengembalikan pembangun dan Siswa (StudentBuilder) yang mengembalikan siswa. Oleh karena itu Mahasiswa (nama ("Joe"). Usia (15). Motto ("Saya telah mengompol"));
oxbow_lakes
2
@oxbow_lakes: Dalam contoh Anda, kelas mana yang memiliki nama metode statis (nama String)?
pengguna443854
Secara teknis, dimungkinkan untuk menggunakan kelas Siswa untuk membangun siswa baru. Saya telah menambahkan metode di dalam kelas Mahasiswa dan itu bekerja dengan baik. Dengan cara ini saya tidak perlu memiliki kelas pembangun lain. Saya tidak yakin apakah ini diinginkan. Apakah ada alasan untuk menggunakan kelas (StudentBuilder) lain untuk membangunnya?
WVrock
1
@WVrock: Itu tergantung pada implementasi Anda. Seperti yang saya katakan dalam jawaban saya, melakukan ini dengan kelas siswa itu sendiri berpotensi meninggalkan kelas dalam keadaan setengah diinisialisasi, misalnya jika Anda memiliki bidang wajib yang belum diinisialisasi.
Eli Courtwright
@EliCourtwright Saya rasa ini tentang preferensi / desain kode. Alih-alih membuat konstruktor membuang pengecualian, saya membuat buildStudent()metode melempar pengecualian.
WVrock
24

Dapatkah Anda merangkum parameter terkait di dalam suatu objek?

misalnya, jika parameternya seperti


MyClass(String house, String street, String town, String postcode, String country, int foo, double bar) {
  super(String house, String street, String town, String postcode, String country);
  this.foo = foo;
  this.bar = bar;

maka Anda dapat memiliki:


MyClass(Address homeAddress, int foo, double bar) {
  super(homeAddress);
  this.foo = foo;
  this.bar = bar;
}

JeeBee
sumber
14

Apa yang mungkin ingin Anda lakukan adalah memiliki kelas Builder. Kemudian Anda akan melakukan sesuatu seperti ini:

MyObject obj = new MyObjectBuilder().setXxx(myXxx)
                                    .setYyy(myYyy)
                                    .setZzz(myZzz)
                                    // ... etc.
                                    .build();

Lihat halaman 8 dan setelah presentasi Josh Bloch (PDF) ini, atau ulasan tentang Java Efektif ini

Michael Myers
sumber
8

Nah, menggunakan pola builder mungkin bisa menjadi salah satu solusinya.

Tetapi begitu Anda sampai pada 20 sampai 30 parameter, saya akan menebak bahwa ada hubungan yang tinggi antara parameter tersebut. Jadi (seperti yang disarankan) membungkusnya menjadi objek data yang logis mungkin paling masuk akal. Dengan cara ini objek data sudah dapat memeriksa validitas batasan antara parameter.

Untuk semua proyek saya di masa lalu, begitu saya sampai pada titik memiliki terlalu banyak parameter (dan itu 8 bukan 28!) Saya dapat membersihkan kode dengan membuat model data yang lebih baik.


sumber
4

Karena Anda dibatasi pada Java 1.4, jika Anda menginginkan DI maka Spring akan menjadi pilihan yang sangat layak. DI hanya membantu di tempat-tempat di mana parameter konstruktor adalah layanan atau sesuatu yang tidak berbeda selama waktu proses.

Jika Anda memiliki semua konstruktor yang berbeda karena Anda menginginkan opsi variabel tentang cara membuat objek, Anda harus mempertimbangkan secara serius untuk menggunakan pola Builder.

Guðmundur Bjarni
sumber
Parameternya sebagian besar adalah layanan seperti yang Anda sebutkan, jadi DI adalah yang saya perlukan. Saya pikir pola Builder yang disebutkan dalam beberapa jawaban lain persis seperti yang saya harapkan.
Steve Armstrong
4

Solusi terbaik adalah tidak memiliki terlalu banyak parameter dalam konstruktor. Hanya parameter yang benar-benar diperlukan dalam konstruktor, adalah parameter yang diperlukan untuk menginisialisasi objek dengan benar. Anda dapat memiliki konstruktor dengan beberapa parameter, tetapi juga memiliki konstruktor dengan hanya parameter minimum. Konstruktor tambahan memanggil konstruktor sederhana ini dan setelah itu penyetel untuk menyetel parameter lainnya. Dengan cara ini Anda dapat menghindari masalah rantai dengan semakin banyak parameter, tetapi juga memiliki beberapa konstruktor kenyamanan.

Mnementh
sumber
2

Saya benar-benar dapat merekomendasikan menggunakan Immutables atau POJOBuilder saat menggunakan pola pembangun.

Tomas Bjerre
sumber
1

Refactoring untuk mengurangi jumlah parameter dan kedalaman hierarki warisan Anda cukup banyak yang dapat saya pikirkan karena, tidak ada yang benar-benar akan membantu menjaga parameter 20-sesuatu tetap lurus. Anda hanya perlu melakukan setiap panggilan sambil melihat dokumentasi.

Satu hal yang dapat Anda lakukan, adalah mengelompokkan beberapa parameter yang dikelompokkan secara logis ke dalam objek tingkat yang lebih tinggi, tetapi itu memiliki masalahnya sendiri.

Aaron Maenpaa
sumber