Поскольку принцип сегрегации интерфейса предполагает, что ни одного клиента не следует заставлять зависеть от методов, которые он не использует, поэтому клиент не должен реализовывать пустой метод для своих методов интерфейса, в противном случае этот метод интерфейса должен быть помещен в другой интерфейс.
Но как насчет конкретных методов? Должен ли я разделить методы, которые не каждый клиент будет использовать? Рассмотрим следующий класс:
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. Итак, мой вопрос: в соответствии с принципом сегрегации интерфейса, я должен отделить конкретные методы, которые будут использовать не все клиенты, чтобы эти методы зависели от клиента только тогда, когда клиент фактически использует, чтобы уменьшить связь?
источник
Car
классе, о котором вы не хотите, чтобы (все) пользователи знали, тогда создайте (более одного) интерфейс, которыйCar
класс реализует, который объявляет только методы, полезные в контексте интерфейсов.Ответы:
В вашем примере
CarShop
это не зависит отisQualityPass
метода и не заставляет его создавать пустую реализацию. Там даже не задействован интерфейс. Так что термин «провайдер» здесь просто не подходит. И пока такой метод, какisQualityPass
метод, который хорошо вписывается вCar
объект, не перегружая его дополнительными обязанностями или зависимостями, это нормально. Нет необходимости реорганизовывать открытый метод класса в другое место только потому, что существует один клиент, не использующий этот метод.Тем не менее, создание доменного класса наподобие
Car
прямой зависимости от чего-то подобногоHttpClient
, вероятно, также не очень хорошая идея, независимо от того, какие клиенты используют или не используют метод. Перемещение логики в отдельный классCheckCarQualityPass
просто не называется «ISP», это называется «разделение интересов» . Задача многоразового автомобильного объекта, вероятно, не должна заключаться в том, чтобы делать какие-либо внешние HTTP-вызовы, по крайней мере, не напрямую, это ограничивает возможность повторного использования и, кроме того, слишком сильно проверяемость.Если
isQualityPass
не может быть легко перемещен в другой класс, альтернативой было бы сделатьHttp
вызовы через абстрактный интерфейс,IHttpClient
который вводится воCar
время создания, или путем внедрения всей стратегии проверки «QualityPass» (с инкапсулированным запросом Http) вCar
объект , Но это ИМХО только второе лучшее решение, поскольку оно увеличивает общую сложность, а не уменьшает ее.источник
Car
объекту. Это не было бы моим первым выбором для решения (по крайней мере, не в контексте этого надуманного примера). Тем не менее, это может иметь больше смысла в «реальном» коде, я не знаю.Принцип разделения интерфейса не запрещает доступ к тому, что вам не нужно. Речь идет о том, чтобы не настаивать на доступе к тому, что вам не нужно.
Интерфейсы не принадлежат классу, который их реализует. Они принадлежат объектам, которые их используют.
То, что используется здесь, это
getTax()
иgetCost()
. На чем настаивают все доступно черезCar
. Проблема состоит в том, чтобы настаивать на том,Car
что он настаивает на доступе,isQualityPass()
который не нужен.Это можно исправить. Вы спрашиваете, можно ли это исправить конкретно. Оно может.
Ни один из этого кода даже не знает,
CarLiability
является ли интерфейс или конкретным классом. Это хорошая вещь. Это не хочет знать.Если это интерфейс,
Car
может реализовать его. Это не будет нарушать ISP , потому что дажеisQuality()
вCar
CarShop
не настаивает на этом. Это хорошо.Если это конкретно, это может быть то, что
isQuality()
либо не существует, либо был перенесен в другое место. Это хорошо.Возможно также, что
CarLiability
это конкретная оболочка,Car
которая делегирует ему работу. До тех пор, покаCarLiability
не разоблачить,isQuality()
тоCarShop
все в порядке. Конечно, это просто пинает банку в будущем иCarLiability
должно выяснить, как следовать за провайдером такCar
же, как этоCarShop
должно было быть.Короче говоря,
isQuality()
не нужно удалять из-Car
за провайдера. Подразумеваемая потребность вisQuality()
потребности должна быть удалена,CarShop
потому чтоCarShop
она не нужна, поэтому она не должна просить об этом.источник
На самом деле, нет. Есть разные способы спрятаться
Car.isQualityPass
отCarShop
.1. Модификаторы доступа
С точки зрения закона Деметры , мы могли бы подумать,
Car
аCardShop
не дружить . Это позволяет нам делать следующее.Помните, что оба компонента находятся в разных пакетах. Теперь
CarShop
не имеет видимости надCar
защищенным поведением. (Извините, если приведенный выше пример выглядит так упрощенно).2. Разделение интерфейса
ISP работает из того , что мы работаем с абстракциями, а не с конкретными классами. Я предполагаю, что вы уже знакомы с реализацией ISP и с интерфейсами ролей .
Несмотря на фактическую
Car
реализацию, ничто не мешает нам практиковать ISP.Что я здесь сделал Я сузил взаимодействие между
Car
иCarShop
через роль интерфейса Billable . Будьте в курсе изменений наgetPrice
подписи. Я намеренно изменил аргумент. Я хотел сделать очевидным, чтоCarShop
он привязан только к одному из доступных интерфейсов ролей . Я мог бы следить за фактической реализацией, но я не знаю реальных деталей реализации, и я боюсь, что у фактическогоgetPrice(String carId)
есть доступ (видимость) к конкретному классу. Если это так, вся работа, проделанная с провайдером, становится бесполезной, потому что разработчик может выполнять кастинг и работать только с интерфейсом Billable . Неважно, насколько мы методичны, искушение всегда будет.3. Единственная ответственность
Я боюсь , что я не в состоянии сказать , если зависимость между
Car
иHttpClient
адекватен, но я согласен с @DocBrown, это вызывает некоторые предупреждения , что стоит обзор дизайна. Ни Закон Деметры, ни Интернет-провайдер не сделают ваш дизайн «лучше» на этом этапе. Они просто замаскируют проблему, а не исправят ее.Я предложил DocBrown шаблон стратегии в качестве возможного решения. Я согласился с ним, что шаблон добавляет сложности, но я также думаю, что любой редизайн будет. Это компромисс: чем больше мы хотим отделить, тем больше у нас движущихся частей (обычно). Во всяком случае, я думаю, что оба согласны с редизайном очень рекомендуется.
Подводя итоги
Нет, вам не нужно перемещать конкретные методы во внешние классы, потому что не делайте их доступными. Там может быть бесчисленное множество потребителей. Будете ли вы переносить все конкретные методы во внешние классы каждый раз, когда в игру вступает новый потребитель ? Я надеюсь, что ты не.
источник