Haruskah metode kelas memanggil getter dan setter sendiri?

53

Tempat saya bekerja, saya melihat banyak kelas yang melakukan hal-hal seperti ini:

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

Jika saya menulis ini dari awal, saya akan menulis methodWithLogic()seperti ini sebagai gantinya:

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

Saya merasa bahwa ketika kelas memanggil getter dan setter sendiri, itu membuat kode lebih sulit untuk dibaca. Bagi saya hampir menyiratkan bahwa logika kompleks terjadi dalam pemanggilan metode meskipun dalam kasus kami hampir tidak pernah ada. Ketika saya sedang men-debug beberapa kode yang tidak dikenal, siapa yang mengatakan bahwa bug tersebut tidak menimbulkan efek samping dalam metode itu? Dengan kata lain, itu membuat saya melakukan banyak perjalanan samping dalam perjalanan memahami kode.

Apakah ada manfaat untuk metode pertama? Apakah metode pertama sebenarnya lebih baik?

Daniel Kaplan
sumber
Ketika saya sedang men-debug beberapa kode yang tidak dikenal, siapa yang mengatakan bahwa bug tersebut tidak menimbulkan efek samping dalam metode itu? Unit Anda diuji. :)
Joshua Taylor
1
Menggunakan getter / setters dalam kode internal Anda memungkinkan Anda untuk membuat breakpoint dalam kode Anda jika Anda akan melewatinya dengan debugger. Ini juga memungkinkan Anda untuk memastikan bahwa jika ada yang salah dengan pengaturan pengaturan / mendapatkan nilai, Anda tahu bahwa itu karena metode itu dan bukan karena beberapa akses yang tidak valid. Secara keseluruhan, ini memberikan kode konsistensi yang lebih banyak.
callyalater

Jawaban:

46

Saya tidak akan mengatakan mana yang lebih baik atau lebih buruk, karena itu sebagian tergantung pada situasi Anda (menurut saya). Tetapi pertimbangkan bahwa getter dan setter Anda dapat mengubah implementasi nanti, dan melewati mereka akan melewati logika itu.

Misalnya, apa yang terjadi jika Anda menambahkan bendera "kotor" ke beberapa bidang setter nanti? Dengan memanggil setter Anda di kode internal Anda, Anda akan menetapkan bendera kotor tanpa mengubah kode lainnya. Dalam banyak situasi ini akan menjadi hal yang baik.

Matt S
sumber
Tetapi bagaimana ketika Anda menginisialisasi data di backend dan Anda tidak ingin itu ditafsirkan sebagai kotor. Jika Anda menelepon setField()dari awal, Anda baru saja memperkenalkan bug.
Daniel Kaplan
6
Benar, itu sebabnya saya katakan itu sebagian tergantung pada situasi. Mungkin inisialisasi data Anda harus melewati setter, tetapi kode logis lainnya tidak boleh. Pilih aturan yang sesuai dan patuhi itu.
Matt S
3
@DanielKaplan: intinya adalah bahwa memanggil getter dan setter dalam banyak kasus adalah hal yang benar untuk dilakukan, dan hanya dalam situasi tertentu tidak. Namun, dalam kode dunia nyata, ketika Anda mengubah pengambil / penyetel-implementasi nanti untuk memperkenalkan beberapa efek samping yang disengaja, Anda kemungkinan besar harus memeriksa setiap panggilan ke pengambil atau penyetel di dalam kelas, dan juga setiap akses langsung ke bidang. Itu sebabnya Anda harus berusaha menjaga kelas Anda sekecil mungkin.
Doc Brown
33

Memanggil setter secara langsung bukanlah masalah. Pertanyaannya seharusnya: Mengapa kode kita memiliki seter di mana-mana?

Kelas yang bisa berubah adalah permainan yang berbahaya, terutama di mana kelas itu sendiri mengelola negaranya sendiri. Pertimbangkan berapa banyak pemukim yang benar-benar perlu ada. Berapa banyak yang bisa diatur dalam konstruktor dan kemudian menjadi dienkapsulasi seluruhnya oleh kelas itu sendiri?

Jika bidang harus diselesaikan secara eksternal maka tanyakan pada diri Anda apakah metode dengan logika harus ada. Apakah kelas Anda sendiri benar-benar hanya kelas data / negara bagian? Apakah kelas ini memiliki lebih dari satu tanggung jawab?

