Apa Gaya Lebih Baik (Variabel Instans vs Nilai Pengembalian) di Jawa

32

Saya sering menemukan diri saya berjuang untuk memutuskan mana dari dua cara ini untuk digunakan ketika saya harus menggunakan data umum di beberapa metode di kelas saya. Apa yang akan menjadi pilihan yang lebih baik?

Dalam opsi ini, saya dapat membuat variabel instan untuk menghindari keharusan mendeklarasikan variabel tambahan, dan juga untuk menghindari mendefinisikan parameter metode, tetapi mungkin tidak begitu jelas di mana variabel-variabel tersebut sedang dipakai / dimodifikasi:

public class MyClass {
    private int var1;

    MyClass(){
        doSomething();
        doSomethingElse();
        doMoreStuff();
    }

    private void doSomething(){
        var1 = 2;
    }

    private void doSomethingElse(){
        int var2 = var1 + 1;
    }

    private void doMoreStuff(){
        int var3 = var1 - 1;
    }
}

Atau hanya instantiate variabel lokal dan meneruskannya sebagai argumen?

public class MyClass {  
    MyClass(){
        int var1 = doSomething();
        doSomethingElse(var1);
        doMoreStuff(var1);
    }

    private int doSomething(){
        int var = 2;
        return var;
    }

    private void doSomethingElse(int var){
        int var2 = var + 1;
    }

    private void doMoreStuff(int var){
        int var3 = var - 1;
    }
}

Jika jawabannya adalah keduanya benar, yang mana yang lebih sering dilihat / digunakan? Juga, jika Anda dapat memberikan pro / kontra tambahan untuk setiap opsi akan sangat berharga.

carlossierra
sumber
1
Reletad (duplikat?): Programmers.stackexchange.com/questions/164347/…
Martin Ba
1
Saya tidak berpikir ada orang yang menunjukkan bahwa menempatkan hasil antara dalam variabel contoh dapat membuat konkurensi lebih sulit, karena kemungkinan pertikaian antara thread untuk variabel-variabel ini.
sdenham

Jawaban:

107

Saya terkejut ini belum disebutkan ...

Itu tergantung apakah var1sebenarnya bagian dari keadaan objek Anda .

Anda berasumsi bahwa kedua pendekatan ini benar dan hanya masalah gaya. Anda salah.

Ini sepenuhnya tentang bagaimana model yang tepat.

Demikian pula, privatemetode instan ada untuk mengubah keadaan objek Anda . Jika bukan itu yang dilakukan metode Anda, maka itu seharusnya private static.

MetaFight
sumber
7
@mucaho: transienttidak ada hubungannya dengan ini, karena transientini tentang keadaan persisten , seperti di bagian-bagian dari objek yang diselamatkan ketika Anda melakukan sesuatu seperti membuat serial objek. Misalnya, toko dukungan ArrayList adalah transientmeskipun itu sangat penting untuk keadaan ArrayList, karena ketika Anda membuat serial ArrayList, Anda hanya ingin menyimpan bagian dari toko dukungan yang menyimpan elemen ArrayList yang sebenarnya, bukan ruang kosong di akhir disediakan untuk penambahan elemen lebih lanjut.
user2357112 mendukung Monica
4
Menambah jawaban - jika var1diperlukan untuk beberapa metode tetapi bukan bagian dari keadaan MyClass, mungkin sudah waktunya untuk memasukkan var1dan metode tersebut ke kelas lain untuk digunakan MyClass.
Mike Partridge
5
@CandiedOrange Kita berbicara tentang pemodelan yang benar di sini. Ketika kita mengatakan "bagian dari objek objek," kita tidak berbicara tentang potongan literal dalam kode. Kita sedang membahas gagasan konseptual tentang keadaan, apa yang seharusnya menjadi keadaan objek untuk memodelkan konsep dengan benar. "Masa manfaat" kemungkinan merupakan faktor penentu terbesar dalam hal itu.
jpmc26
4
@CandiedOrange Next dan Previous jelas merupakan metode publik. Jika nilai var1 hanya relevan di seluruh urutan metode pribadi itu jelas bukan bagian dari keadaan objek yang persisten dan karenanya harus diteruskan sebagai argumen.
Taemyr
2
@CandiedOrange Anda mengklarifikasi maksud Anda, setelah dua komentar. Saya mengatakan bahwa Anda dapat dengan mudah memiliki jawaban pertama. Juga, ya, saya lupa bahwa ada lebih dari sekedar objek; Saya setuju bahwa terkadang metode instance pribadi memiliki tujuan. Aku hanya tidak bisa mengingatnya. EDIT: Saya baru sadar Anda bukan orang pertama yang membalas saya. Salahku. Saya bingung mengapa satu jawaban itu singkat, satu kalimat, tanpa jawaban, sedangkan yang berikutnya benar-benar menjelaskan banyak hal.
Dana Gugatan Monica
17

