Là một giao diện chỉ có getters một mùi mã?


10

(Tôi đã nhìn thấy câu hỏi này , nhưng câu trả lời đầu tiên đi về các thuộc tính tự động hơn về thiết kế, và một trong những thứ hai nói giấu mã lưu trữ dữ liệu từ người tiêu dùng , mà tôi không chắc chắn là những gì tôi muốn / mã của tôi không, vì vậy tôi muốn nghe một số ý kiến ​​khác)

Tôi có hai thực thể rất giống nhau HolidayDiscountRentalDiscount, đại diện cho giảm giá theo chiều dài là 'nếu nó kéo dài ít nhất numberOfDays, percentgiảm giá được áp dụng'. Các bảng có fks cho các thực thể mẹ khác nhau và được sử dụng ở những nơi khác nhau, nhưng khi chúng được sử dụng, có một logic chung để được giảm giá tối đa áp dụng. Chẳng hạn, a HolidayOffercó một số HolidayDiscountsvà khi tính chi phí của nó, chúng ta cần tính ra mức chiết khấu áp dụng. Tương tự cho thuê và RentalDiscounts.

Vì logic là như nhau, tôi muốn giữ nó ở một nơi duy nhất. Đó là những gì phương pháp, vị ngữ và so sánh sau đây làm:

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());
    }
}

Vì thông tin duy nhất cần là số ngày, tôi kết thúc với một giao diện như

public interface LengthDiscount {

    Integer getNumberOfDays();
}

Và hai thực thể

@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;
    }
}

Giao diện có một phương thức getter duy nhất mà hai thực thể thực hiện, tất nhiên là hoạt động, nhưng tôi nghi ngờ đó là một thiết kế tốt. Nó không đại diện cho bất kỳ hành vi nào, đưa ra một giá trị không phải là một tài sản. Đây là một trường hợp khá đơn giản, tôi có thêm một vài trường hợp tương tự, phức tạp hơn (với 3-4 getters).

Câu hỏi của tôi là, đây có phải là một thiết kế xấu? Một cách tiếp cận tốt hơn là gì?


4
Mục đích của giao diện là thiết lập một mô hình kết nối, không đại diện cho hành vi. Nhiệm vụ đó thuộc về các phương pháp trong việc thực hiện.
Robert Harvey

Theo triển khai của bạn, có rất nhiều nồi hơi (mã không thực sự làm gì cả, ngoài việc cung cấp cấu trúc). Bạn có chắc là bạn cần tất cả không?
Robert Harvey

Phương pháp thậm chí giao diện của chỉ 'thu khí' nó không có nghĩa là bạn không thể cụ 'setters' về việc thực hiện (nếu tất cả đều có setter, s bạn vẫn có thể sử dụng một bản tóm tắt)
рüффп

Câu trả lời:


5

Tôi muốn nói rằng thiết kế của bạn là một chút sai lầm.

Một giải pháp tiềm năng sẽ là tạo ra một giao diện được gọi là IDiscountCalculator.

decimal CalculateDiscount();

Bây giờ bất kỳ lớp nào cần giảm giá sẽ thực hiện giao diện này. Nếu giảm giá sử dụng số ngày, vì vậy, giao diện không thực sự quan tâm vì đó là chi tiết triển khai.

Số ngày có thể thuộc về một số loại cơ sở trừu tượng nếu tất cả các lớp giảm giá cần thành viên dữ liệu này. Nếu nó là lớp cụ thể, thì chỉ cần khai báo một thuộc tính có thể được truy cập công khai hoặc riêng tư tùy theo nhu cầu.


1
Tôi không chắc chắn tôi hoàn toàn đồng ý với việc có HolidayDiscountRentalDiscountthực hiện IDiscountCalculator, vì chúng không phải là máy tính chiết khấu. Việc tính toán được thực hiện theo phương pháp khác đó getMaxApplicableLengthDiscount. HolidayDiscountRentalDiscountđược giảm giá. Giảm giá không được tính, là một loại chiết khấu áp dụng được tính toán, hoặc chỉ được chọn trong số một số giảm giá.
dùng3748908

1
Có lẽ Giao diện nên được gọi là IDiscountable. Bất cứ lớp học nào cần thực hiện đều có thể. Nếu các lớp giảm giá cho kỳ nghỉ / cho thuê chỉ là DTO / POCO và lưu trữ kết quả thay vì tính toán thì không sao.
Jon Raynor

3

Để trả lời câu hỏi của bạn: bạn không thể kết luận mùi mã nếu một giao diện chỉ có getters.

Một ví dụ cho một số liệu mờ cho mùi mã là các giao diện cồng kềnh với nhiều phương thức (không phải là nếu getters hay không). Họ có xu hướng vi phạm nguyên tắc phân chia giao diện.

Ở cấp độ ngữ nghĩa, nguyên tắc trách nhiệm duy nhất cũng không nên bị vi phạm. Tôi có xu hướng bị vi phạm nếu bạn thấy một số vấn đề khác nhau được xử lý trong hợp đồng giao diện không phải là một chủ đề. Điều này đôi khi rất khó để xác định và bạn cần một số kinh nghiệm để xác định nó.

Mặt khác, bạn nên biết nếu giao diện của bạn xác định setters. Đó là bởi vì setters có khả năng thay đổi trạng thái, đó là công việc của họ. Câu hỏi cần đặt ra ở đây: Liệu đối tượng đằng sau giao diện có thực hiện chuyển đổi từ trạng thái hợp lệ sang trạng thái hợp lệ khác không. Nhưng đó không phải chủ yếu là vấn đề của giao diện mà là việc thực hiện hợp đồng giao diện của đối tượng thực hiện.

Mối quan tâm duy nhất tôi có với cách tiếp cận của bạn là bạn hoạt động trên OR-Mappings. Trong trường hợp nhỏ này, đây không phải là một vấn đề. Về mặt kỹ thuật là ok. Nhưng tôi sẽ không hoạt động trên các đối tượng OR-Mapped. Họ có thể có quá nhiều trạng thái khác nhau mà bạn chắc chắn không xem xét trong các hoạt động được thực hiện trên chúng ...

Các đối tượng OR-Mapped có thể là (giả định JPA) tạm thời, tách rời liên tục không thay đổi, liên tục không thay đổi, tách rời liên tục thay đổi, đính kèm liên tục thay đổi ... nếu bạn sử dụng xác thực bean trên các đối tượng mà bạn không thể kiểm tra xem đối tượng có được kiểm tra hay không. Sau tất cả, bạn có thể xác định hơn 10 trạng thái khác nhau mà đối tượng OR-Mapped có thể có. Làm tất cả các hoạt động của bạn xử lý các trạng thái đúng cách?

Điều này chắc chắn không có vấn đề gì nếu giao diện của bạn xác định hợp đồng và việc thực hiện tuân theo nó. Nhưng đối với các đối tượng được ánh xạ OR, tôi có nghi ngờ hợp lý rằng có thể điền đầy đủ hợp đồng như vậy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.