Jangan salah paham, akan ada kasus di mana ini baik-baik saja. Saya tidak mengatakan bahwa Anda harus menghilangkan setter pada kelas dengan logika sepenuhnya. Tapi ini harus menjadi pengecualian, bukan aturannya. Dan, dalam beberapa kasus itu, harus jelas mana yang dapat diakses secara langsung.

pdr
sumber
1
Saya sepenuhnya setuju / mempraktikkan pemikiran ini. Saya juga berpikir Anda memunculkan poin yang sebenarnya lebih penting daripada pertanyaan saya. Karena itu, saya merasa tidak langsung menjawab pertanyaan awal saya. Kecuali jika Anda mengatakan, "dalam skema besar hal-hal pertanyaan Anda tidak masalah". Yang mungkin juga benar :)
Daniel Kaplan
@DanielKaplan: Saya mengatakan hal itu. :) Saya terpecah antara jawaban dan komentar dengan yang satu ini, tetapi saya benar-benar tidak merasa bahwa ada jawaban yang benar yang tidak menanyakan pertanyaan yang lebih besar.
pdr
9

Ya, metode kelas Anda harus memanggil getter dan setter. Inti dari penulisan getter dan setter adalah pemeriksaan di masa depan. Anda bisa membuat setiap properti menjadi bidang dan langsung mengekspos data ke pengguna kelas. Alasan Anda membuat getter dan setter belum tentu karena ada logika kompleks sekarang tetapi agar antarmuka tidak rusak di masa depan jika Anda harus menambahkannya.

Michael
sumber
1
Saya tidak melihat hubungan antara kalimat pertama Anda dan sisa paragraf Anda. Kalimat pertama mengatakan bahwa kelas harus memanggil nya sendiri getter dan setter. Sisa paragraf menjelaskan mengapa kode klien (yaitu: kode menggunakan kelas) harus menggunakan getter dan setter alih-alih mengakses bidang secara langsung. Tapi saya tidak melihat bagaimana itu alasan mengapa kelas tidak boleh langsung mengakses bidangnya sendiri .
Daniel Kaplan
1
@DanielKaplan Maksud saya adalah bahwa alasannya satu dan sama. Bahwa jika Anda menambahkan logika setter nanti, itu berdampak pada kode internal sebanyak (berpotensi) sebagai eksternal.
Michael
2
@Michael: Sering kali ada alasan bagus bagi kelas untuk tidak menggunakan getter / setter sendiri. Satu skenario umum adalah di mana ada banyak setter dari formulir someField=newValue; refresh(); Jika metode memungkinkan beberapa bidang diatur, memanggil setter untuk menulis bidang-bidang itu akan menyebabkan operasi "penyegaran" yang berlebihan. Menulis semua bidang dan kemudian menelepon refresh()sekali dapat menghasilkan operasi yang lebih efisien dan tampak lebih halus.
supercat
6

Untuk menjawab pertanyaan Anda dalam satu kata, ya.

Memiliki panggilan kelas getter dan setter sendiri menambahkan ekstensibilitas dan menyediakan basis yang lebih baik untuk kode masa depan.

Katakan Anda memiliki sesuatu seperti ini:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

Memanggil setter untuk tahun dan membuat saat ini mungkin tidak menambahkan fungsionalitas apa pun, tetapi bagaimana jika Anda ingin menambahkan sesuatu seperti validasi input ke setter?

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}
Zach Latta
sumber
5

Satu hal yang belum disebutkan adalah bahwa pengambil dan penyetel (karena semua metode) adalah virtual di Jawa. Ini menambahkan fitur lain untuk selalu menggunakannya dalam kode Anda. Pengguna lain dapat memperluas kelas Anda, menimpa getter dan setter Anda. Kelas dasar Anda kemudian akan menggunakan data subkelas alih-alih miliknya. Dalam bahasa di mana Anda secara eksplisit menandai fungsi virtual, ini jauh lebih praktis karena Anda dapat memprediksi dan mendeklarasikan fungsi mana yang bisa dilakukan. Di Jawa, ini adalah sesuatu yang Anda harus selalu waspadai. Jika perilaku yang diinginkan maka menggunakannya dalam kode Anda adalah hal yang baik, jika tidak, tidak begitu banyak.