Saya tidak tahu mana yang lebih umum, tetapi saya akan selalu melakukan yang terakhir. Ini lebih jelas mengkomunikasikan aliran data dan masa pakai, dan itu tidak mengasapi setiap instance kelas Anda dengan bidang yang hanya relevan seumur hidup selama inisialisasi. Saya akan mengatakan yang pertama hanya membingungkan, dan membuat tinjauan kode secara signifikan lebih sulit, karena saya harus mempertimbangkan kemungkinan bahwa metode apa pun dapat memodifikasi var1.

Derek Elkins
sumber
7

Anda harus mengurangi cakupan variabel Anda sebanyak mungkin (dan masuk akal). Tidak hanya dalam metode, tetapi secara umum.

Untuk pertanyaan Anda, artinya tergantung apakah variabel tersebut merupakan bagian dari status objek. Jika ya, tidak apa-apa untuk menggunakannya dalam lingkup itu, yaitu seluruh objek. Dalam hal ini, pergi dengan opsi pertama. Jika tidak, gunakan opsi kedua karena mengurangi visibilitas variabel dan kompleksitas keseluruhan.

Andy
sumber
5

Apa Gaya Lebih Baik (Variabel Instans vs Nilai Pengembalian) di Jawa

Ada gaya lain - gunakan konteks / negara.

public static class MyClass {
    // Hold my state from one call to the next.
    public static final class State {
        int var1;
    }

    MyClass() {
        State state = new State();
        doSomething(state);
        doSomethingElse(state);
        doMoreStuff(state);
    }

    private void doSomething(State state) {
        state.var1 = 2;
    }

    private void doSomethingElse(State state) {
        int var2 = state.var1 + 1;
    }

    private void doMoreStuff(State state) {
        int var3 = state.var1 - 1;
    }
}

Ada sejumlah manfaat dari pendekatan ini. Objek negara dapat berubah secara independen dari objek misalnya, memberikan banyak ruang gerak untuk masa depan.

Ini adalah pola yang juga berfungsi dengan baik dalam sistem server terdistribusi di mana beberapa detail harus dipertahankan di seluruh panggilan. Anda dapat menyimpan detail pengguna, koneksi basis data, dll di stateobjek.

OldCurmudgeon
sumber
3

Ini tentang efek samping.

Bertanya apakah var1bagian dari negara meleset dari pertanyaan ini. Tentu jika var1harus bertahan, itu harus menjadi contoh. Salah satu pendekatan dapat dilakukan untuk bekerja apakah diperlukan kegigihan atau tidak.

Pendekatan efek samping

Beberapa variabel instan hanya digunakan untuk berkomunikasi antara metode pribadi dari panggilan ke panggilan. Variabel instance semacam ini dapat di-refactored dari keberadaannya tetapi tidak harus demikian. Terkadang hal-hal menjadi lebih jelas. Tapi ini bukan tanpa risiko.

Anda membiarkan variabel keluar dari cakupannya karena digunakan dalam dua cakupan pribadi yang berbeda. Bukan karena itu diperlukan dalam ruang lingkup Anda menempatkannya. Ini bisa membingungkan. "Global itu jahat!" tingkat membingungkan. Ini bisa berhasil tetapi tidak akan skala dengan baik. Ini hanya berfungsi di yang kecil. Tidak ada benda besar. Tidak ada rantai pewarisan panjang. Jangan menyebabkan efek yo yo .

Pendekatan fungsional

