ゲッターのみのインターフェースはコードの匂いですか?


10

(私はこの質問を見ましたが、最初の答えはデザインよりも自動プロパティに関するもので、2番目の答えは、データストレージコードをコンシューマから隠すことです。これは、自分のコードが何をしたいのかわからないのですが、他の意見も聞きたいです)

私には2つの非常に類似したエンティティがHolidayDiscountありRentalDiscount、「それが少なくとも続く場合numberOfDayspercent割引が適用される」として長さ割引を表します。テーブルにはさまざまな親エンティティへのアクセス権があり、さまざまな場所で使用されますが、それらが使用される場所には、適用可能な最大の割引を取得するための共通のロジックがあります。たとえば、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;
    }
}

このインターフェースには、2つのエンティティーが実装する単一のゲッターメソッドがありますが、これはもちろん機能しますが、それが優れたデザインであるとは思えません。値を保持することはプロパティではないため、これは動作を表すものではありません。これはかなり単純なケースですが、似たような複雑なケースがいくつかあります(3〜4ゲッターを使用)。

私の質問は、これは悪いデザインですか?より良いアプローチとは何ですか?


4
インターフェイスの目的は、動作を表すことではなく、接続のパターンを確立することです。その義務は実装の方法にあります。
Robert Harvey、

実装に関しては、非常に多くのボイラープレートがあります(構造を提供する以外に、実際には何もしないコード)。あなたはそれのすべてが必要ですか?
ロバートハーベイ

でも、インタフェースのメソッドは、唯一の「ゲッター」であり、それはあなたが、実装上の実装「セッターは」(すべてのセッターを持っている場合は、よあなたはまだ抽象を使用することができます)ことができないことを意味しない
рüффп

回答:


5

あなたのデザインは少し見当違いです。

解決策の1つは、というインターフェースを作成することIDiscountCalculatorです。

decimal CalculateDiscount();

これで、割引を提供する必要があるすべてのクラスがこのインターフェースを実装します。割引が日数を使用する場合、それが実際の日数であっても、それは実装の詳細であるため、インターフェイスは実際には気にしません。

すべての割引クラスがこのデータメンバーを必要とする場合、日数はおそらく何らかの抽象基本クラスに属します。クラス固有の場合は、必要に応じてパブリックまたはプライベートにアクセスできるプロパティを宣言します。


1
これらは割引計算機ではないためHolidayDiscount、のRentalDiscount実装と実装に完全に同意するかどうかIDiscountCalculatorはわかりません。計算はその別の方法で行われgetMaxApplicableLengthDiscountます。HolidayDiscountRentalDiscount割引です。割引は計算されないか、計算されるか、またはいくつかの割引から選択された適用可能な割引の一種です。
user3748908 2017

1
多分インターフェイスはIDiscountableと呼ばれるべきです。実装する必要のあるクラスは何でもできます。休日/レンタル割引クラスがDTO / POCOのみで、計算ではなく結果を保存する場合は問題ありません。
Jon Raynor 2017

3

あなたの質問に答えるために:インターフェースにゲッターしかない場合、コードの匂いを終わらせることはできません。

コードのにおいのファジーメトリックの例は、多くのメソッドを使用した肥大化したインターフェースです(ゲッターかどうかに関係なく)。それらは、インターフェース分離の原則に違反する傾向があります。

セマンティックレベルでは、単一責任の原則にも違反するべきではありません。インターフェースコントラクトで、1つの主題ではないいくつかの異なる問題が処理された場合、違反する傾向があります。これは特定するのが難しい場合があり、特定するにはある程度の経験が必要です。

反対に、インターフェースがセッターを定義しているかどうかに注意する必要があります。それはセッターが状態を変える可能性が高いからです、それが彼らの仕事です。ここで質問:インターフェイスの背後にあるオブジェクトは、有効な状態から別の有効な状態に遷移しますか?しかし、それは主にインターフェースの問題ではなく、実装オブジェクトのインターフェースコントラクトの実装です。

あなたのアプローチに関して私が唯一懸念しているのは、ORマッピングを操作することです。この小さなケースでは、これは問題ではありません。技術的には大丈夫です。しかし、私はORマップオブジェクトを操作しません。彼らはあなたが確かにそれらで行われた操作で考慮しないあまりにも多くの異なる状態を持つことができます...

ORマッピングされたオブジェクトは、(JPAを前提として)一時的、永続的なデタッチ未変更、永続的なアタッチ未変更、永続的なデタッチ変更、永続的なアタッチ変更...である可能性があります。結局、ORマップされたオブジェクトが持つ可能性のある10を超える異なる状態を特定できます。すべてのオペレーションがこれらの状態を適切に処理していますか?

インターフェースがコントラクトを定義し、実装がそれに従う場合、これは確かに問題ありません。しかし、ORマッピングされたオブジェクトについては、そのような契約を完全に満たすことができるのか、私には合理的な疑いがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.