Berapa Banyak Logika dalam Getters

46

Rekan kerja saya memberi tahu saya harus ada logika sesedikit mungkin dalam getter dan setter.

Namun, saya yakin bahwa banyak hal dapat disembunyikan di getter dan setter untuk melindungi pengguna / programer dari detail implementasi.

Contoh apa yang saya lakukan:

public List<Stuff> getStuff()
{
   if (stuff == null || cacheInvalid())
   {
       stuff = getStuffFromDatabase();
   }
   return stuff;
}

Contoh bagaimana pekerjaan memberi tahu saya untuk melakukan sesuatu (mereka mengutip 'Kode Bersih' dari Paman Bob):

public List<Stuff> getStuff()
{
    return stuff;
}

public void loadStuff()
{
    stuff = getStuffFromDatabase();
}

Berapa banyak logika yang sesuai dalam setter / pengambil? Apa gunanya getter dan setter kosong kecuali pelanggaran data yang disembunyikan?

Maarten van Leunen
sumber
6
Ini lebih mirip tryGetStuff () bagi saya ...
Bill Michell
16
Ini bukan 'rajin'. Istilah ini digunakan untuk pengakses baca properti, bukan metode yang secara tidak sengaja Anda beri 'get' dalam nama.
Boris Yankov
6
Saya tidak tahu apakah contoh kedua adalah contoh yang adil dari buku kode bersih ini yang Anda sebutkan, atau seseorang yang salah mengerti tentang hal itu, tetapi satu hal yang tidak mudah pecah adalah kode bersih.
Jon Hanna
@ BorisYankov Yah ... metode kedua adalah. public List<Stuff> getStuff() { return stuff; }
R. Schmitz
Bergantung pada kasus penggunaan yang tepat, saya ingin memisahkan caching saya ke kelas yang terpisah. Buat StuffGetterantarmuka, terapkan StuffComputeryang menghitung, dan bungkus di dalam objek StuffCacher, yang bertanggung jawab untuk mengakses cache atau meneruskan panggilan ke tempat StuffComputeryang dibungkusnya.
Alexander

Jawaban:

71

Cara kerja memberitahu Anda untuk melakukan sesuatu adalah timpang.

Sebagai aturan praktis, cara saya melakukan hal-hal adalah sebagai berikut: jika mendapatkan barang-barang secara komputasi murah, (atau jika sebagian besar kemungkinan hal itu akan ditemukan dalam cache,) maka gaya getStuff Anda () baik-baik saja. Jika mendapatkan barang-barang itu diketahui mahal secara komputasional, begitu mahal sehingga biaya iklannya diperlukan pada antarmuka, maka saya tidak akan menyebutnya getStuff (), saya akan menyebutnya calculStuff () atau sesuatu seperti itu, untuk menunjukkan bahwa akan ada beberapa pekerjaan yang harus dilakukan.

Dalam kedua kasus tersebut, cara kerja memberitahu Anda untuk melakukan sesuatu adalah lumpuh, karena getStuff () akan meledak jika loadStuff () belum dipanggil sebelumnya, jadi mereka pada dasarnya ingin Anda memperumit antarmuka Anda dengan memperkenalkan kompleksitas urutan operasi untuk itu. Urutan operasi cukup banyak tentang jenis kompleksitas terburuk yang dapat saya pikirkan.

Mike Nakis
sumber
23
+1 untuk menyebutkan kompleksitas urutan operasi. Sebagai solusinya, mungkin pekerjaan akan meminta saya untuk selalu memanggil loadStuff () di konstruktor, tetapi itu akan menjadi buruk juga karena itu berarti itu akan selalu harus dimuat. Dalam contoh pertama, data hanya dimuat malas ketika dibutuhkan, yang sebagus mungkin.
laurent
6
Saya biasanya mengikuti aturan "jika benar-benar murah, gunakan pengambil properti. Jika mahal, gunakan fungsi". Itu biasanya bermanfaat bagi saya, dan penamaan yang tepat seperti yang Anda tunjukkan untuk menekankan sepertinya juga baik untuk saya.
Denis Troller
3
jika itu bisa gagal - itu bukan rajin. Dalam hal ini bagaimana jika tautan DB sedang down?
Martin Beckett
6
+1, saya sedikit kaget dengan banyaknya jawaban yang salah diposting. Ada Getters / Setters untuk menyembunyikan detail implementasi, jika tidak, variabel hanya boleh dipublikasikan.
Izkata
2
Jangan lupa bahwa membutuhkan loadStuff()fungsi dipanggil sebelum getStuff()fungsi juga berarti bahwa kelas tidak benar-benar mengabstraksi apa yang terjadi di bawah tenda.
rjzii
23