Sekarang, bahkan jika var1harus bertahan tidak ada yang mengatakan Anda harus menggunakan jika untuk setiap nilai sementara mungkin diperlukan sebelum mencapai kondisi yang Anda inginkan dipertahankan antara panggilan publik. Itu berarti Anda masih dapat menetapkan var1contoh menggunakan metode yang lebih fungsional.

Jadi bagian dari keadaan atau tidak, Anda masih bisa menggunakan pendekatan mana pun.

Dalam contoh-contoh ini, 'var1' tidak mengandung apa-apa selain debugger Anda tahu itu ada. Saya kira Anda melakukannya dengan sengaja karena Anda tidak ingin membuat kami bias. Untungnya saya tidak peduli yang mana.

Risiko efek samping

Yang mengatakan, saya tahu dari mana pertanyaan Anda berasal. Saya telah bekerja di bawah warisan yo yo ing menyedihkan yang mengubah variabel contoh di berbagai tingkatan dalam berbagai metode dan pergi dengan tupai mencoba mengikutinya. Inilah risikonya.

Ini adalah rasa sakit yang mendorong saya ke pendekatan yang lebih fungsional. Suatu metode dapat mendokumentasikan dependensi dan output dalam tanda tangannya. Ini adalah pendekatan yang kuat dan jelas. Ini juga memungkinkan Anda mengubah apa yang Anda lewati metode pribadi sehingga lebih dapat digunakan kembali di dalam kelas.

Kelebihan efek samping

Ini juga membatasi. Fungsi murni tidak memiliki efek samping. Itu bisa menjadi hal yang baik tetapi tidak berorientasi objek. Sebagian besar orientasi objek adalah kemampuan untuk merujuk pada konteks di luar metode. Melakukan itu tanpa membocorkan global di sini dan pergi adalah kekuatan OOP. Saya mendapatkan fleksibilitas global tetapi terkandung dengan baik di kelas. Saya dapat memanggil satu metode dan memutasikan setiap variabel instan sekaligus jika saya mau. Jika saya melakukan itu, saya berkewajiban untuk setidaknya memberikan nama metode yang membuat jelas apa yang terserah sehingga orang tidak akan terkejut ketika itu terjadi. Komentar dapat membantu juga. Terkadang komentar ini diformalkan sebagai "kondisi posting".

Kelemahan dari metode pribadi yang fungsional

Pendekatan fungsional membuat beberapa dependensi menjadi jelas. Kecuali jika Anda menggunakan bahasa fungsional murni, itu tidak dapat mengesampingkan dependensi tersembunyi. Anda tidak tahu, hanya melihat tanda tangan metode, bahwa itu tidak menyembunyikan efek samping dari Anda di sisa kode itu. Anda tidak melakukannya.

Posting kondisional

Jika Anda, dan semua orang di tim, andal mendokumentasikan efek samping (kondisi sebelum / pasca) dalam komentar, maka keuntungan dari pendekatan fungsional jauh lebih sedikit. Ya saya tahu, teruslah bermimpi.

Kesimpulan

Secara pribadi saya cenderung ke arah metode privat fungsional dalam kedua kasus jika saya bisa, tapi jujur ​​itu sebagian besar karena komentar efek sampingan pra / pasca bersyarat tidak menyebabkan kesalahan kompiler ketika mereka sudah ketinggalan zaman atau ketika metode yang disebut rusak. Kecuali saya benar-benar membutuhkan kelenturan efek samping, saya lebih baik mengetahui bahwa semuanya berjalan baik.

