Является ли принцип разделения интерфейса только заменой принципа единой ответственности?
Является ли принцип разделения интерфейса только заменой принципа единой ответственности?
Я думаю, что если мой класс выполняет SRP, нет необходимости извлекать более одного интерфейса.
Таким образом, ISP выглядит как решение на случай, если нам по какой-то причине придется сломать SRP.
Я прав?
3 ответа
Нет. Возьмем пример класса, в обязанности которого входит сохранение данных, например, о жестком диске. Разделение класса на части для чтения и записи не имеет практического смысла. Но некоторые клиенты должны использовать класс только для чтения данных, некоторые клиенты только для записи данных, а некоторые - для обоих. Применение ISP здесь с тремя различными интерфейсами было бы хорошим решением.
Я думаю, что если мой класс выполняет SRP, нет необходимости извлекать более одного интерфейса.
Принцип единой ответственности состоит в том, что у класса (или метода) не должно быть более одной причины для изменения (т.е. каждая отвечает только за одну функцию). Чтобы почтить это, вы создадите новые классы по мере развития вашей системы.
Например, если вы начнете с Car
и узнайте, что вам нужна функциональность для переключения передач, вы извлечете это в Gearbox
учебный класс. Это означает, что если вы меняете механизм переключения передач, родитель Car
класс не должен меняться. Если вы добавите усилитель рулевого управления в свой автомобиль, вы снова извлечете его в свой класс. Радио было бы другим классом.
Этот каскад абстракции будет происходить на протяжении всего вашего Car
учебный класс. Как вы двигаетесь от Car
вниз, вы обнаружите, что детали увеличиваются в каждом классе - например, в то время как Car
класс может иметь changeGear()
метод, позволяющий пользователю выбрать передачу для включения, Gearbox
класс позаботится о том, чтобы это произошло (например, выжмите сцепление, отключите текущую передачу, выберите новую передачу и т. д.)
Однако с ОО-дизайном мы не хотим раскрывать детали нашего Gearbox
для конечного пользователя - мы хотим, чтобы они взаимодействовали с нашей системой на высоком уровне абстракции, без необходимости знать, как работают внутренние компоненты. Мы также хотим ограждать эти внутренние компоненты, чтобы мы могли изменить их в будущем, не требуя от пользователей рефакторинга их кода (поэтому мы отметили их как private
или же protected
).
Из-за этого мы позволяем пользователям взаимодействовать с нашей машиной только через Car
сам класс. Вот тут-то и вступает в силу принцип разделения интерфейсов. SRP гарантирует, что Car
класс делегирует свои подкомпоненты различным классам, но все наши public
методы по-прежнему будут вызываться через Car
сам класс. ISP гарантирует, что вместо того, чтобы объединять все это в одном интерфейсе, мы вместо этого создаем логические различия и выставляем несколько интерфейсов для связанной функциональности.
Нет.
Класс может реализовывать несколько интерфейсов, но он должен реализовывать только методы, применимые к нему.
Предположим, что у вас есть 10+ различных возможностей, таких как Climb, Think, Learn, Apply
, Учебный класс Dog
может иметь 2 возможности и класс Cat
может иметь 2 возможности и класс Man
может иметь 6 возможностей. Имеет смысл реализовать только применимые возможности в соответствующих классах.
Посмотрите на этот код.
public class ISRDemo{
public static void main(String args[]){
Dog dog = new Dog("Jack",16);
System.out.println(dog);
Learn dl = dog;
dl.learn();
ProtectOwner p = dog;
p.protectOwner();
Cat cat = new Cat("Joe",20);
System.out.println(cat);
Climb c = cat;
c.climb();
Remember r = cat;
cat.doRemember();
Man man = new Man("Ravindra",40);
System.out.println(man);
Think t = man;
t.think();
Learn l = man;
l.learn();
Apply a = man;
a.apply();
PlaySports pm = man;
pm.playSports();
Remember rm = man;
rm.doRemember();
}
}
class Dog implements Learn,ProtectOwner{
private String name;
private int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public void learn(){
System.out.println(this.getClass().getSimpleName()+ " can learn");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " can protect owner");
}
public String toString(){
return "Dog :"+name+":Age:"+age;
}
}
class Cat implements Climb,Remember {
private String name;
private int age;
public Cat(String name,int age){
this.name = name;
this.age = age;
}
public void climb(){
System.out.println(this.getClass().getSimpleName()+ " can climb");
}
public void doRemember(){
System.out.println(this.getClass().getSimpleName()+ " can remember");
}
public String toString(){
return "Cat :"+name+":Age:"+age;
}
}
interface ProtectOwner {
public void protectOwner();
}
interface Remember{
public void doRemember();
}
interface Climb{
public void climb();
}
interface Think {
public void think();
}
interface Learn {
public void learn();
}
interface Apply{
public void apply();
}
interface PlaySports{
public void playSports();
}
class Man implements Think,Learn,Apply,PlaySports,Remember{
String name;
int age;
public Man(String name,int age){
this.name = name;
this.age = age;
}
public void think(){
System.out.println(this.getClass().getSimpleName() + " can think");
}
public void learn(){
System.out.println(this.getClass().getSimpleName() + " can learn");
}
public void apply(){
System.out.println(this.getClass().getSimpleName() + " can apply");
}
public void playSports(){
System.out.println(this.getClass().getSimpleName() + " can play sports");
}
public void doRemember(){
System.out.println(this.getClass().getSimpleName() + " can remember");
}
public String toString(){
return "Man :"+name+":Age:"+age;
}
}
выход:
java ISRDemo
Dog :Jack:Age:16
Dog can learn
Dog can protect owner
Cat :Joe:Age:20
Cat can climb
Cat can remember
Man :Ravindra:Age:40
Man can think
Man can learn
Man can apply
Man can play sports
Man can remember
В вышеприведенном примере сегрегация интерфейса рекомендует определять 10 возможностей в 10 интерфейсах вместо объявления их всех в толстом интерфейсе. Но это не значит, что вам нужны разные классы, чтобы соответствовать единым критериям ответственности.
Посмотрите на реализацию Dog, Cat and Man
классы в том же примере.