Apakah memodifikasi parameter yang masuk merupakan antipattern? [Tutup]

60

Saya pemrograman di Java, dan saya selalu membuat konverter seperti ini:

public OtherObject MyObject2OtherObject(MyObject mo){
    ... Do the conversion
    return otherObject;
}

Di tempat kerja baru polanya adalah:

public void MyObject2OtherObject(MyObject mo, OtherObject oo){
    ... Do the conversion
}

Bagi saya itu sedikit bau, karena saya terbiasa tidak mengubah parameter yang masuk. Apakah perubahan parameter yang masuk ini merupakan antipattern atau apakah itu OK? Apakah ada beberapa kelemahan serius?

CsBalazsHungary
sumber
4
Jawaban yang tepat untuk ini akan menjadi spesifik bahasa, karena mereka yang parameter pass-by-value secara efektif mengubahnya menjadi variabel lokal.
Blrfl
1
Pola kedua kadang-kadang digunakan sebagai ukuran efisiensi untuk menggunakan kembali objek. Anggap itu ooadalah objek yang diteruskan ke metode, bukan penunjuk yang diatur ke objek baru. Apakah itu yang terjadi di sini? Jika ini adalah Jawa mungkin, jika C ++ mungkin tidak
Richard Tingle
3
Ini seperti optimasi prematur, ya. Saya sarankan Anda menggunakan formulir pertama secara umum, tetapi jika Anda mengalami hambatan kinerja, Anda bisa menggunakan formulir kedua dengan komentar untuk membenarkannya . Sebuah komentar akan mencegah Anda dan orang lain melihat kode itu dan membuat pertanyaan yang sama muncul kembali.
Tertarik
2
Pola kedua berguna ketika fungsi mengembalikan nilai juga, misalnya, sukses / gagal. Tapi tidak ada yang 'salah' dengan itu. Itu benar-benar tergantung di mana Anda ingin membuat objek.
GrandmasterB
12
Saya tidak akan menyebutkan fungsi yang mengimplementasikan dua pola berbeda ini dengan cara yang sama.
Casey

Jawaban:

70

Ini bukan antipengganti, ini praktik yang buruk.

Perbedaan antara antipattern dan hanya praktik buruk ada di sini: definisi anti-pola .

Gaya tempat kerja baru yang Anda perlihatkan adalah praktik yang buruk , peninggalan zaman atau pra-OOP, menurut Kode Bersih Paman Bob.

Argumen paling diartikan sebagai input ke suatu fungsi.

Apa pun yang memaksa Anda untuk memeriksa tanda tangan fungsi sama dengan pengambilan ganda. Ini adalah istirahat kognitif dan harus dihindari. Pada hari-hari sebelum pemrograman berorientasi objek kadang-kadang perlu untuk memiliki argumen keluaran. Namun, sebagian besar kebutuhan akan argumen keluaran menghilang dalam bahasa OO

Tulains Córdova
sumber
7
Saya tidak melihat mengapa definisi yang Anda tautelakkan untuk menghalangi ini dianggap sebagai anti-pola.
Samuel Edwin Ward
7
Saya kira itu karena alasan bahwa "kita menyimpan CPU / mem dengan tidak membuat objek baru, sebaliknya kita harus mendaur ulang yang kita miliki dan memberikannya sebagai argumen" jelas sangat salah untuk semua orang dengan latar belakang OOP yang serius bahwa ini dapat bukan merupakan pola yang pernah - jadi tidak mungkin kita bisa menganggapnya sebagai anti-pola dengan definisi ...
vaxquis
1
Bagaimana dengan kasus ketika parameter input adalah koleksi besar objek yang perlu dimodifikasi?
Steve Chambers
Meskipun saya juga lebih suka opsi 1, bagaimana jika OtherObjectantarmuka? Hanya penelepon (mudah-mudahan) yang tahu jenis konkret yang diinginkannya.
user949300
@SteveChambers Saya pikir, dalam hal ini, desain kelas atau metode buruk dan harus di refactored untuk menghindarinya.
piotr.wittchen
16

Mengutip buku terkenal Robert C. Martin "Clean Code":

Argumen keluaran harus dihindari

Fungsinya harus memiliki sejumlah kecil argumen

Pola kedua melanggar kedua aturan, terutama "argumen keluaran". Dalam hal ini lebih buruk daripada pola pertama.

claasz
sumber
1
Saya akan mengatakan itu melanggar yang pertama, banyak argumen berarti kode berantakan, tapi saya pikir dua argumen sama sekali ok. Saya pikir aturan pada kode bersih berarti jika Anda memerlukan 5 atau 6 data yang masuk, Anda ingin melakukan terlalu banyak dalam satu metode, jadi Anda harus refactor untuk meningkatkan keterbacaan kode dan ruang lingkup OOP.
CsBalazsHungary
2
Lagi pula saya curiga ini bukan hukum yang ketat, tapi saran yang kuat. Saya tahu ini tidak akan bekerja dengan tipe primitif, jika itu adalah pola yang tersebar luas dalam sebuah proyek, seorang junior akan mendapatkan ide untuk lulus int dan berharap itu akan berubah seperti Objects.
CsBalazsHungary
27
Terlepas dari seberapa benar Kode Bersih mungkin, saya tidak berpikir ini adalah jawaban yang berharga tanpa menjelaskan mengapa ini harus dihindari. Itu bukan perintah yang turun pada loh batu, pemahaman itu penting. Jawaban ini dapat ditingkatkan dengan memberikan ringkasan alasan dalam buku dan referensi bab
Daenyth
3
Ya ampun, jika seseorang menulisnya dalam sebuah buku, itu pasti saran yang bagus untuk situasi yang mungkin terjadi. Tidak perlu dipikirkan.
Casey
2
@emodendroket Tapi dude, itu yang pria!
Pierre Arlaud
15

