Принцип сегрегации интерфейса-программа для интерфейса

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

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

6 ответов


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

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


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

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

Итак,

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 знать больше, чем следует.


предположим, что у вас есть один интерфейс fat со многими методами, которые будут реализованы.

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

давайте посмотрим на пример кода отсутствие интерфейса сегрегация!--18-->.

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());

    }
}

выход:

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

Примечания:

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

     Rectangle : getLength(), getWidth(), getArea()
     Square    : getLength() and getArea()
     Circle    : getRadius() and getArea()
    
  2. в отсутствии сегрегации, все формы снабжали весь жирный интерфейс : Форма.

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

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());

    }
}

Примечания:

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


согласен с ответами выше. Просто чтобы привести пример TrueWillкод запаха выше, вы не должны найти себя делать это:

@Override
public void foo() {
    //Not used: just needed to implement interface
}

вот реальный пример этого принципа (в 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 не заботится ни о каком несвязанном поведении различных типов контента, которые он использует. Он заботится только о поведении, которое ему нужно, независимо от того, с чем связано это поведение. Поэтому, предоставляя каждый тип контента, который вы хотите обсудить, a Discussable интерфейс, вы использование принципа сегрегации интерфейса.

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

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


  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");
    
         }
    
    }
    

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