Jonathan Henson
sumber
3

Jika Anda mencoba untuk mencapai sesuatu yang disediakan oleh antarmuka publik, maka gunakan getter / setter.

Namun, sebagai pemilik / pengembang kelas, Anda diizinkan untuk mengakses bagian pribadi dari kode Anda (dari dalam kelas tentu saja), tetapi Anda juga bertanggung jawab untuk mengurangi bahaya.

Jadi mungkin Anda memiliki pengambil yang beralih pada beberapa daftar internal, dan Anda ingin mendapatkan nilai saat ini tanpa menambah iterator. Dalam hal ini, gunakan variabel pribadi.

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}
ConditionRacer
sumber
Bisakah Anda memberikan alasan untuk ini?
Daniel Kaplan
1

Iya. Getters dan setters mewakili status, jadi balikkan pertanyaan ini - apakah Anda ingin melacak beberapa cara untuk mengubah status objek dengan cara yang sama kecuali Anda harus?

Semakin sedikit hal yang harus Anda perhatikan, semakin baik - inilah sebabnya objek yang tidak dapat diubah lebih mudah ditangani.

Pertimbangkan, tidak ada yang mencegah Anda memiliki bidang publik dan pengambil / penyetel publik - tetapi apa yang akan Anda peroleh?

Kadang-kadang mungkin diinginkan atau bahkan perlu untuk secara langsung mengakses lapangan karena satu atau beberapa alasan, Anda tidak boleh menghindarinya jika itu terjadi. Tetapi Anda harus benar-benar hanya melakukannya jika ada manfaat yang pasti untuk melakukannya.

jmoreno
sumber
Inti dari pertanyaan saya adalah bahwa saya hanya bertanya tentang akses langsung bidang di kelas yang sama. Saya sudah mengerti tujuan getter dan setter untuk kode klien.
Daniel Kaplan
@DanielKaplan: Saya mengerti itu, dan apa yang saya katakan adalah sejauh praktis Anda harus memperlakukan mereka sama.
jmoreno
@DanielKaplan: Anda dapat memiliki satu setter pribadi dan satu setter publik, yang memiliki hasil yang sama persis (semua efek samping sama) setidaknya untuk momement, tetapi apa yang akan memberi Anda selain potensi hal-hal yang berbeda? Perbedaan antara skenario ini dan apa yang Anda gambarkan adalah bahwa Anda tidak dapat menghindari bisa mengakses keadaan di luar getter / setter, tetapi Anda dapat menghindari sebenarnya melakukannya. Jangan menyulitkan kode Anda kecuali Anda harus melakukannya.
jmoreno
1

Kedua metode memiliki kasus penggunaannya. Karena setter publik menjaga nilai bidang (dan / atau nilai terikat) konsisten, Anda harus menggunakan setter saat logika metode Anda tidak mengganggu logika konsistensi ini. Jika Anda hanya mengatur "properti" Anda, gunakan setter. Di sisi lain, ada situasi, di mana Anda memerlukan akses langsung ke beberapa bidang, misalnya operasi massal dengan setter berat, atau operasi di mana konsep setter terlalu sederhana.

Adalah tanggung jawab Anda untuk menjaga segala sesuatu tetap konsisten. Setter melakukan ini menurut definisi, tetapi tidak dapat mencakup kasus yang kompleks.

Artur
sumber
1

Saya akan mengatakan Tidak ..

Jika getter / setter Anda hanya mendapatkan dan mengatur (tidak ada inisialisasi malas, tidak ada pemeriksaan, dll) maka gunakan saja variabel Anda. Jika di masa depan Anda mengubah getter / setter Anda, Anda dapat dengan sangat baik mengubah methodWithLogic()(karena kelas Anda yang berubah) dan Anda dapat memanggil pengambil / setter alih-alih penugasan langsung. Anda dapat mendokumentasikan panggilan ini untuk pengambil / penyetel (karena akan aneh untuk memanggil pengambil / penyetel ketika sisa kode kelas Anda langsung menggunakan variabel).

JVM akan inline sering panggilan ke getter / setter. Jadi, ini bukan masalah kinerja. Keuntungan menggunakan variabel adalah keterbacaan dan IMHO, saya akan melakukannya.

Semoga ini membantu..

Pravin Sonawane
sumber