Logika yang diterima tidak masalah.

Tetapi mendapatkan data dari database jauh lebih banyak daripada "logika". Ini melibatkan serangkaian operasi yang sangat mahal di mana banyak hal bisa salah, dan dengan cara yang tidak menentukan. Saya akan ragu melakukan itu secara implisit dalam rajin rajin.

Di sisi lain, sebagian besar ORM mendukung pemuatan malas koleksi, yang pada dasarnya adalah apa yang Anda lakukan.

Michael Borgwardt
sumber
18

Saya pikir bahwa menurut 'Kode Bersih' harus dibagi sebanyak mungkin, menjadi sesuatu seperti:

public List<Stuff> getStuff() {
   if (hasStuff()) {
       return stuff;
   }
   loadStuff();
   return stuff;
}

private boolean hasStuff() {
    if (stuff == null) {
       return false;
    }
    if (cacheInvalid()) {
       return false;        
    }
    return true;
} 

private void loadStuff() {
    stuff = getStuffFromDatabase();
}

Tentu saja, ini omong kosong, mengingat bahwa bentuk yang indah, yang Anda tulis, melakukan hal yang benar dengan sepersekian kode yang sekilas dipahami oleh siapa pun:

public List<Stuff> getStuff() {
   if (stuff == null || cacheInvalid()) {
       stuff = getStuffFromDatabase();
   }
   return stuff;
}

Seharusnya bukan sakit kepala penelepon bagaimana barang-barang itu berada di bawah tenda, dan terutama tidak boleh sakit kepala penelepon untuk mengingat untuk memanggil hal-hal dalam "urutan yang benar" sewenang-wenang.

Joonas Pulakka
sumber
8
-1. Sakit kepala yang sebenarnya adalah ketika pemanggil macet mencari tahu mengapa panggilan pengambil sederhana mengakibatkan akses database yang lambat sekali.
Domenic
14
@Domenic: Akses basis data harus tetap dilakukan, Anda tidak menyimpan kinerja siapa pun dengan tidak melakukannya. Jika Anda membutuhkan ini List<Stuff>, hanya ada satu cara untuk mendapatkannya.
DeadMG
4
@ Lukas: Terima kasih, saya tidak ingat semua trik yang digunakan dalam Kode 'Bersih' untuk membuat bit kode sepele namun satu baris lebih lama ;-) Tetap sekarang.
Joonas Pulakka
2
Anda memfitnah Robert Martin. Dia tidak akan pernah memperluas disjungsi boolean sederhana menjadi fungsi sembilan baris. Fungsi Anda hasStuffadalah kebalikan dari kode bersih.
kevin cline
2
Saya membaca bagian awal dari jawaban ini, dan saya akan memotongnya, berpikir "ada penyembah buku lain", dan kemudian bagian "Tentu saja, ini benar-benar omong kosong" menarik perhatian saya. Dikatakan dengan baik! C -: =
Mike Nakis
8

Mereka memberi tahu saya harus ada logika sesedikit mungkin dalam getter dan setter.