MagicWindow
sumber
1
"Beberapa variabel instan hanya digunakan untuk berkomunikasi antara metode pribadi dari panggilan ke panggilan." Itu bau kode. Ketika variabel instan digunakan dengan cara ini, itu adalah tanda kelasnya terlalu besar. Ekstrak variabel dan metode itu ke kelas baru.
kevin cline
2
Ini benar-benar tidak masuk akal. Sementara OP dapat menulis kode dalam paradigma fungsional (dan ini umumnya akan menjadi hal yang baik), itu jelas bukan konteks pertanyaan. Mencoba memberi tahu OP bahwa mereka dapat menghindari penyimpanan keadaan objek dengan mengubah paradigma tidak benar-benar relevan ...
jpmc26
@kevincline contoh OP hanya memiliki satu variabel instan dan 3 metode. Intinya, sudah diekstraksi ke kelas baru. Bukan berarti contoh itu melakukan sesuatu yang bermanfaat. Ukuran kelas tidak ada hubungannya dengan bagaimana metode pembantu pribadi Anda berkomunikasi satu sama lain.
MagicWindow
@ jpmc26 OP telah menunjukkan bahwa mereka dapat menghindari penyimpanan var1sebagai variabel status dengan mengubah paradigma. Lingkup kelas BUKAN hanya di mana negara disimpan. Ini juga merupakan ruang lingkup penutup. Itu berarti ada dua kemungkinan motivasi untuk meletakkan variabel di tingkat kelas. Anda dapat mengklaim melakukan itu hanya untuk lingkup tertutup dan tidak menyatakan bahwa itu jahat, tetapi saya katakan itu adalah pertukaran dengan kesucian yang juga jahat. Saya tahu ini karena saya harus menjaga kode yang melakukan ini. Beberapa dilakukan dengan baik. Beberapa adalah mimpi buruk. Garis antara keduanya bukan negara. Ini keterbacaan.
MagicWindow
Aturan negara meningkatkan keterbacaan: 1) menghindari memiliki hasil sementara operasi seperti negara 2) menghindari menyembunyikan ketergantungan metode. Jika arity dari suatu metode adalah tinggi, maka itu dapat direduksi dan benar-benar mewakili kompleksitas metode itu atau desain saat ini memiliki kompleksitas yang tidak perlu.
sdenham
1

Varian pertama terlihat tidak intuitif dan berpotensi berbahaya bagi saya (bayangkan untuk alasan apa pun seseorang membuat metode pribadi Anda menjadi publik).

Saya lebih suka instantiate variabel Anda pada konstruksi kelas, atau meneruskannya sebagai argumen. Yang terakhir memberi Anda pilihan untuk menggunakan idiom fungsional dan tidak bergantung pada keadaan objek yang mengandung.

Brian Agnew
sumber
0

Sudah ada jawaban yang berbicara tentang keadaan objek dan ketika metode kedua lebih disukai. Saya hanya ingin menambahkan satu use case umum untuk pola pertama.

Pola pertama benar-benar valid ketika semua yang dilakukan kelas Anda adalah bahwa ia merangkum sebuah algoritma . Satu kasus penggunaan untuk ini adalah bahwa jika Anda menulis algoritma ke metode tunggal itu akan terlalu besar. Jadi Anda memecahnya menjadi metode yang lebih kecil menjadikannya kelas dan membuat sub metode pribadi.

Sekarang melewati semua keadaan algoritma melalui parameter mungkin membosankan sehingga Anda menggunakan bidang pribadi. Ini juga sesuai dengan aturan di paragraf pertama karena pada dasarnya adalah keadaan instance. Anda hanya perlu mengingat dan mendokumentasikan dengan baik bahwa algoritme tidak reentrant jika Anda menggunakan bidang pribadi untuk ini . Ini seharusnya tidak menjadi masalah sebagian besar waktu tetapi mungkin bisa menggigit Anda.

Honza Brabec
sumber
0

Mari kita coba contoh yang melakukan sesuatu. Maafkan saya karena ini javascript bukan java. Intinya harus sama.

Kunjungi https://blockly-games.appspot.com/pond-duck?lang=en , klik tab javascript, dan rekatkan ini:

hunt = lock(-90,1), 
heading = -135;

while(true) {
  hunt()  
  heading += 2
  swim(heading,30)
}

function lock(direction, width) {
  var dir = direction
  var wid = width
  var dis = 10000

  //hunt
  return function() {
    //randomize() //Calling this here makes the state of dir meaningless
    scanLock()
    adjustWid()
    if (isSpotted()) {
      if (inRange()) {
        if (wid <= 4) {
          cannon(dir, dis)
        }
      }
    } else {
      if (!left()) {
        right()
      }
    }
  }

  function scanLock() {
    dis = scan(dir, wid);
  }

  function adjustWid() {
    if (inRange()) {
      if (wid > 1)
        wid /= 2;
    } else {
      if (wid < 16) {
        wid *= 2; 
      }
    }
  }

  function isSpotted() {
    return dis < 1000;
  }

  function left() {
    dir += wid;
    scanLock();
    return isSpotted();
  }

  function right() {
    dir -= wid*2;
    scanLock();
    return isSpotted()
  }

  function inRange() {
    return dis < 70;
  }

  function randomize() {
    dir = Math.random() * 360
  }
}