Idiom kedua bisa lebih cepat, karena pemanggil dapat menggunakan kembali satu variabel melalui loop panjang, alih-alih setiap iterasi membuat instance baru.

Saya biasanya tidak akan menggunakannya, tetapi misalnya. dalam pemrograman game itu ada tempatnya. Sebagai contoh, lihat banyak operasi JavaMonkey's Vector3f memungkinkan untuk lulus contoh yang harus diubah dan dikembalikan sebagai hasilnya.

pengguna470365
sumber
12
baik, saya bisa setuju jika ini adalah bottleneck, tetapi di mana pun saya bekerja, bottleneck biasanya adalah algoritma dengan efisiensi rendah, tidak mengkloning atau membuat objek. Saya kurang tahu tentang pengembangan game.
CsBalazsHungary
4
@CsBalazsHungary Saya percaya masalah saat pembuatan objek menjadi perhatian cenderung terkait dengan alokasi memori dan pengumpulan sampah, yang mungkin akan menjadi level kemacetan berikutnya setelah kompleksitas algoritmik (terutama dalam lingkungan memori terbatas, misalnya smartphone dan sejenisnya) .
JAB
JMonkey juga tempat saya sering melihat ini karena sering kali kinerja kritis. Namun saya belum pernah mendengar namanya "JavaMonkey"
Richard Tingle
3
@ JAB: JVM's GC disetel secara khusus untuk kasus objek fana. Benda-benda berumur pendek dalam jumlah besar sangat mudah untuk dikumpulkan, dan dalam banyak kasus seluruh generasi singkat Anda dapat dikumpulkan dengan satu gerakan penunjuk tunggal.
Phoshi
2
sebenarnya, jika seseorang harus membuat objek , itu tidak akan efektif kinerja; jika suatu blok kode harus sangat dioptimalkan untuk kecepatan, Anda harus menggunakan primitif dan array asli sebagai gantinya; karena itu, saya menganggap bahwa pernyataan dalam jawaban ini tidak berlaku.
vaxquis
10

Saya tidak berpikir itu adalah dua bagian kode yang setara. Dalam kasus pertama, Anda harus membuat otherObject. Anda dapat mengubah instance yang ada di yang kedua. Keduanya memiliki kegunaannya. Kode bau akan lebih suka satu sama lain.

Euforia
sumber
Saya tahu mereka tidak setara. Anda benar, kami terus melakukan hal-hal ini, jadi itu akan membuat perbedaan. Jadi Anda mengatakan tidak ada dari mereka yang anti-internet, itu hanya soal use case?
CsBalazsHungary
@CsBalazsHungary Saya akan mengatakan ya. Menilai dari potongan kecil kode yang Anda berikan.
Euforia
Saya kira itu menjadi masalah yang lebih serius jika Anda memiliki parameter primitif yang masuk, yang tentu saja tidak akan diperbarui, jadi seperti @claasz menulis: itu harus dihindari kecuali diperlukan.
CsBalazsHungary
1
@CsBalazsHungary Dalam kasus argumen primitif, fungsi tersebut bahkan tidak akan berfungsi. Jadi, Anda memiliki masalah yang lebih buruk daripada mengubah argumen.
Euforia
Saya akan mengatakan seorang junior akan jatuh ke dalam perangkap mencoba membuat tipe primitif sebagai parameter output. Jadi saya akan mengatakan konverter pertama harus lebih disukai jika memungkinkan.
CsBalazsHungary
7

Itu benar-benar tergantung pada bahasa.

Di Jawa bentuk kedua mungkin anti-pola, tetapi beberapa bahasa memperlakukan parameter lewat secara berbeda. Di Ada dan VHDL misalnya, bukannya lulus dengan nilai atau dengan referensi, parameter dapat memiliki mode in, outatau in out.

Ini adalah kesalahan untuk mengubah suatu inparameter atau membaca suatu outparameter, tetapi setiap perubahan pada suatu in outparameter diteruskan kembali ke pemanggil.

Jadi dua bentuk di Ada (ini juga VHDL legal)

function MyObject2OtherObject(mo : in MyObject) return OtherObject is
begin
    ... Do the conversion
    return otherObject;
end MyObject2OtherObject;

dan

procedure MyObject2OtherObject(mo : in MyObject; oo : out OtherObject) is
begin
    ... Do the conversion
    oo := ... the conversion result;