Perlu ada logika sebanyak yang diperlukan untuk memenuhi kebutuhan kelas. Preferensi pribadi saya sesedikit mungkin, tetapi ketika mempertahankan kode, Anda biasanya harus meninggalkan antarmuka asli dengan getter / setter yang ada, tetapi menaruh banyak logika di dalamnya untuk memperbaiki logika bisnis yang lebih baru (sebagai contoh, "pelanggan" "rajin dalam lingkungan pasca-911 harus memenuhi" kenali pelanggan Anda "dan peraturan OFAC , dikombinasikan dengan kebijakan perusahaan yang melarang penampilan pelanggan dari negara-negara tertentu agar tidak muncul [(seperti Kuba atau Iran]).

Dalam contoh Anda, saya lebih suka sampel Anda dan tidak suka sampel "paman bob" karena versi "paman bob" mengharuskan pengguna / pengelola untuk mengingat untuk memanggil loadStuff()sebelum mereka memanggil getStuff()- ini adalah resep untuk bencana jika ada satu pun dari pengelola Anda lupa (atau lebih buruk, tidak pernah tahu). Sebagian besar tempat saya bekerja dalam dekade terakhir masih menggunakan kode yang lebih dari satu dekade, jadi kemudahan pemeliharaan adalah faktor penting untuk dipertimbangkan.

Tangurena
sumber
6

Anda benar, kolega Anda salah.

Lupakan semua aturan tentang apa yang harus atau tidak harus dilakukan oleh metode get. Kelas harus menyajikan abstraksi sesuatu. Kelas Anda sudah terbaca stuff. Di Jawa sudah biasa menggunakan metode 'get' untuk membaca properti. Miliaran garis kerangka telah ditulis berharap dibaca stuffdengan menelepon getStuff. Jika Anda memberi nama fungsi Anda fetchStuffatau selain dari itu getStuff, maka kelas Anda akan tidak kompatibel dengan semua kerangka kerja itu.

Anda mungkin mengarahkan mereka ke Hibernate, di mana 'getStuff ()' dapat melakukan beberapa hal yang sangat rumit, dan melempar RuntimeException pada kegagalan.

kevin cline
sumber
Hibernate adalah ORM, jadi paket itu sendiri menyatakan maksudnya. Maksud ini tidak semudah dipahami jika paket itu sendiri bukan ORM.
FMJaguar
@ FMJaguar: sangat mudah dimengerti. Hibernasi abstrak operasi database untuk menyajikan jaringan objek. OP mengabstraksi operasi basis data untuk menyajikan objek yang memiliki properti bernama stuff. Keduanya menyembunyikan detail untuk membuatnya lebih mudah menulis kode panggilan.
kevin cline
Jika kelas itu adalah kelas ORM, maka maksudnya sudah dinyatakan, dalam konteks lain: pertanyaannya tetap: "Bagaimana programmer lain mengetahui efek samping dari memanggil pengambil?". Jika program ini berisi 1k kelas dan 10k getter, kebijakan yang memungkinkan panggilan basis data di antara mereka bisa menjadi masalah
FMJaguar
4

Kedengarannya seperti ini mungkin sedikit perdebatan murni versus aplikasi yang mungkin dipengaruhi oleh bagaimana Anda lebih suka mengontrol nama fungsi. Dari sudut pandang yang diterapkan, saya lebih suka melihat:

List<String> names = clientRoster.getNames();
List<String> emails = clientRoster.getEmails();

Sebagai lawan:

myObject.load();
List<String> names = clientRoster.getNames();
List<String> emails = clientRoster.getEmails();

Atau lebih buruk lagi:

myObject.loadNames();
List<String> names = clientRoster.getNames();
myOjbect.loadEmails();
List<String> emails = clientRoster.getEmails();

Yang cenderung membuat kode lain jauh lebih berlebihan dan lebih sulit dibaca karena Anda harus mulai mengarungi semua panggilan serupa. Selain itu, memanggil fungsi-fungsi loader atau sejenisnya menghancurkan seluruh tujuan bahkan menggunakan OOP karena Anda tidak lagi diabstraksi dari rincian implementasi objek yang sedang Anda kerjakan. Jika Anda memiliki clientRosterobjek, Anda tidak perlu peduli tentang cara getNameskerjanya, seperti yang Anda lakukan jika Anda harus memanggil loadNames, Anda harus tahu bahwa getNamesmemberi Anda List<String>nama klien.

Jadi, sepertinya masalah ini lebih tentang semantik dan nama terbaik untuk fungsi untuk mendapatkan data. Jika perusahaan (dan lain-lain) memiliki masalah dengan getdan setawalan, lalu bagaimana memanggil fungsi sesuatu seperti retrieveNamesbukan? Dikatakan apa yang sedang terjadi tetapi tidak menyiratkan bahwa operasi akan instan seperti yang diharapkan dari suatu getmetode.

Dalam hal logika dalam metode accessor, jaga agar seminimal mungkin karena secara umum tersirat bersifat instan dengan hanya interaksi nominal yang terjadi dengan variabel. Namun, itu juga umumnya hanya berlaku untuk tipe sederhana, tipe data kompleks (yaitu List) Saya merasa lebih sulit untuk merangkum dengan benar di properti dan umumnya menggunakan metode lain untuk berinteraksi dengan mereka sebagai lawan dari mutator dan accessor yang ketat.

rjzii
sumber
2

Memanggil seorang pengambil harus menunjukkan perilaku yang sama seperti membaca bidang:

  • Harus murah untuk mengambil nilainya
  • Jika Anda menetapkan nilai dengan setter dan kemudian membacanya dengan pengambil, nilainya harus sama
  • Mendapatkan nilai seharusnya tidak memiliki efek samping
  • Seharusnya tidak membuang pengecualian
Sjoerd
sumber
2
Saya tidak sepenuhnya setuju dengan ini. Saya setuju bahwa itu seharusnya tidak memiliki efek samping, tapi saya pikir itu baik-baik saja untuk menerapkannya dengan cara yang membedakannya dari lapangan. Melihat .Net BCL, InvalidOperationException banyak digunakan ketika melihat getter. Juga, lihat jawaban MikeNakis pada urutan operasi.
Maks
Setuju dengan semua poin kecuali yang terakhir. Tentunya mungkin bahwa mendapatkan nilai mungkin melibatkan melakukan perhitungan atau operasi lain yang bergantung pada nilai atau sumber daya lain yang mungkin belum ditetapkan. Dalam kasus-kasus itu saya berharap pengambil akan melontarkan semacam pengecualian.
TMN
1
@ TMN: Dalam skenario kasus terbaik kelas harus diatur sedemikian rupa sehingga pengambil tidak perlu menjalankan operasi yang mampu melemparkan pengecualian. Meminimalkan tempat-tempat yang dapat membuang pengecualian menyebabkan kejutan yang tidak terduga.
hugomg
8
Aku akan setuju dengan poin kedua dengan sebuah contoh khusus: foo.setAngle(361); bar = foo.getAngle(). barbisa saja 361, tetapi mungkin juga sah 1jika sudut terikat pada suatu rentang.
zzzzBov
1
-1. (1) adalah murah dalam contoh ini - setelah lazy loading. (2) saat ini tidak ada "setter" dalam contoh, tetapi jika seseorang menambahkan satu setelah, dan itu hanya set stuff, pengambil akan mengembalikan nilai yang sama. (3) Memuat malas seperti yang ditunjukkan dalam contoh tidak menghasilkan efek samping "terlihat". (4) masih bisa diperdebatkan, mungkin poin yang valid, karena memperkenalkan "lazy loading" sesudahnya dapat mengubah kontrak API sebelumnya - tetapi kita harus melihat kontrak itu untuk membuat keputusan.
Doc Brown
2

Seorang pengambil yang memanggil properti dan metode lain untuk menghitung nilainya sendiri juga menyiratkan ketergantungan. Misalnya, jika properti Anda harus dapat menghitung sendiri, dan melakukan hal itu mengharuskan anggota lain ditetapkan, maka Anda harus khawatir tentang referensi-nol yang tidak disengaja jika properti Anda diakses dalam kode inisialisasi di mana semua anggota tidak perlu disetel.

Itu tidak berarti 'tidak pernah mengakses anggota lain yang bukan bidang dukungan properti dalam pengambil' itu hanya berarti memperhatikan apa yang Anda maksudkan tentang apa status objek yang diperlukan, dan jika itu sesuai dengan konteks yang Anda harapkan properti ini untuk diakses di.

Namun, dalam dua contoh konkret yang Anda berikan, alasan saya memilih satu di atas yang lain sama sekali berbeda. Pembuat Anda diinisialisasi pada akses pertama, misalnya, Inisialisasi Malas . Contoh kedua diasumsikan diinisialisasi pada beberapa titik sebelumnya, misalnya, Inisialisasi Eksplisit .

Kapan tepatnya inisialisasi terjadi mungkin atau mungkin tidak penting.

Misalnya itu bisa sangat sangat lambat, dan perlu dilakukan selama langkah beban di mana pengguna mengharapkan penundaan, daripada kinerja tiba-tiba cegukan ketika pengguna pertama kali memicu akses (yaitu, klik kanan pengguna,, menu konteks muncul, pengguna sudah diklik kanan lagi).

Juga, kadang-kadang ada titik yang jelas dalam eksekusi di mana segala sesuatu yang dapat mempengaruhi / mengotori nilai properti yang di-cache terjadi. Anda bahkan mungkin memverifikasi bahwa tidak ada dependensi yang berubah dan melempar pengecualian nanti. Dalam situasi ini, masuk akal juga untuk men-cache nilai pada titik itu, bahkan jika itu tidak terlalu mahal untuk dihitung, hanya untuk menghindari membuat eksekusi kode lebih kompleks dan lebih sulit untuk diikuti secara mental.

Yang mengatakan, Inisialisasi Malas masuk akal dalam banyak situasi lain. Jadi, seperti yang sering terjadi dalam memprogramnya sulit untuk turun ke aturan, itu turun ke kode konkret.

James
sumber
0

Lakukan saja seperti yang dikatakan @MikeNakis ... Jika Anda baru saja mendapatkan barang-barangnya maka itu baik-baik saja ... Jika Anda melakukan sesuatu yang lain buatlah fungsi baru yang melakukan pekerjaan itu dan buat publik.

Jika properti / fungsi Anda hanya melakukan apa yang dikatakan namanya maka tidak ada banyak ruang untuk komplikasi yang tersisa. Kohesi adalah kunci IMO

Ivan Crojach Karačić
sumber
1
Hati-hati tentang hal ini, Anda dapat mengekspos terlalu banyak keadaan internal Anda. Anda tidak ingin berakhir dengan banyak metode kosong loadFoo()atau preloadDummyReferences()atau createDefaultValuesForUninitializedFields()hanya karena implementasi awal kelas Anda membutuhkannya.
TMN
Tentu ... Saya baru saja mengatakan bahwa jika Anda melakukan apa yang disebutkan namanya tidak boleh ada banyak masalah ... tapi apa yang Anda katakan benar-benar benar ...
Ivan Crojach Karačić
0

Secara pribadi, saya akan mengekspos persyaratan Stuff melalui parameter di konstruktor, dan memungkinkan kelas mana saja yang Instantiating barang untuk melakukan pekerjaan mencari tahu di mana itu harus berasal. Jika barang nol, itu harus mengembalikan nol. Saya lebih suka untuk tidak mencoba solusi pintar seperti OP asli karena ini adalah cara mudah untuk menyembunyikan bug jauh di dalam implementasi Anda di mana sama sekali tidak jelas apa yang mungkin terjadi ketika ada sesuatu yang rusak.

EricBoersma
sumber
0

Ada masalah yang lebih penting daripada hanya "kesesuaian" di sini dan Anda harus mendasarkan keputusan Anda pada mereka . Terutama, keputusan besar di sini adalah apakah Anda ingin mengubah orang untuk memotong cache atau tidak.

  1. Pertama, pikirkan jika ada cara untuk mengatur ulang kode Anda sehingga semua panggilan pemuatan yang diperlukan dan manajemen cache dilakukan di konstruktor / initializer. Jika ini memungkinkan, Anda dapat membuat kelas yang invariannya memungkinkan Anda lakukan pada pengambil sederhana dari bagian 2 dengan keamanan pengambil aktif dari bagian 1. (Skenario menang-menang)

  2. Jika Anda tidak dapat membuat kelas seperti itu, putuskan apakah Anda memiliki tradeoff dan perlu memutuskan apakah Anda ingin mengizinkan konsumen untuk melewatkan kode pemeriksaan cache atau tidak.

    1. Jika penting bahwa konsumen tidak pernah melewati pemeriksaan cache dan Anda tidak keberatan dengan hukuman kinerja, maka gabungkan cek di dalam pengambil dan buat mustahil bagi konsumen untuk melakukan hal yang salah.

    2. Jika OK untuk melewati pemeriksaan cache atau sangat penting bahwa Anda dijamin O (1) kinerja dalam pengambil kemudian gunakan panggilan terpisah.

Seperti yang mungkin telah Anda catat, saya bukan penggemar besar filosofi "clean-code", "membagi semuanya menjadi fungsi-fungsi kecil". Jika Anda memiliki banyak fungsi ortogonal yang dapat dipanggil dalam urutan apa pun yang membelahnya akan memberi Anda kekuatan yang lebih ekspresif dengan sedikit biaya. Namun, jika fungsi Anda memiliki dependensi pesanan (atau hanya benar-benar berguna dalam urutan tertentu) maka membaginya hanya meningkatkan jumlah cara Anda dapat melakukan hal-hal yang salah, sambil menambahkan sedikit manfaat.

hugomg
sumber
-1, konstruktor harus membangun, bukan menginisialisasi. Menempatkan logika database dalam sebuah konstruktor membuat kelas itu benar-benar tidak dapat diuji, dan jika Anda memiliki lebih dari ini, waktu startup aplikasi Anda akan menjadi sangat besar. Dan itu hanya untuk pemula.
Domenic
@Domenic: Ini adalah masalah semantik dan tergantung pada bahasa. Titik bahwa suatu objek cocok untuk digunakan dan menyediakan invarian yang sesuai setelah, dan hanya setelah, itu sepenuhnya dibangun.
hugomg
0

Menurut pendapat saya, Getters seharusnya tidak memiliki banyak logika di dalamnya. Mereka seharusnya tidak memiliki efek samping dan Anda tidak boleh mendapatkan pengecualian dari mereka. Kecuali tentu saja, Anda tahu apa yang Anda lakukan. Sebagian besar getter saya tidak memiliki logika di dalamnya dan hanya pergi ke lapangan. Tetapi pengecualian untuk itu adalah dengan API publik yang perlu sesederhana mungkin untuk digunakan. Jadi saya punya satu pengambil yang akan gagal jika pengambil lain belum dipanggil. Solusinya? Baris kode seperti var throwaway=MyGetter;pada pengambil yang bergantung padanya. Saya tidak bangga akan hal itu, tetapi saya masih tidak melihat cara yang lebih bersih untuk melakukannya

Earlz
sumber
0

Ini terlihat seperti read from cache dengan lazy loading. Seperti yang telah dicatat orang lain, memeriksa dan memuat mungkin termasuk dalam metode lain. Memuat mungkin perlu disinkronkan agar Anda tidak mendapatkan dua puluh utas yang memuat sekaligus.

Mungkin tepat untuk menggunakan nama getCachedStuff()untuk pengambil karena tidak akan memiliki waktu eksekusi yang konsisten.

Bergantung pada cara kerja cacheInvalid()rutinnya, pemeriksaan untuk null mungkin tidak diperlukan. Saya tidak akan mengharapkan cache valid kecuali stufftelah diisi dari database.

BillThor
sumber
0

Logika utama yang saya harapkan untuk dilihat dalam getter yang mengembalikan daftar adalah logika untuk memastikan daftar tidak dapat dimodifikasi. Karena kedua contoh Anda berpotensi memecahkan enkapsulasi.

sesuatu seperti:

public List<Stuff> getStuff()
{
    return Collections.unmodifiableList(stuff);
}

sebagai untuk caching di pengambil, saya pikir ini akan baik-baik saja tetapi saya mungkin tergoda untuk keluar dari logika cache jika membangun cache membutuhkan waktu yang signifikan. yaitu tergantung.

jk.
sumber
0

Bergantung pada kasus penggunaan yang tepat, saya ingin memisahkan caching saya ke kelas yang terpisah. Buat StuffGetterantarmuka, terapkan StuffComputeryang menghitung, dan bungkus di dalam objek StuffCacher, yang bertanggung jawab untuk mengakses cache atau meneruskan panggilan ke tempat StuffComputeryang dibungkusnya.

interface StuffGetter {
     public List<Stuff> getStuff();
}

class StuffComputer implements StuffGetter {
     public List<Stuff> getStuff() {
         getStuffFromDatabase()
     }
}

class StuffCacher implements StuffGetter {
     private stuffComputer; // DI this
     private Cache<List<Stuff>> cache = new Cache<>();

     public List<Stuff> getStuff() {
         if cache.hasStuff() {
             return cache.getStuff();
         }

         List<Stuffs> stuffs = stuffComputer.getStuff();
         cache.store(stuffs);
         return stuffs;
     }
}

Desain ini memungkinkan Anda dengan mudah menambahkan caching, menghapus caching, mengubah logika derivasi yang mendasarinya (misalnya mengakses DB vs mengembalikan data tiruan), dll. Ini agak bertele-tele, tetapi layak sementara untuk proyek yang cukup maju.

Alexander
sumber
-1

IMHO itu sangat sederhana jika Anda menggunakan desain berdasarkan kontrak. Putuskan apa yang harus disediakan oleh pengambil Anda dan kode yang sesuai (kode sederhana atau logika kompleks yang mungkin terlibat atau didelegasikan di suatu tempat).

Kemoda
sumber
+1: Saya setuju dengan Anda! Jika objek hanya dimaksudkan untuk menyimpan beberapa data, maka getter hanya akan mengembalikan konten objek saat ini. Dalam hal ini adalah tanggung jawab beberapa objek lain untuk memuat data. Jika kontrak mengatakan bahwa objek adalah proksi dari catatan database, maka pengambil harus mengambil data dengan cepat. Itu bisa menjadi lebih rumit jika data telah dimuat tetapi tidak up to date: haruskah objek diberitahu tentang perubahan dalam database? Saya pikir tidak ada jawaban unik untuk pertanyaan ini.
Giorgio