Принцип разделения интерфейса - программа для интерфейса - PullRequest
20 голосов
/ 12 февраля 2012

Я читал о SOLID и других принципах дизайна. Я думал, что ISP - это то же самое, что «Программа для интерфейса, а не для реализации». Но, похоже, это разные принципы?

Есть ли разница?

Ответы [ 6 ]

38 голосов
/ 13 февраля 2012

Роберт Мартин очень хорошо объяснил принцип разделения интерфейса (ISP) в своей книге «UML для Java-программистов».Исходя из этого, я не думаю, что ISP - это интерфейс, «сфокусированный» на одной логической, связной группе вещей.Потому что это само собой разумеется;или, по крайней мере, это должно быть само собой разумеется.Каждый класс, интерфейс или абстрактный класс должны быть разработаны таким образом.

Итак, что такое провайдер?Позвольте мне объяснить это на примере.Скажем, у вас есть класс A и класс B, который является клиентом класса A. Предположим, у класса A есть десять методов, из которых только два используют B. Теперь нужно ли B знать обо всех десяти методах A?Наверное, нет - принцип скрытия информации.Чем больше вы выставляете, тем больше у вас шансов для связи.По этой причине вы можете вставить интерфейс, называемый С, между двумя классами (разделение).Этот интерфейс будет объявлять только два метода, которые используются B, и B будет зависеть от этого интерфейса, а не напрямую от A.

Итак,

class A {
   method1()
   method2()
   // more methods
   method10()
}
class B {
    A a = new A()

}

станет

interface C {
      method1()
       method2()
}



class A implements C{
      method1()
      method2()
      // more methods
      method10()
  }
  class B {
       C c = new A()

 }   

Это не позволяет B узнать больше, чем следует.

30 голосов
/ 12 февраля 2012

Интернет-провайдер сфокусирован на идее каждого интерфейса, представляющего одно дискретное и связное поведение.

То есть каждая логическая группа действий, которые должен выполнять объект, должна отображаться на один конкретный интерфейс.Класс может захотеть сделать несколько вещей, но каждая вещь будет привязана к определенному интерфейсу, представляющему это поведение.По идее, каждый интерфейс очень сфокусирован.

9 голосов
/ 13 февраля 2016

Предположим, что у вас есть один толстый интерфейс с множеством методов для реализации.

Любой класс, который реализует этот толстый интерфейс, должен обеспечивать реализацию для всех этих методов. Некоторые из методов могут быть неприменимы к этому конкретному классу. Но все же это должно обеспечить реализацию в отсутствие принципа разделения интерфейса.

Давайте рассмотрим пример кода в отсутствии сегрегации интерфейса .

interface Shape{
    public int getLength();
    public int getWidth();
    public int getRadius();
    public double getArea();
}

class Rectangle implements Shape{
    int length;
    int width;
    public Rectangle(int length, int width){
        this.length = length;
        this.width = width;
    }
    public int getLength(){
        return length;
    }
    public int getWidth(){
        return width;
    }
    public int getRadius(){
        // Not applicable
        return 0;
    }
    public double getArea(){
        return width * length;
    }
}
class Square implements Shape{
    int length;

    public Square(int length){
        this.length = length;
    }
    public int getLength(){
        return length;
    }
    public int getWidth(){
        // Not applicable
        return 0;
    }
    public int getRadius(){
        // Not applicable
        return 0;
    }
    public double getArea(){
        return length * length;
    }
}

class Circle implements Shape{
    int radius;
    public Circle(int radius){
        this.radius = radius;
    }
    public int getLength(){
        // Not applicable
        return 0;
    }
    public int getWidth(){
        // Not applicable
        return 0;
    }
    public int getRadius(){
        return radius;
    }
    public double getArea(){
        return 3.14* radius * radius;
    }
}

public class InterfaceNoSeggration{
    public static void main(String args[]){
        Rectangle r = new Rectangle(10,20);
        Square s = new Square(15);
        Circle c = new Circle(2);
        System.out.println("Rectangle area:"+r.getArea());
        System.out.println("Square area:"+s.getArea());
        System.out.println("Circle area:"+c.getArea());

    }
}

выход: * +1010 *

java InterfaceNoSeggration
Rectangle area:200.0
Square area:225.0
Circle area:12.56

Примечания:

  1. Shape - это толстый интерфейс общего назначения, который содержит методы, необходимые для всех реализаций Shape, таких как Rectangle, Circle и Square. Но только некоторые методы необходимы в соответствующих Shape childs

     Rectangle : getLength(), getWidth(), getArea()
     Square    : getLength() and getArea()
     Circle    : getRadius() and getArea()
    
  2. В отсутствие сегрегации все Shapes реализовали весь толстый интерфейс: Shape.

Мы можем добиться того же результата с принципом разделения интерфейса, если изменим код следующим образом.

interface Length{
    public int getLength();
}
interface Width{
    public int getWidth();
}
interface Radius{
    public int getRadius();
}
interface Area {
    public double getArea();
}


