Itu tergantung dari arti sebenarnya dari a
, b
dan getProduct
.
Tujuan getter adalah untuk dapat mengubah implementasi aktual sambil menjaga antarmuka objek tetap sama. Misalnya, jika suatu hari, getA
menjadi return a + 1;
, perubahan dilokalkan ke pengambil.
Kasus skenario nyata terkadang lebih rumit daripada bidang dukungan konstan yang ditetapkan melalui konstruktor yang terkait dengan pengambil. Misalnya, nilai bidang dapat dihitung atau dimuat dari database dalam versi asli kode. Di versi berikutnya, caching dapat ditambahkan untuk mengoptimalkan kinerja. Jika getProduct
terus menggunakan versi yang dihitung, itu tidak akan mendapat manfaat dari caching (atau pengelola akan melakukan perubahan yang sama dua kali).
Jika masuk akal untuk getProduct
digunakan a
dan b
secara langsung, gunakan itu. Kalau tidak, gunakan getter untuk mencegah masalah pemeliharaan nanti.
Contoh di mana seseorang akan menggunakan getter:
class Product {
public:
Product(ProductId id) : {
price = Money.fromCents(
data.findProductById(id).price,
environment.currentCurrency
)
}
Money getPrice() {
return price;
}
Money getPriceWithRebate() {
return getPrice().applyRebate(rebate); // ← Using a getter instead of a field.
}
private:
Money price;
}
Sementara untuk saat ini, pengambil tidak mengandung logika bisnis, tidak dikecualikan bahwa logika dalam konstruktor akan dimigrasikan ke pengambil untuk menghindari melakukan pekerjaan basis data saat menginisialisasi objek:
class Product {
public:
Product(ProductId id) : id(id) { }
Money getPrice() {
return Money.fromCents(
data.findProductById(id).price,
environment.currentCurrency
)
}
Money getPriceWithRebate() {
return getPrice().applyRebate(rebate);
}
private:
const ProductId id;
}
Kemudian, caching dapat ditambahkan (dalam C #, orang akan menggunakan Lazy<T>
, membuat kode pendek dan mudah; Saya tidak tahu apakah ada yang setara dalam C ++):
class Product {
public:
Product(ProductId id) : id(id) { }
Money getPrice() {
if (priceCache == NULL) {
priceCache = Money.fromCents(
data.findProductById(id).price,
environment.currentCurrency
)
return priceCache;
}
Money getPriceWithRebate() {
return getPrice().applyRebate(rebate);
}
private:
const ProductId id;
Money priceCache;
}
Kedua perubahan difokuskan pada pengambil dan bidang dukungan, kode yang tersisa tidak terpengaruh. Jika, sebagai gantinya, saya telah menggunakan bidang alih-alih pengambil getPriceWithRebate
, saya harus mencerminkan perubahan di sana juga.
Contoh di mana seseorang mungkin akan menggunakan bidang pribadi:
class Product {
public:
Product(ProductId id) : id(id) { }
ProductId getId() const { return id; }
Money getPrice() {
return Money.fromCents(
data.findProductById(id).price, // ← Accessing `id` directly.
environment.currentCurrency
)
}
private:
const ProductId id;
}
Pencari mudah: itu adalah representasi langsung dari bidang konstan (mirip dengan C # 's readonly
) yang tidak diharapkan untuk berubah di masa depan: kemungkinan, ID pengambil tidak akan pernah menjadi nilai yang dihitung. Jadi sederhanakan, dan akses bidang secara langsung.
Manfaat lain adalah bahwa getId
mungkin dihapus di masa depan jika tampaknya tidak digunakan di luar (seperti pada potongan kode sebelumnya).
const
: Saya berasumsi itu berarti kompiler akan menyambungkangetId
panggilan tetap dan memungkinkan Anda untuk membuat perubahan di kedua arah. (Kalau tidak, saya sepenuhnya setuju dengan alasan Anda untuk menggunakan getter.) Dan dalam bahasa yang menyediakan sintaksis properti, ada lebih sedikit alasan untuk tidak menggunakan properti daripada bidang dukungan secara langsung.Biasanya, Anda akan menggunakan variabel secara langsung. Anda berharap untuk mengubah semua anggota saat mengubah implementasi kelas. Tidak menggunakan variabel secara langsung hanya membuatnya lebih sulit untuk secara benar mengisolasi kode yang bergantung pada mereka dan membuatnya lebih sulit untuk membaca anggota.
Ini tentu saja berbeda jika pengambil menerapkan logika nyata, dalam hal itu tergantung pada apakah Anda perlu memanfaatkan logika mereka atau tidak.
sumber
Saya akan mengatakan bahwa menggunakan metode publik akan lebih disukai, jika bukan karena alasan lain selain untuk menyesuaikan dengan KERING .
Saya tahu dalam kasus Anda, Anda memiliki bidang dukungan sederhana untuk pengakses Anda, tetapi Anda bisa memiliki logika tertentu, misalnya kode pemuatan malas, yang harus Anda jalankan sebelum pertama kali Anda menggunakan variabel itu. Dengan demikian, Anda ingin memanggil pengakses Anda alih-alih merujuk langsung ke bidang Anda. Meskipun Anda tidak memiliki ini dalam kasus ini, masuk akal untuk tetap pada satu konvensi. Dengan begitu, jika Anda pernah membuat perubahan pada logika Anda, Anda hanya perlu mengubahnya di satu tempat.
sumber
Untuk kelas, kesederhanaan kecil ini menang. Saya hanya akan menggunakan a * b.
Untuk sesuatu yang jauh lebih rumit, saya akan sangat mempertimbangkan menggunakan getA () * getB () jika saya ingin memisahkan dengan jelas antarmuka "minimal" dari semua fungsi lain di API publik lengkap. Contoh yang bagus adalah std :: string dalam C ++. Ini memiliki 103 fungsi anggota, tetapi hanya 32 di antaranya yang benar - benar membutuhkan akses ke anggota pribadi. Jika Anda memiliki kelas yang kompleks, memaksa semua fungsi "non-inti" untuk secara konsisten melalui "API inti" dapat membuat implementasi jauh lebih mudah untuk diuji, debug, dan refactor.
sumber
getA() * getB()
lebih baik dalam jangka menengah dan panjang.