Anda harus memperhatikan itu dir,, widdan distidak sering diedarkan. Anda juga harus memperhatikan bahwa kode dalam fungsi yang lock()mengembalikan sangat mirip dengan kode semu. Yah itu kode aktual. Namun sangat mudah dibaca. Kita dapat menambahkan passing dan penetapan tetapi itu akan menambah kekacauan yang tidak akan pernah Anda lihat dalam kode semu.

Jika Anda ingin berargumen bahwa tidak melakukan penetapan dan passing tidak apa-apa karena ketiga vars tersebut merupakan kondisi persisten, maka pertimbangkan desain ulang yang menetapkan dirnilai acak setiap loop. Tidak gigih sekarang, kan?

Tentu, sekarang kita bisa drop- dirdown dalam lingkup sekarang tetapi tidak tanpa dipaksa untuk mengacaukan kode pseudo like kita dengan passing dan setting.

Jadi tidak, nyatakan bukan mengapa Anda memutuskan untuk menggunakan atau tidak menggunakan efek samping daripada lewat dan kembali. Efek samping juga tidak berarti kode Anda tidak dapat dibaca. Anda tidak mendapatkan manfaat dari kode fungsional murni . Tetapi dilakukan dengan baik, dengan nama-nama baik, mereka sebenarnya bisa menyenangkan untuk dibaca.

Itu tidak berarti mereka tidak bisa berubah menjadi spageti seperti mimpi buruk. Tetapi, apa yang tidak bisa?

Selamat berburu bebek.

candied_orange
sumber
1
Fungsi dalam / luar adalah paradigma yang berbeda dari yang dijelaskan OP. - Saya setuju bahwa trade off antara variabel lokal dalam fungsi luar kontra argumen yang dilewatkan ke fungsi dalam mirip dengan variabel pribadi pada kelas kontra argumetns ke fungsi pribadi. Namun jika Anda melakukan perbandingan ini, umur objek akan sesuai dengan satu kali fungsi, sehingga variabel dalam fungsi luar adalah bagian dari status persisten.
Taemyr
@Taemyr OP menjelaskan metode pribadi yang disebut dalam konstruktor publik yang tampaknya sangat mirip dengan yang ada di dalam diri saya. Negara tidak benar-benar 'persisten' jika Anda menginjaknya setiap kali Anda memasukkan fungsi. Kadang-kadang negara digunakan sebagai tempat berbagi metode dapat menulis dan membaca. Bukan berarti itu perlu dipertahankan. Fakta bahwa itu tetap bertahan bukanlah sesuatu yang harus diandalkan oleh sesuatu.
candied_orange
1
Itu gigih bahkan jika Anda menginjaknya setiap kali Anda membuang objek.
Taemyr
1
Poin bagus, tetapi mengekspresikannya dalam JavaScript benar-benar mengaburkan diskusi. Juga, jika Anda menghapus sintaks JS, Anda berakhir dengan objek konteks yang diedarkan (penutupan fungsi luar)
fdreger
1
@sdenham Saya berpendapat bahwa ada perbedaan antara atribut kelas dan hasil sementara sementara operasi. Mereka tidak setara. Tetapi satu dapat disimpan di yang lain. Tentu tidak harus begitu. Pertanyaannya adalah apakah memang seharusnya demikian. Ya ada masalah semantik dan seumur hidup. Ada juga masalah arity. Anda tidak berpikir itu cukup penting. Tidak apa-apa. Tetapi untuk mengatakan bahwa menggunakan tingkat kelas untuk apa pun selain keadaan objek adalah pemodelan yang salah adalah bersikeras pada satu cara pemodelan. Saya telah memelihara kode profesional yang melakukannya dengan cara lain.
candied_orange