インターフェース分離の原則は具体的な方法に適用されますか?


10

インターフェース分離の原則は、クライアントが使用しないメソッドに依存することを強制されるべきではないことを示唆しているので、クライアントはそのインターフェースメソッドに空のメソッドを実装すべきではありません。

しかし、具体的な方法はどうですか?すべてのクライアントが使用するわけではない方法を分離する必要がありますか?次のクラスを考えてみましょう:

public class Car{
    ....

    public boolean isQualityPass(){
        ...
    }

    public int getTax(){
        ...
    }

    public int getCost(){
        ...
    }
}

public class CarShop{
    ...
    public int getCarPrice(int carId){
        Car car=carList[carId];
        int price=car.getTax() + car.getCost()... (some formula);
        return price;
    }
}

上記のコードでは、CarShopはisQualityPass()を新しいクラスに分離する必要がある場合、CarでisQualityPass()メソッドをまったく使用しません。

public class CheckCarQualityPass{
    public boolean isQualityPass(Car car){
    }
}

CarShopの結合を減らすために?isQualityPass()が追加の依存関係を必要とするかどうか、一度考えると、

public boolean isQualityPass(){
    HttpClient client=...
}

CarShopは、実際にはHttpClientを使用しない場合でも、HttpClientに依存します。だから私の質問は:インターフェース分離の原則に従って、すべてのクライアントが使用するわけではない具体的なメソッドを分離して、結合を減らすために、クライアントが実際に使用する場合にのみクライアントに依存するようにすべきですか?


2
通常、車は「品質」を合格したことを知っていますか?それとも、それ自体でカプセル化できるビジネスルールなのでしょうか。
Laiv

2
ISPのインターフェイスという言葉が意味するように、それはインターフェイスについてです。したがって、Car(すべての)ユーザーに知られたくないクラスのメソッドがある場合、クラスが実装する(複数の)インターフェースを作成Carし、インターフェースコンテキストで有用なメソッドのみを宣言します。
ティモシートラックル2017年

@Laivきっとそれ以上のことをもっとよく知っている車両が近いうちに見られると確信しています。;)
統合モデリングサンドイッチ

1
車はそのメーカーが彼に知ってほしいことを知っています。フォルクスワーゲンは私が話していることを知っています:-)
Laiv

1
インターフェースについて言及していますが、この例にはインターフェースがありません。Carをインターフェイスに変えることと、そのインターフェイスに含めるメソッドについて話しているのでしょうか。
Neil

回答:


6

あなたの例でCarShopは、はに依存せずisQualityPass、メソッドの空の実装を強制することもありません。関連するインターフェースすらありません。したがって、「ISP」という用語は単にここでは一致しません。そして、のようなisQualityPassメソッドがCarオブジェクトにうまくフィットするメソッドである限り、追加の責任や依存関係でそれを過度に負担することなく、これは問題ありません。メソッドを使用していないクライアントが1つ存在するからといって、クラスのパブリックメソッドを別の場所にリファクタリングする必要はありません。

ただし、ドメインクラスをのようなものにCar直接依存するようにするHttpClientことも、どのクライアントがメソッドを使用するかどうかに関係なく、おそらく良いアイデアではありません。ロジックを別のクラスに移動することCheckCarQualityPassは、単に「ISP」と呼ばれるのではなく、「懸念の分離」と呼ばれます。再利用可能なcarオブジェクトの懸念は、おそらく少なくとも直接ではなく、外部HTTP呼び出しを行うことであってはなりません。これにより、再利用性が制限され、さらにテスト機能が制限されます。

isQualityPass別のクラスに簡単に移動できない場合の代替方法は、構築時に注入されるHttp抽象インターフェースIHttpClientを介してCar、または「QualityPass」チェック戦略全体(Httpリクエストをカプセル化して)をCarオブジェクトに注入することです。 。しかし、それはIMHOであり、2番目に最適なソリューションです。これは、全体的な複雑さを減らすのではなく増加させるためです。


isQualityPassメソッドを解決するための戦略パターンはどうですか?
Laiv

@Laiv:技術的には、これは確かに機能しますが(私の編集を参照)、より複雑なCarオブジェクトになります。それは私のソリューションの最初の選択ではありません(少なくともこの不自然な例のコンテキストでは)。しかし、それは「実際の」コードでもっと意味があるかもしれません、私は知りません。
ドクターブラウン

6

だから私の質問は:インターフェース分離の原則に従って、すべてのクライアントが使用するわけではない具体的なメソッドを分離して、結合を減らすために、クライアントが実際に使用する場合にのみクライアントに依存するようにすべきですか?

インターフェイス分離の原則は、不要なものへのアクセスを禁止することではありません。それはあなたが必要のないものへのアクセスを主張しないことについてです。

