Apa cara terbaik untuk merefaktor metode yang memiliki terlalu banyak parameter (6+)?

102

Kadang-kadang saya menemukan metode dengan jumlah parameter yang tidak nyaman. Lebih sering daripada tidak, mereka tampak seperti konstruktor. Sepertinya harus ada cara yang lebih baik, tapi saya tidak bisa melihat apa itu.

return new Shniz(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

Saya telah berpikir untuk menggunakan struct untuk mewakili daftar parameter, tetapi itu sepertinya mengalihkan masalah dari satu tempat ke tempat lain, dan membuat tipe lain dalam prosesnya.

ShnizArgs args = new ShnizArgs(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)
return new Shniz(args);

Jadi itu sepertinya bukan peningkatan. Jadi apa pendekatan terbaik?

rekursif
sumber
Anda mengatakan "struct". Istilah tersebut memiliki konotasi yang berbeda dalam bahasa pemrograman yang berbeda. Apa maksudmu itu?
Jay Bazuzi
1
Jika Anda mencari bahasa tertentu untuk disambiguasikan, gunakan C #. Tapi pada dasarnya, tas properti hanyalah milik sederhana. Ini memiliki properti bernama yang berbeda dengan jenis yang berbeda. Bisa didefinisikan sebagai kelas, tabel hash, struct, atau apapun.
rekursif
Artikel ini memiliki beberapa wawasan bagus tentang topik tersebut. Javascript spesifik, tetapi prinsipnya dapat diterapkan kembali ke bahasa lain.
lala

Jawaban:

94

Cara terbaik adalah menemukan cara untuk mengelompokkan argumen menjadi satu. Ini mengasumsikan, dan benar-benar hanya berfungsi jika, Anda akan berakhir dengan beberapa "pengelompokan" argumen.

Misalnya, jika Anda mengirimkan spesifikasi untuk sebuah persegi panjang, Anda dapat memasukkan x, y, width, dan height atau Anda bisa mengirimkan objek persegi panjang yang berisi x, y, width, dan height.

Carilah hal-hal seperti ini saat refactoring untuk membersihkannya. Jika argumen benar-benar tidak dapat digabungkan, mulailah melihat apakah Anda telah melanggar Prinsip Tanggung Jawab Tunggal.

Matthew Brubaker
sumber
4
Ide bagus tapi contoh buruk; konstruktor untuk Persegi Panjang harus memiliki 4 argumen. Ini akan lebih masuk akal jika metode mengharapkan 2 set koordinat / dimensi persegi panjang. Kemudian Anda bisa melewati 2 persegi panjang, bukan x1, x2, y1, y2 ...
Programmer Penjahat
2
Cukup adil. Seperti yang saya katakan, itu benar-benar masuk akal untuk dilakukan jika Anda berakhir dengan beberapa pengelompokan logis.
Matthew Brubaker
23
+1: Untuk Tanggung Jawab Tunggal, ini adalah salah satu dari sedikit komentar di semua jawaban yang benar-benar membahas masalah sebenarnya. Objek apa yang sangat membutuhkan 7 nilai independen untuk membentuk identitasnya.
AnthonyWJones
6
@Anony Saya tidak setuju. Data kondisi cuaca saat ini dapat memiliki lebih banyak nilai independen untuk membentuk identitasnya.
funct7
107

Saya akan menganggap yang Anda maksud C # . Beberapa dari hal ini juga berlaku untuk bahasa lain.

Anda memiliki beberapa opsi:

beralih dari konstruktor ke penyetel properti . Ini dapat membuat kode lebih mudah dibaca, karena jelas bagi pembaca nilai mana yang sesuai dengan parameter mana. Sintaks Object Initializer membuatnya terlihat bagus. Ini juga mudah diterapkan, karena Anda cukup menggunakan properti yang dibuat secara otomatis dan melewati penulisan konstruktor.

class C
{
    public string S { get; set; }
    public int I { get; set; }
}

new C { S = "hi", I = 3 };

Namun, Anda kehilangan keabadian, dan Anda kehilangan kemampuan untuk memastikan bahwa nilai yang diperlukan disetel sebelum menggunakan objek pada waktu kompilasi.

Pola Pembangun .

Pikirkan tentang hubungan antara stringdan StringBuilder. Anda bisa mendapatkan ini untuk kelas Anda sendiri. Saya suka menerapkannya sebagai kelas bersarang, jadi kelas Cmemiliki kelas terkait C.Builder. Saya juga menyukai antarmuka yang fasih pada pembangun. Jika dilakukan dengan benar, Anda bisa mendapatkan sintaks seperti ini:

C c = new C.Builder()
    .SetX(4)    // SetX is the fluent equivalent to a property setter
    .SetY("hello")
    .ToC();     // ToC is the builder pattern analog to ToString()

// Modify without breaking immutability
c = c.ToBuilder().SetX(2).ToC();

// Still useful to have a traditional ctor:
c = new C(1, "...");

// And object initializer syntax is still available:
c = new C.Builder { X = 4, Y = "boing" }.ToC();

Saya memiliki skrip PowerShell yang memungkinkan saya menghasilkan kode pembangun untuk melakukan semua ini, di mana inputnya terlihat seperti:

class C {
    field I X
    field string Y
}

Jadi saya bisa menghasilkan pada waktu kompilasi. partialkelas biarkan saya memperluas kelas utama dan pembangun tanpa mengubah kode yang dihasilkan.

Pemfaktoran ulang "Perkenalkan Objek Parameter" . Lihat Katalog Refactoring . Idenya adalah Anda mengambil beberapa parameter yang Anda teruskan dan memasukkannya ke tipe baru, lalu meneruskan instance dari tipe itu. Jika Anda melakukan ini tanpa berpikir, Anda akan kembali ke tempat Anda memulai:

new C(a, b, c, d);

menjadi

new C(new D(a, b, c, d));

Namun, pendekatan ini memiliki potensi terbesar untuk memberikan dampak positif pada kode Anda. Jadi, lanjutkan dengan mengikuti langkah-langkah berikut:

  1. Cari subset parameter yang masuk akal jika digabungkan. Mengelompokkan semua parameter fungsi secara sembarangan tidak akan menghasilkan banyak hasil; tujuannya adalah untuk memiliki pengelompokan yang masuk akal. Anda akan tahu bahwa Anda melakukannya dengan benar ketika nama tipe baru sudah jelas.

  2. Cari tempat lain di mana nilai-nilai ini digunakan bersama, dan gunakan tipe baru di sana juga. Kemungkinannya adalah, ketika Anda menemukan tipe baru yang bagus untuk sekumpulan nilai yang sudah Anda gunakan di semua tempat, tipe baru itu juga akan masuk akal di semua tempat itu.

  3. Cari fungsionalitas yang ada di kode yang ada, tetapi termasuk dalam tipe baru.

Misalnya, mungkin Anda melihat beberapa kode yang terlihat seperti:

bool SpeedIsAcceptable(int minSpeed, int maxSpeed, int currentSpeed)
{
    return currentSpeed >= minSpeed & currentSpeed < maxSpeed;
}

Anda bisa mengambil minSpeeddan maxSpeedparameter dan menempatkan mereka dalam jenis baru:

class SpeedRange
{
   public int Min;
   public int Max;
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return currentSpeed >= sr.Min & currentSpeed < sr.Max;
}

Ini lebih baik, tetapi untuk benar-benar memanfaatkan tipe baru, pindahkan perbandingan ke tipe baru:

class SpeedRange
{
   public int Min;
   public int Max;

   bool Contains(int speed)
   {
       return speed >= min & speed < Max;
   }
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return sr.Contains(currentSpeed);
}

Dan sekarang kita menuju ke suatu tempat: implementasi SpeedIsAcceptable()now mengatakan apa yang Anda maksud, dan Anda memiliki kelas yang berguna dan dapat digunakan kembali. (Langkah jelas berikutnya adalah membuat SpeedRangeke Range<Speed>.)

Seperti yang Anda lihat, Memperkenalkan Objek Parameter adalah awal yang baik, tetapi nilai sebenarnya adalah membantu kami menemukan jenis berguna yang telah hilang dari model kami.

Jay Bazuzi
sumber
4
Saya sarankan mencoba "Perkenalkan Objek Parameter" terlebih dahulu, dan hanya mundur ke opsi lain jika Anda tidak dapat menemukan objek parameter yang baik untuk dibuat.
Douglas Leeder
4
jawaban yang sangat bagus. jika Anda telah menyebutkan penjelasan refactoring sebelum c # gula sintaksis, ini akan dipilih IMHO lebih tinggi.
rpattabi
10
Ooh! +1 untuk "Anda akan tahu bahwa Anda benar ketika nama jenis baru sudah jelas."
Sean McMillan
20

Jika ini adalah konstruktor, terutama jika ada beberapa varian yang kelebihan muatan, Anda harus melihat pola Builder:

Foo foo = new Foo()
          .configBar(anything)
          .configBaz(something, somethingElse)
          // and so on

Jika ini adalah metode normal, Anda harus memikirkan tentang hubungan antara nilai yang diteruskan, dan mungkin membuat Objek Transfer.

kdgregory
sumber
Balasan yang bagus. Mungkin bahkan lebih relevan daripada jawaban "letakkan parameter di kelas" yang diberikan semua orang (termasuk saya).
Wouter Lievens
1
Mungkin ide yang buruk untuk membuat kelas Anda bisa berubah hanya untuk menghindari memberikan terlalu banyak parameter ke konstruktor.
Outlaw Programmer
@outlaw - jika mutabilitas menjadi perhatian, Anda dapat dengan mudah menerapkan semantik "jalankan sekali". Namun, sejumlah besar parameter ctor sering kali menunjukkan perlunya konfigurasi (atau, seperti yang dicatat orang lain, kelas yang mencoba melakukan terlalu banyak hal). (lanjutan)
kdgregory
Meskipun Anda dapat mengeksternalisasi konfigurasi, dalam banyak kasus hal itu tidak perlu, terutama jika itu didorong oleh status program atau standar untuk program tertentu (pikirkan pengurai XML, yang dapat mengenali ruang nama, memvalidasi dengan alat yang berbeda, & c).
kdgregory
Saya suka pola pembangun, tetapi saya memisahkan jenis pembangun yang tidak dapat diubah dan yang dapat diubah, seperti string / StringBuilder, tetapi saya menggunakan kelas bersarang: Foo / Foo.Builder. Saya memiliki skrip PowerShell untuk menghasilkan kode untuk melakukan ini untuk kelas data sederhana.
Jay Bazuzi
10

Jawaban klasik untuk ini adalah dengan menggunakan kelas untuk merangkum beberapa, atau semua, parameter. Secara teori kedengarannya bagus, tapi saya adalah tipe orang yang membuat kelas untuk konsep yang memiliki makna dalam domain tersebut, jadi tidak selalu mudah untuk menerapkan saran ini.

Misalnya, alih-alih:

driver.connect(host, user, pass)

Anda bisa menggunakan

config = new Configuration()
config.setHost(host)
config.setUser(user)
config.setPass(pass)
driver.connect(config)

YMMV

Wouter Lievens
sumber
5
Saya pasti lebih suka potongan kode pertama. Saya setuju, bahwa ada batasan tertentu, di atasnya parameter nomor menjadi jelek, tetapi menurut selera saya, 3 dapat diterima.
blabla999
10

Ini dikutip dari buku Fowler and Beck: "Refactoring"

Daftar Parameter Panjang

Pada hari-hari awal pemrograman, kami diajari untuk memasukkan semua yang dibutuhkan oleh rutin sebagai parameter. Ini dapat dimengerti karena alternatifnya adalah data global, dan data global itu jahat dan biasanya menyakitkan. Objek mengubah situasi ini karena jika Anda tidak memiliki sesuatu yang Anda butuhkan, Anda selalu dapat meminta objek lain untuk mendapatkannya untuk Anda. Jadi dengan objek Anda tidak meneruskan semua yang dibutuhkan metode; alih-alih Anda lulus cukup sehingga metode dapat mencapai semua yang dibutuhkannya. Banyak dari apa yang dibutuhkan metode tersedia di kelas host metode. Dalam program berorientasi objek, daftar parameter cenderung jauh lebih kecil daripada program tradisional. Ini bagus karena daftar parameter yang panjang sulit dipahami, karena menjadi tidak konsisten dan sulit digunakan, dan karena Anda akan selamanya mengubahnya karena Anda membutuhkan lebih banyak data. Sebagian besar perubahan dihapus dengan meneruskan objek karena Anda kemungkinan besar hanya perlu membuat beberapa permintaan untuk mendapatkan bagian data baru. Gunakan Replace Parameter with Method ketika Anda bisa mendapatkan data dalam satu parameter dengan membuat permintaan dari objek yang sudah Anda ketahui. Objek ini mungkin berupa bidang atau mungkin parameter lain. Gunakan Preserve Whole Object untuk mengambil sekumpulan data yang dikumpulkan dari sebuah objek dan menggantinya dengan objek itu sendiri. Jika Anda memiliki beberapa item data tanpa objek logis, gunakan Perkenalkan Objek Parameter. Ada satu pengecualian penting untuk melakukan perubahan ini. Ini adalah saat Anda secara eksplisit tidak ingin membuat ketergantungan dari objek yang dipanggil ke objek yang lebih besar. Dalam kasus tersebut, membongkar data dan mengirimkannya sebagai parameter adalah wajar, tetapi perhatikan rasa sakit yang terlibat. Jika daftar parameter terlalu panjang atau berubah terlalu sering, Anda perlu memikirkan kembali struktur dependensi Anda.

Youssef
sumber
7

Saya tidak ingin terdengar seperti orang yang bijak, tetapi Anda juga harus memeriksa untuk memastikan data yang Anda berikan benar - benar harus diedarkan: Mengirimkan barang ke konstruktor (atau metode dalam hal ini) berbau agak mirip sedikit penekanan pada perilaku suatu objek.

Jangan salah paham: Metode dan konstruktor akan memiliki banyak parameter kadang-kadang. Tetapi ketika ditemui, coba pertimbangkan untuk merangkum data dengan perilaku sebagai gantinya.

Jenis bau ini (karena kita berbicara tentang pemfaktoran ulang, kata mengerikan ini sepertinya tepat ...) mungkin juga terdeteksi untuk objek yang memiliki banyak properti atau getter / setter (baca: apa saja).

Daren Thomas
sumber
7

Ketika saya melihat daftar parameter yang panjang, pertanyaan pertama saya adalah apakah fungsi atau objek ini bekerja terlalu banyak. Mempertimbangkan:

EverythingInTheWorld earth=new EverythingInTheWorld(firstCustomerId,
  lastCustomerId,
  orderNumber, productCode, lastFileUpdateDate,
  employeeOfTheMonthWinnerForLastMarch,
  yearMyHometownWasIncorporated, greatGrandmothersBloodType,
  planetName, planetSize, percentWater, ... etc ...);

Tentu saja contoh ini sengaja konyol, tetapi saya telah melihat banyak program nyata dengan contoh hanya sedikit kurang konyol, di mana satu kelas digunakan untuk menampung banyak hal yang hampir tidak terkait atau tidak terkait, tampaknya hanya karena program pemanggil yang sama membutuhkan keduanya atau karena programmer kebetulan memikirkan keduanya pada saat yang bersamaan. Terkadang solusi yang mudah adalah dengan memecah kelas menjadi beberapa bagian yang masing-masing melakukan tugasnya sendiri.

Sedikit lebih rumit adalah ketika sebuah kelas benar-benar perlu menangani banyak hal logis, seperti pesanan pelanggan dan informasi umum tentang pelanggan. Dalam kasus ini, buat kelas untuk pelanggan dan kelas untuk pesanan, dan biarkan mereka berbicara satu sama lain jika perlu. Jadi, alih-alih:

 Order order=new Order(customerName, customerAddress, customerCity,
   customerState, customerZip,
   orderNumber, orderType, orderDate, deliveryDate);

Kami dapat memiliki:

Customer customer=new Customer(customerName, customerAddress,
  customerCity, customerState, customerZip);
Order order=new Order(customer, orderNumber, orderType, orderDate, deliveryDate);

Meskipun tentu saja saya lebih suka fungsi yang hanya mengambil 1 atau 2 atau 3 parameter, terkadang kita harus menerima bahwa, secara realistis, fungsi ini membutuhkan banyak, dan jumlah itu sendiri tidak benar-benar menciptakan kerumitan. Sebagai contoh:

Employee employee=new Employee(employeeId, firstName, lastName,
  socialSecurityNumber,
  address, city, state, zip);

Ya, itu sekumpulan bidang, tapi mungkin yang akan kita lakukan dengan mereka adalah menyimpannya ke catatan database atau melemparkannya ke layar atau semacamnya. Sebenarnya tidak banyak pemrosesan di sini.

Ketika daftar parameter saya menjadi panjang, saya lebih suka jika saya dapat memberikan field tipe data yang berbeda. Seperti ketika saya melihat fungsi seperti:

void updateCustomer(String type, String status,
  int lastOrderNumber, int pastDue, int deliveryCode, int birthYear,
  int addressCode,
  boolean newCustomer, boolean taxExempt, boolean creditWatch,
  boolean foo, boolean bar);

Dan kemudian saya melihatnya disebut dengan:

updateCustomer("A", "M", 42, 3, 1492, 1969, -7, true, false, false, true, false);

Saya khawatir. Melihat panggilan itu, sama sekali tidak jelas apa arti semua nomor, kode, dan bendera samar ini. Ini hanya meminta kesalahan. Seorang programmer mungkin dengan mudah bingung tentang urutan parameter dan secara tidak sengaja mengganti dua, dan jika mereka adalah tipe data yang sama, kompiler akan menerimanya. Saya lebih suka memiliki tanda tangan di mana semua hal ini adalah enum, jadi panggilan lewat dalam hal-hal seperti Type.ACTIVE daripada "A" dan CreditWatch.NO daripada "false", dll.

Jay
sumber
5

Jika beberapa parameter konstruktor bersifat opsional, masuk akal untuk menggunakan pembangun, yang akan mendapatkan parameter yang diperlukan dalam konstruktor, dan memiliki metode untuk yang opsional, mengembalikan pembangun, untuk digunakan seperti ini:

return new Shniz.Builder(foo, bar).baz(baz).quux(quux).build();

Rincian ini dijelaskan dalam Effective Java, 2nd Ed., Hal. 11. Untuk argumen metode, buku yang sama (p. 189) menjelaskan tiga pendekatan untuk memperpendek daftar parameter:

  • Bagi metode menjadi beberapa metode yang membutuhkan lebih sedikit argumen
  • Buat kelas anggota penolong statis untuk mewakili kelompok parameter, yaitu lewat a, DinoDonkeybukan dinodandonkey
  • Jika parameter bersifat opsional, pembangun di atas dapat diadopsi untuk metode, mendefinisikan objek untuk semua parameter, menyetel yang diperlukan dan kemudian memanggil beberapa metode eksekusi di atasnya
Fabian Steeg
sumber
4

Saya akan menggunakan konstruktor default dan pemukim properti. C # 3.0 memiliki sintaks yang bagus untuk melakukan ini secara otomatis.

return new Shniz { Foo = foo,
                   Bar = bar,
                   Baz = baz,
                   Quuz = quux,
                   Fred = fred,
                   Wilma = wilma,
                   Barney = barney,
                   Dino = dino,
                   Donkey = donkey
                 };

Peningkatan kode datang dengan menyederhanakan konstruktor dan tidak harus mendukung banyak metode untuk mendukung berbagai kombinasi. Sintaks "pemanggilan" masih sedikit "bertele-tele", tetapi tidak lebih buruk daripada memanggil pemukim properti secara manual.

tvanfosson.dll
sumber
2
Ini akan memungkinkan objek baru Shniz () ada. Implementasi OO yang baik akan berusaha meminimalkan kemungkinan objek yang ada dalam keadaan tidak lengkap.
AnthonyWJones
Secara umum, bahasa apa pun dengan sintaks hash / kamus asli dilengkapi dengan pengganti yang memadai untuk parameter bernama (yang hebat dan sering kali dibutuhkan oleh situasi ini, tetapi untuk beberapa alasan satu-satunya bahasa populer yang mendukungnya adalah yang terburuk di planet ini) .
kekacauan
4

Anda belum memberikan informasi yang cukup untuk menjamin jawaban yang bagus. Daftar parameter yang panjang tidak selalu buruk.

Shniz (foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

dapat diartikan sebagai:

void Shniz(int foo, int bar, int baz, int quux, int fred, 
           int wilma, int barney, int dino, int donkey) { ...

Dalam hal ini Anda lebih baik membuat kelas untuk merangkum parameter karena Anda memberi arti pada parameter yang berbeda dengan cara yang dapat diperiksa oleh kompilator serta membuat kode lebih mudah dibaca secara visual. Ini juga membuatnya lebih mudah untuk membaca dan memfaktor ulang nanti.

// old way
Shniz(1,2,3,2,3,2,1,2);
Shniz(1,2,2,3,3,2,1,2); 

//versus
ShnizParam p = new ShnizParam { Foo = 1, Bar = 2, Baz = 3 };
Shniz(p);

Atau jika Anda memiliki:

void Shniz(Foo foo, Bar bar, Baz baz, Quux quux, Fred fred, 
           Wilma wilma, Barney barney, Dino dino, Donkey donkey) { ...

Ini adalah kasus yang jauh berbeda karena semua objek berbeda (dan tidak mungkin tercampur aduk). Setuju bahwa jika semua objek diperlukan, dan semuanya berbeda, tidak masuk akal untuk membuat kelas parameter.

Selain itu, apakah beberapa parameter bersifat opsional? Apakah ada penimpaan metode (nama metode yang sama, tetapi tanda tangan metode yang berbeda?) Detail semacam ini penting bagi jawaban terbaiknya .

* Tas properti juga bisa berguna, tetapi tidak secara khusus lebih baik karena tidak ada latar belakang yang diberikan.

Seperti yang Anda lihat, ada lebih dari 1 jawaban yang benar untuk pertanyaan ini. Ambil pilihanmu.

Robert Paulson
sumber
3

Anda dapat mencoba mengelompokkan parameter Anda menjadi beberapa struct / kelas yang berarti (jika memungkinkan).

Julien Hoarau
sumber
2

Saya biasanya akan bersandar pada pendekatan struct - mungkin sebagian besar parameter ini terkait dalam beberapa cara dan mewakili keadaan beberapa elemen yang relevan dengan metode Anda.

Jika kumpulan parameter tidak dapat dibuat menjadi objek yang bermakna, itu mungkin merupakan tanda bahwa Shnizmelakukan terlalu banyak, dan pemfaktoran ulang harus melibatkan pemecahan metode menjadi masalah yang terpisah.

Andrzej Doyle
sumber
2

Anda dapat memperdagangkan kompleksitas untuk baris kode sumber. Jika metodenya sendiri melakukan terlalu banyak (pisau Swiss) coba bagi separuh tugasnya dengan membuat metode lain. Jika metodenya sederhana hanya membutuhkan terlalu banyak parameter maka yang disebut objek parameter adalah cara yang harus dilakukan.

Karl
sumber
2

Jika bahasa Anda mendukungnya, gunakan parameter bernama dan buat opsional sebanyak mungkin (dengan default yang masuk akal).

pengguna54650
sumber
1

Saya pikir metode yang Anda jelaskan adalah cara untuk pergi. Ketika saya menemukan metode dengan banyak parameter dan / atau yang mungkin membutuhkan lebih banyak di masa depan, saya biasanya membuat objek ShnizParams untuk dilewati, seperti yang Anda gambarkan.

Instantsoup
sumber
1

Bagaimana kalau tidak menyetelnya sekaligus di konstruktor tetapi melakukannya melalui properti / penyetel ? Saya telah melihat beberapa kelas .NET yang memanfaatkan pendekatan ini seperti Processkelas:

        Process p = new Process();

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.FileName = "cmd";
        p.StartInfo.Arguments = "/c dir";
        p.Start();
Gant
sumber
3
C # 3 sebenarnya memiliki sintaks untuk melakukan ini dengan mudah: penginisialisasi objek.
Daren Thomas
1

Saya setuju dengan pendekatan memindahkan parameter ke objek parameter (struct). Daripada hanya menempelkan semuanya dalam satu objek, tinjau jika fungsi lain menggunakan grup parameter yang serupa. Objek paramater lebih berharga jika digunakan dengan beberapa fungsi yang Anda harapkan kumpulan parameter berubah secara konsisten di seluruh fungsi tersebut. Bisa jadi Anda hanya memasukkan beberapa parameter ke dalam objek parameter baru.

Frank Schwieterman
sumber
1

Jika Anda memiliki banyak parameter, kemungkinan besar metode tersebut melakukan terlalu banyak, jadi atasi ini terlebih dahulu dengan membagi metode menjadi beberapa metode yang lebih kecil. Jika Anda masih memiliki terlalu banyak parameter setelah ini coba kelompokkan argumen atau ubah beberapa parameter menjadi anggota instance.

Lebih suka kelas / metode kecil daripada besar. Ingatlah prinsip tanggung jawab tunggal.

Brian Rasmussen
sumber
Masalah dengan anggota dan properti instance adalah bahwa mereka 1) harus dapat ditulis, 2) mungkin tidak disetel. Dalam kasus konstruktor, ada bidang tertentu yang ingin saya pastikan diisi sebelum sebuah instance diizinkan ada.
rekursif
@ Rekursif - Saya tidak setuju bahwa bidang / properti selalu harus dapat ditulis. Untuk kelas kecil, ada banyak waktu di mana anggota hanya-baca masuk akal.
Brian Rasmussen
1

Argumen yang diberi nama adalah opsi yang baik (dengan asumsi bahasa yang mendukungnya) untuk menghilangkan ambiguitas daftar parameter yang panjang (atau bahkan pendek!) Sambil juga mengizinkan (dalam kasus konstruktor) properti kelas menjadi tidak dapat diubah tanpa memaksakan persyaratan untuk mengizinkannya ada dalam keadaan sebagian dibangun.

Opsi lain yang akan saya cari dalam melakukan refactor semacam ini adalah grup parameter terkait yang mungkin lebih baik ditangani sebagai objek independen. Menggunakan kelas Persegi Panjang dari jawaban sebelumnya sebagai contoh, konstruktor yang mengambil parameter untuk x, y, tinggi, dan lebar dapat memfaktorkan x dan y menjadi objek Titik, memungkinkan Anda untuk meneruskan tiga parameter ke konstruktor Persegi Panjang. Atau melangkah lebih jauh dan menjadikannya dua parameter (UpperLeftPoint, LowerRightPoint), tetapi itu akan menjadi pemfaktoran ulang yang lebih radikal.

Dave Sherohman
sumber
0

Itu tergantung pada jenis argumen yang Anda miliki, tetapi jika banyak nilai / opsi boolean mungkin Anda bisa menggunakan Flag Enum?

scottm
sumber
0

Saya pikir masalah itu sangat terkait dengan domain masalah yang Anda coba selesaikan dengan kelas.

Dalam beberapa kasus, konstruktor 7-parameter dapat menunjukkan hierarki kelas yang buruk: dalam kasus tersebut, struct / class helper yang disarankan di atas biasanya merupakan pendekatan yang baik, tetapi Anda juga cenderung berakhir dengan banyak struct yang hanya merupakan tas properti dan tidak melakukan sesuatu yang berguna. Konstruktor 8-argumen mungkin juga menunjukkan bahwa kelas Anda terlalu umum / terlalu serbaguna sehingga membutuhkan banyak opsi agar benar-benar berguna. Dalam hal ini, Anda dapat memfaktor ulang kelas atau mengimplementasikan konstruktor statis yang menyembunyikan konstruktor kompleks yang sebenarnya: mis. Shniz.NewBaz (foo, bar) sebenarnya dapat memanggil konstruktor sebenarnya dengan memberikan parameter yang tepat.

axel_c
sumber
0

Salah satu pertimbangannya adalah nilai mana yang akan menjadi hanya-baca setelah objek dibuat?

Properti yang dapat ditulis secara publik mungkin dapat diberikan setelah konstruksi.

Dari mana asalnya nilai-nilai itu? Mungkin beberapa nilai benar-benar eksternal sedangkan yang lain benar-benar dari beberapa konfigurasi atau data global yang dikelola oleh pustaka.

Dalam hal ini Anda dapat menyembunyikan konstruktor dari penggunaan eksternal dan menyediakan fungsi Buat untuknya. Fungsi create mengambil nilai eksternal yang sebenarnya dan membuat objek, lalu menggunakan pengakses yang hanya tersedia di perpustakaan untuk menyelesaikan pembuatan objek.

Akan sangat aneh untuk memiliki objek yang membutuhkan 7 atau lebih parameter untuk memberikan objek keadaan lengkap dan semuanya benar-benar bersifat eksternal.

AnthonyWJones
sumber
0

Ketika sebuah klas memiliki konstruktor yang mengambil terlalu banyak argumen, biasanya itu adalah tanda bahwa ia memiliki terlalu banyak tanggung jawab. Ini mungkin dapat dipecah menjadi kelas-kelas terpisah yang bekerja sama untuk memberikan fungsi yang sama.

Jika Anda benar-benar membutuhkan banyak argumen ke konstruktor, pola Builder dapat membantu Anda. Tujuannya adalah untuk tetap meneruskan semua argumen ke konstruktor, sehingga statusnya diinisialisasi dari awal dan Anda masih dapat membuat kelas tidak dapat diubah jika diperlukan.

Lihat di bawah :

public class Toto {
    private final String state0;
    private final String state1;
    private final String state2;
    private final String state3;

    public Toto(String arg0, String arg1, String arg2, String arg3) {
        this.state0 = arg0;
        this.state1 = arg1;
        this.state2 = arg2;
        this.state3 = arg3;
    }

    public static class TotoBuilder {
        private String arg0;
        private String arg1;
        private String arg2;
        private String arg3;

        public TotoBuilder addArg0(String arg) {
            this.arg0 = arg;
            return this;
        }
        public TotoBuilder addArg1(String arg) {
            this.arg1 = arg;
            return this;
        }
        public TotoBuilder addArg2(String arg) {
            this.arg2 = arg;
            return this;
        }
        public TotoBuilder addArg3(String arg) {
            this.arg3 = arg;
            return this;
        }

        public Toto newInstance() {
            // maybe add some validation ...
            return new Toto(this.arg0, this.arg1, this.arg2, this.arg3);
        }
    }

    public static void main(String[] args) {
        Toto toto = new TotoBuilder()
            .addArg0("0")
            .addArg1("1")
            .addArg2("2")
            .addArg3("3")
            .newInstance();
    }

}
Guillaume
sumber