end MyObject2OtherObject;

Keduanya memiliki kegunaannya; suatu prosedur dapat mengembalikan beberapa nilai dalam banyak Outparameter sementara suatu fungsi hanya dapat mengembalikan satu hasil. Karena tujuan dari parameter kedua secara jelas dinyatakan dalam prosedur deklarasi tidak ada keberatan untuk formulir ini. Saya cenderung lebih suka fungsi agar mudah dibaca. tetapi akan ada kasus di mana prosedur lebih baik, misalnya di mana pemanggil telah membuat objek.

Brian Drummond
sumber
3

Memang terlihat bau, dan tanpa melihat lebih banyak konteks tidak mungkin untuk mengatakan dengan pasti. Mungkin ada dua alasan untuk melakukan ini, meskipun ada alternatif untuk keduanya.

Pertama, ini adalah cara ringkas untuk mengimplementasikan konversi sebagian, atau membiarkan hasilnya dengan nilai default jika konversi gagal. Yaitu, Anda mungkin memiliki ini:

public void ConvertFoo(Foo from, Foo to) {
    if (can't convert) {
        return;
    }
    ...
}

Foo a;
Foo b = DefaultFoo();
ConvertFoo(a, b);
// If conversion fails, b is unchanged

Tentu saja, biasanya ini ditangani dengan menggunakan pengecualian. Namun, bahkan jika pengecualian perlu dihindari karena alasan apa pun, ada cara yang lebih baik untuk melakukan ini - pola TryParse menjadi satu opsi.

Alasan lain adalah bahwa itu bisa karena alasan konsistensi murni, misalnya itu adalah bagian dari API publik di mana metode ini digunakan untuk semua fungsi konversi untuk alasan apa pun (seperti fungsi konversi lainnya yang memiliki banyak keluaran).

Java tidak hebat dalam menangani beberapa keluaran - ia tidak bisa memiliki parameter keluaran saja seperti beberapa bahasa, atau memiliki beberapa nilai pengembalian seperti yang lain - tetapi meskipun demikian, Anda masih bisa menggunakan objek pengembalian.

Alasan konsistensi agak timpang tetapi sayangnya itu mungkin yang paling umum.

  • Mungkin gaya polisi di tempat kerja Anda (atau basis kode Anda) berasal dari latar belakang non-Jawa dan enggan berubah.
  • Kode Anda mungkin merupakan port dari bahasa tempat gaya ini lebih idiomatis.
  • Organisasi Anda mungkin perlu mempertahankan konsistensi API di berbagai bahasa, dan ini adalah gaya penyebut yang paling umum (itu gila tapi itu terjadi bahkan untuk Google ).
  • Atau mungkin gaya lebih masuk akal di masa lalu yang jauh dan berubah menjadi bentuk saat ini (misalnya, itu bisa menjadi pola TryParse tetapi beberapa pendahulu yang bermaksud baik menghapus nilai kembali setelah menemukan bahwa tidak ada yang memeriksanya sama sekali).
congusbongus
sumber
2

Keuntungan dari pola kedua adalah memaksa penelepon untuk mengambil kepemilikan dan tanggung jawab untuk objek yang dibuat. Tidak ada pertanyaan apakah metode tersebut membuat objek atau mendapatkannya dari kumpulan yang dapat digunakan kembali. Penelepon tahu bahwa mereka bertanggung jawab atas masa pakai dan pembuangan objek baru.

Kerugian dari metode ini adalah:

  1. Tidak dapat digunakan sebagai metode pabrik, penelepon harus mengetahui subtipe yang tepat dari OtherObjectkebutuhan dan membangunnya terlebih dahulu.
  2. Tidak dapat digunakan dengan objek yang memerlukan parameter dalam konstruktornya jika parameter tersebut berasal MyObject. OtherObjectharus dapat dibangun tanpa mengetahui MyObject.

Jawabannya didasarkan pada pengalaman saya di c #, saya harap logikanya diterjemahkan ke Jawa.

Rotem
sumber
Saya pikir dengan tanggung jawab yang Anda sebutkan, itu juga menciptakan siklus hidup objek yang "lebih sulit dilihat". Dalam sistem metode yang kompleks saya lebih suka modifikasi langsung dari suatu objek daripada mulai melihat melalui semua metode yang kita lewati.
CsBalazsHungary
Inilah yang saya pikirkan. Jenis primitif dari Dependency Injection
Lyndon White
Keuntungan lain adalah jika OtherObject abstrak atau antarmuka. Fungsi tidak tahu jenis yang harus dibuat tetapi pemanggil mungkin.
user949300
2

Dengan semantik yang longgar - myObjectToOtherObjectbisa juga berarti Anda memindahkan beberapa data dari objek pertama ke objek kedua atau Anda mengonversinya sepenuhnya baru, metode kedua tampaknya lebih memadai.

Namun, jika nama metode itu Convert(yang seharusnya jika kita melihat bagian "... lakukan konversi"), saya akan mengatakan metode kedua tidak masuk akal. Anda tidak mengonversi nilai menjadi nilai lain yang sudah ada, IMO.

guillaume31
sumber