(Saya sudah melihat pertanyaan ini , tetapi jawaban pertama tentang properti otomatis lebih dari tentang desain, dan jawaban kedua mengatakan menyembunyikan kode penyimpanan data dari konsumen , yang saya tidak yakin apa yang saya inginkan / kode saya tidak, jadi saya ingin mendengar pendapat lain)
Saya memiliki dua entitas yang sangat mirip, HolidayDiscount
dan RentalDiscount
, yang mewakili diskon panjang sebagai 'jika bertahan setidaknya numberOfDays
, percent
diskon berlaku'. Tabel memiliki fks ke entitas induk yang berbeda, dan digunakan di tempat yang berbeda, tetapi di mana mereka digunakan, ada logika umum untuk mendapatkan diskon maksimum yang berlaku. Misalnya, a HolidayOffer
memiliki sejumlah HolidayDiscounts
, dan ketika menghitung biayanya, kita perlu mencari tahu diskon yang berlaku. Sama untuk rental dan RentalDiscounts
.
Karena logikanya sama, saya ingin menyimpannya di satu tempat. Itulah yang dilakukan oleh metode, predikat, dan pembanding berikut:
Optional<LengthDiscount> getMaxApplicableLengthDiscount(List<LengthDiscount> discounts, int daysToStay) {
if (discounts.isEmpty()) {
return Optional.empty();
}
return discounts.stream()
.filter(new DiscountIsApplicablePredicate(daysToStay))
.max(new DiscountMinDaysComparator());
}
public class DiscountIsApplicablePredicate implements Predicate<LengthDiscount> {
private final long daysToStay;
public DiscountIsApplicablePredicate(long daysToStay) {
this.daysToStay = daysToStay;
}
@Override
public boolean test(LengthDiscount discount) {
return daysToStay >= discount.getNumberOfDays();
}
}
public class DiscountMinDaysComparator implements Comparator<LengthDiscount> {
@Override
public int compare(LengthDiscount d1, LengthDiscount d2) {
return d1.getNumberOfDays().compareTo(d2.getNumberOfDays());
}
}
Karena satu-satunya informasi yang diperlukan adalah jumlah hari, saya berakhir dengan antarmuka sebagai
public interface LengthDiscount {
Integer getNumberOfDays();
}
Dan dua entitas
@Entity
@Table(name = "holidayDiscounts")
@Setter
public class HolidayDiscount implements LengthDiscount {
private BigInteger percent;
private Integer numberOfDays;
public BigInteger getPercent() {
return percent;
}
@Override
public Integer getNumberOfDays() {
return numberOfDays;
}
}
@Entity
@Table(name = "rentalDiscounts")
@Setter
public class RentalDiscount implements LengthDiscount {
private BigInteger percent;
private Integer numberOfDays;
public BigInteger getPercent() {
return percent;
}
@Override
public Integer getNumberOfDays() {
return numberOfDays;
}
}
Antarmuka memiliki metode pengambil tunggal yang diterapkan dua entitas, yang tentu saja berfungsi, tapi saya ragu itu desain yang bagus. Itu tidak mewakili perilaku apa pun, mengingat memegang nilai bukanlah properti. Ini adalah kasus yang agak sederhana, saya punya beberapa kasus yang serupa, lebih kompleks (dengan 3-4 getter).
Pertanyaan saya adalah, apakah ini desain yang buruk? Apa pendekatan yang lebih baik?
sumber
Jawaban:
Saya akan mengatakan desain Anda agak salah arah.
Salah satu solusi potensial adalah membuat antarmuka yang disebut
IDiscountCalculator
.Sekarang setiap kelas yang perlu memberikan diskon akan mengimplementasikan antarmuka ini. Jika diskon menggunakan jumlah hari, ya, antarmuka tidak terlalu peduli karena itu adalah detail implementasi.
Jumlah hari mungkin termasuk dalam semacam kelas dasar abstrak jika semua kelas diskon membutuhkan anggota data ini. Jika itu adalah kelas khusus, maka cukup deklarasikan properti yang dapat diakses secara publik atau pribadi tergantung pada kebutuhan.
sumber
HolidayDiscount
danRentalDiscount
menerapkanIDiscountCalculator
, karena mereka bukan kalkulator diskon. Perhitungan dilakukan dengan metode laingetMaxApplicableLengthDiscount
.HolidayDiscount
danRentalDiscount
diskon. Diskon tidak dihitung, adalah jenis diskon yang berlaku apa yang dihitung, atau hanya dipilih di antara sejumlah diskon.Untuk menjawab pertanyaan Anda: Anda tidak dapat menyimpulkan bau kode jika antarmuka hanya memiliki getter.
Contoh untuk metrik fuzzy untuk bau kode adalah antarmuka yang membengkak dengan banyak metode (tidak jika getter atau tidak). Mereka cenderung melanggar prinsip pemisahan antarmuka.
Pada tingkat semantik, prinsip tanggung jawab tunggal tidak boleh dilanggar juga. Saya cenderung dilanggar jika Anda melihat beberapa masalah berbeda ditangani dalam kontrak antarmuka yang bukan merupakan satu subjek. Ini terkadang sulit untuk diidentifikasi dan Anda perlu beberapa pengalaman untuk mengidentifikasinya.
Di sisi lain Anda harus waspada jika antarmuka Anda mendefinisikan setter. Itu karena pemukim cenderung mengubah keadaan, itulah pekerjaan mereka. Pertanyaan untuk diajukan di sini: Apakah objek di belakang antarmuka membuat transisi dari status yang valid ke status valid lainnya. Tapi itu bukan terutama masalah antarmuka tetapi implementasi kontrak antarmuka objek pelaksana.
Satu-satunya masalah yang saya miliki dengan pendekatan Anda adalah bahwa Anda beroperasi pada OR-Mappings. Dalam kasus kecil ini ini bukan masalah. Secara teknis tidak masalah. Tapi saya tidak akan beroperasi pada objek yang dipetakan OR. Mereka dapat memiliki terlalu banyak negara berbeda yang pasti tidak Anda pertimbangkan dalam operasi yang dilakukan pada mereka ...
Objek yang dipetakan OR mungkin (mengandaikan JPA) transient, persistent detached, tidak berubah, persistent attached tidak berubah, persistent detached berubah, persistent dilampirkan berubah ... jika Anda menggunakan validasi kacang pada objek-objek yang selanjutnya Anda tidak dapat melihat apakah objek diperiksa atau tidak. Setelah semua Anda dapat mengidentifikasi lebih dari 10 negara yang berbeda objek OR-Mapped dapat memiliki. Apakah semua operasi Anda menangani keadaan ini dengan benar?
Ini tentu bukan masalah jika antarmuka Anda mendefinisikan kontrak dan implementasinya mengikutinya. Tetapi untuk objek yang dipetakan OR saya ragu-ragu yang wajar dapat memenuhi kontrak seperti itu.
sumber