インターフェイスは、それらを実装するクラスによって所有されていません。それらは、それらを使用するオブジェクトによって所有されています。

public class CarShop{
    ...
    public int getCarPrice(int carId){
        Car car=carList[carId];
        int price=car.getTax() + car.getCost()... (some formula);
        return price;
    }
}

ここで使用されているのはgetTax()getCost()です。主張されているのは、を通じてアクセスできるすべてのものCarです。問題はCarisQualityPass()それが必要とされていないものへのアクセスを主張していることを意味することを主張しています。

これは修正できます。具体的に直せますか?できる。

public class CarShop{
    ...
    public int getCarPrice(int carId){
        CarLiability carLiability=carLiabilityList[carId];
        int price=carLiability.getTax() + carLiability.getCost()... (some formula);
        return price;
    }
}

そのコードはどれもCarLiability、インターフェースであるか具象クラスであるかを知りません。よかったです。知りたくない。

インターフェースの場合、それをCar実装する可能性があります。これはISPに違反することisQuality()はありCar CarShopません。これで結構です。

具体的にisQuality()は、存在しないか、別の場所に移動された可能性があります。これで結構です。

またCarLiabilityCar作業を委任する具体的なラッパーである可能性もあります。限りCarLiability公開しませんisQuality()その後、CarShop罰金です。もちろん、これは缶詰になるだけCarLiabilityCar、ISPをフォローする方法と同じ方法でISPをフォローする方法を見つける必要がありCarShopます。

つまり、ISPのためにisQuality()から削除する必要はありませんCar。暗黙の必要性はそれを必要としないのでisQuality()削除する必要があるので、それを要求するべきではありません。CarShopCarShop


4

インターフェース分離の原則は具体的な方法に適用されますか?

しかし、具体的な方法はどうですか?すべてのクライアントが使用するわけではない方法を分離する必要がありますか?

あんまり。非表示にするには、さまざまな方法がありますCar.isQualityPassからはCarShop

1.アクセス修飾子

デメテルの法則の視点、私たちは考えることができCar、およびCardShopではないと友人。次のことをすることが合法です。

package com.my.package.domain.model
public class Car{
    ...
    protected boolean isQualityPass(){...}
}

package com.my.package.domain.services
public class CarShop{
    ...
}

両方のコンポーネントが異なるパッケージにあることに注意してください。さてCarShopに対して何らの視認性がないCar 保護行動を。(上記の例が非常に単純に見える場合は、事前に失礼します)。

2.インターフェイスの分離

ISPは、私たちが抽象化ではなく、具体的なクラスで動作することを前提に動作します。あなたはすでにISPの実装と役割のインターフェースに精通していると思います

実際のCar実装にもかかわらず、ISPの実践を妨げるものは何もありません。

//role interfaces 
public interface Billable{
   public int getCosts();
   public int getTaxs();
}

//role interfaces
public interface QualityAssurance{
   public boolean isQualityPass();
}

public class Car implements Billable, QualityAssurance{
   ...
}

public class CarShop {
  ...
  public int getPrice(Billable billable){
     return billable.getCosts() * billable.getTaxs();
  }
}

私がここでやったこと。私は間の相互作用絞られているCarCarShop通じ請求可能なインターフェース役割をgetPrice署名の変更に注意してください。私は意図的に議論を修正しました。それCarShopが、利用可能な役割インターフェースの 1つにのみ「結び付いている」ことを明らかにしたかったのです。実際の実装を追跡することはできましたが、実際の実装の詳細はわかりませんgetPrice(String carId)。具体的なクラスへの実際のアクセス(可視性)が心配です。もしそうなら、ISPで行われたすべての作業は役に立たなくなります。なぜなら、キャストを行い、インターフェースBillableでのみ機能するのは開発者の手にあるからです。私たちがどんなに礼儀正しいとしても、誘惑は常にそこにあります。

3.単一の責任

私は間の依存関係ならば、私が言う立場にないんだけど怖いCarとはHttpClient十分ですが、私はそれは価値のあるAのデザインレビューといういくつかの警告を発生させ、@DocBrownに同意します。この時点では、デメテルの法則もISPもあなたのデザインを「より良く」することはできません。彼らは問題をマスクするだけで、修正はしません。

可能な解決策として、DocBrown 戦略パターンを提案しました。このパターンは複雑さを増すと彼に同意しましたが、どんな再設計でもそうなると思います。それはトレードオフであり、必要なデカップリングが多ければ多いほど、(通常は)可動部分が多くなります。とにかく、どちらも再設計に同意することを強くお勧めします。

まとめ

いいえ、アクセスできないようにするために具象メソッドを外部クラスに移動する必要はありません。無数の消費者がいる可能性があります。新しいコンシューマーが登場するたびに、すべての具体的なメソッドを外部クラスに移動しますか?私はあなたがそうしないことを望みます。

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