class Rectangle implements Length,Width,Area{
    int length;
    int width;
    public Rectangle(int length, int width){
        this.length = length;
        this.width = width;
    }
    public int getLength(){
        return length;
    }
    public int getWidth(){
        return width;
    }
    public int getRadius(){
        // Not applicable
        return 0;
    }
    public double getArea(){
        return width * length;
    }
}
class Square implements Length,Area{
    int length;

    public Square(int length){
        this.length = length;
    }
    public int getLength(){
        return length;
    }
    public int getWidth(){
        // Not applicable
        return 0;
    }
    public int getRadius(){
        // Not applicable
        return 0;
    }
    public double getArea(){
        return length * length;
    }
}

class Circle implements Radius,Area{
    int radius;
    public Circle(int radius){
        this.radius = radius;
    }
    public int getLength(){
        // Not applicable
        return 0;
    }
    public int getWidth(){
        // Not applicable
        return 0;
    }
    public int getRadius(){
        return radius;
    }
    public double getArea(){
        return 3.14* radius * radius;
    }
}

public class InterfaceSeggration{
    public static void main(String args[]){
        Rectangle r = new Rectangle(10,20);
        Square s = new Square(15);
        Circle c = new Circle(2);
        System.out.println("Rectangle area:"+r.getArea());
        System.out.println("Square area:"+s.getArea());
        System.out.println("Circle area:"+c.getArea());

    }
}

Примечания:

Теперь отдельные Shapes, такие как Rectangle, Square и Circle, реализовали только необходимые интерфейсы и избавились от неиспользуемых методов.

1 голос
/ 20 июня 2017
  1. Интерфейс IWorker:

    public interface IWorker {
        public void work();
        public void eat();
    
    }
    
  2. Класс разработчика:

    public class Developer implements IWorker {
    
         @Override
         public void work() {
               // TODO Auto-generated method stub
               System.out.println("Developer working");
    
         }
    
         @Override
         public void eat() {
               // TODO Auto-generated method stub
               System.out.println("developer eating");
    
         }
    
    }
    
  3. Класс робота:

    public class Robot implements IWorker {
    
         @Override
         public void work() {
               // TODO Auto-generated method stub
               System.out.println("robot is working");
    
         }
    
         @Override
         public void eat() {
               // TODO Auto-generated method stub
               throw new UnsupportedOperationException("cannot eat");
    
         }
    
    }
    

Для более полного примера перейдите здесь .

1 голос
/ 06 мая 2013

Согласен с обоими ответами выше. Просто чтобы привести пример запаха кода TrueWill , приведенного выше, вам не следует делать следующее:

@Override
public void foo() {
    //Not used: just needed to implement interface
}
0 голосов
/ 18 марта 2014

Вот реальный пример этого принципа (в PHP)

Постановка задачи:

Я хочу, чтобы различные формы контента имели комментарии / обсуждения, связанные сих.Это может быть что угодно: от темы на форуме, до новостной статьи, от профиля пользователя до личного сообщения в стиле разговора.

Архитектура

Нам понадобитсяповторно используемый класс DiscussionManager, который присоединяет Discussion к данному объекту содержимого.Тем не менее, приведенные выше четыре примера (и многие другие) концептуально различны.Если мы хотим, чтобы DiscussionManager использовал их, то у всех четырех + должен быть один общий интерфейс, которым они все пользуются.У DiscussionManager нет другого способа использовать их, если вы не хотите, чтобы ваши аргументы были обнажены (например, без проверки типов).

Решение: Discussable взаимодействует с этими методами:

  • attachDiscussion($topic_id)
  • detachDiscussion()
  • getDiscussionID()

Тогда DiscussionManager может выглядеть так:

class DiscussionManager
{
    public function addDiscussionToContent(Discussable $Content)
    {
        $Discussion = $this->DiscussionFactory->make( ...some data...);
        $Discussion->save() // Or $this->DiscussionRepository->save($Discussion);
        $Content->attachDiscussion($Discussion->getID()); // Maybe saves itself, or you can save through a repository
    }

    public function deleteDiscussion(Discussable $Content)
    {
        $id = $Content->getDiscussionID();
        $Content->detatchDiscussion();
        $this->DiscussionRepository->delete($id);
    }

    public function closeDiscussion($discussion_id) { ... }
}

Таким образом, DiscussionManager не заботится ни о каком несвязанном поведении различных типов контента, которые он использует.Он заботится только о поведении, в котором нуждается, независимо от того, с чем связано это поведение.Таким образом, предоставляя каждому типу контента, для которого вы хотите обсудить, интерфейс Discussable, вы используете принцип разделения интерфейса.

Это также хороший пример ситуации, когда абстрактный базовый класс не являетсяхорошая идея.Тема форума, профиль пользователя и новостная статья не являются даже концептуально отдаленными, поэтому попытка заставить их наследовать поведение при обсуждении приводит к странной связи с неродственным родителем.Используя определенный интерфейс, представляющий обсуждения, вы можете убедиться, что объекты, которые вы хотите обсуждать, совместимы с клиентским кодом, который будет управлять этими обсуждениями.

Этот пример также может быть хорошим кандидатом на использование черт в PHP, хотя и стоит.

...