Является ли принцип разделения интерфейса только заменой принципа единой ответственности?

Является ли принцип разделения интерфейса только заменой принципа единой ответственности?

Я думаю, что если мой класс выполняет 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 классы в том же примере.

Другие вопросы по тегам