只有吸气剂的接口有代码气味吗?


10

(我已经看到了这个问题,但是第一个答案更多的是关于自动属性而不是设计,而第二个答案则是向使用者隐藏数据存储代码,我不确定这是我想要的/我的代码做了什么,所以我想听听其他意见)

我有两个非常相似的实体,HolidayDiscountRentalDiscount,它们表示长度折扣,即“如果至少持续numberOfDays使用percent折扣,则适用”。这些表有不同的父实体,并且在不同的地方使用,但是在使用它们的地方,有一种通用的逻辑来获取最大的适用折扣。例如,a HolidayOffer具有多个HolidayDiscounts,在计算其成本时,我们需要找出适用的折扣。租金和相同RentalDiscounts

由于逻辑相同,因此我想将其放在一个地方。这就是以下方法,谓词和比较器的作用:

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

由于唯一需要的信息是天数,因此我最终得到一个界面

public interface LengthDiscount {

    Integer getNumberOfDays();
}

和两个实体

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

该接口具有两个实体实现的单个getter方法,该方法当然可以工作,但是我怀疑这是一个好的设计。考虑到持有值不是属性,它不代表任何行为。这是一个非常简单的案例,我有几个类似,更复杂的案例(使用3-4个吸气剂)。

我的问题是,这是一个不好的设计吗?有什么更好的方法?


4
接口的目的是建立连接模式,而不是代表行为。该责任属于实施中的方法。
罗伯特·哈维

关于您的实现,有很多样板代码(除了提供结构之外,代码实际上不执行任何操作)。确定要全部吗?
罗伯特·哈维

即使是接口的方法是唯一的“干将”并不意味着你不能在机具实行“二传手(如果都二传手,S仍然可以使用抽象)
рüффп

Answers:


5

我会说您的设计有些误导。

一种可能的解决方案是创建一个名为的接口IDiscountCalculator

decimal CalculateDiscount();

现在,任何需要提供折扣的类都将实现此接口。如果折扣使用天数,那么接口实际上并不在乎,因为这是实现细节。

如果所有折扣类都需要该数据成员,则天数可能属于某种抽象基类。如果是特定于类的,则只需声明一个可以根据需要公开或私有访问的属性。


1
我不确定我是否完全同意HolidayDiscountRentalDiscount实施IDiscountCalculator,因为它们不是折扣计算器。计算是用其他方法完成的getMaxApplicableLengthDiscountHolidayDiscount并且RentalDiscount是折扣。不计算折扣,折扣是一种计算得出的适用折扣,或者只是在众多折扣中选择的一种。
user3748908 '02

1
也许接口应该称为IDiscountable。任何需要实现的类都可以。如果假日/租金折扣级别仅为DTO / POCO,则存储结果而不是计算结果就可以了。
乔恩·雷诺

3

回答您的问题:如果接口只有吸气剂,则无法得出代码气味。

代码气味的模糊度量的一个示例是具有许多方法的(肿接口(无论是否使用吸气剂)。它们倾向于违反接口隔离原则。

在语义层面上,也不应违反单一责任原则。如果您看到接口合同中处理的多个不同问题不是一个主题,那么我往往会受到侵犯。有时这很难识别,您需要一些经验来识别它。

另一方面,您应该知道您的界面是否定义了setter。那是因为安装员很可能会改变状态,这就是他们的工作。这里要问的问题:接口后面的对象是否从一个有效状态过渡到另一个有效状态。但这主要不是接口问题,而是实现对象的接口契约的实现。

我对您的方法唯一的担心是您在OR映射上进行操作。在这种情况下,这不是问题。从技术上来说还可以。但是我不会对OR映射的对象进行操作。它们可能具有太多不同的状态,您肯定不会在对其执行的操作中考虑这些状态。

OR映射的对象可能是(假定为JPA)瞬态的,持久分离的未更改,持久连接的未更改,持久分离的已更改,持久连接的已更改...如果在这些对象上使用bean验证,则您将进一步无法查看是否已检查对象。毕竟,您可以识别OR映射对象可以具有的10多种不同状态。您所有的操作是否都正确处理了这些状态?

如果您的界面定义了合同,并且实现遵循合同,那么这当然没问题。但是对于具有OR映射的对象,我有合理的怀疑,是否可以履行这样的合同。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.