2012-02-12 71 views

回答

26

ISP专注于每个界面的思想,代表一种离散和内聚的行为。

也就是说,对象应该做的每个逻辑组都应该映射到一个特定的接口。一个类可能想做几件事,但每件事都会映射到一个表示该行为的特定接口。这个想法是每个界面非常集中。

+1

短和点 – Gordon 2012-02-12 15:15:47

+1

哇..谢谢!不能接受另外5分钟:) – hangar18 2012-02-12 15:19:03

+3

+1。经典的“嗅觉”告诉你你没有遵循这个原则,这是一个客户端消费(取决于)一个接口,客户端只调用接口方法的一个子集。 – TrueWill 2012-02-12 18:36:10

32

Robert Martin在他的书“UML for Java程序员”一书中对接口隔离原理(ISP)有很好的解释。基于此,我不认为ISP是关于一个界面“聚焦”在一个逻辑上一致的事物上。因为,那是不言而喻的;或者,至少它应该不用说。每个类,接口或抽象类都应该这样设计。

那么,什么是ISP?让我用一个例子来解释它。也就是说,你有一个A类和一个B类,它是A类的客户。假设A类有十个方法,其中只有两个被B使用。现在,B是否需要知道A的所有十个方法?可能不是 - 信息隐藏的原则。你暴露得越多,你创造的机会就越多。出于这个原因,你可以在两个类之间插入一个接口,称之为C(隔离)。该接口将只宣布由D所使用的两种方法,和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知道更多的东西。

+0

+1为了抽出时间做详细的解释! – hangar18 2012-02-13 14:04:57

+0

优秀的答案! – 2012-07-23 17:26:11

+1

我认为@PeteStensønes的解释比我所做的更为一般和精确。我建议人们更加关注他的定义。虽然不一定是错误的,但我从一个使用案例的角度解释了这个概念,但却忽略了它的一般观点。 – 2012-12-24 17:01:07

1

同意以上两个答案。只是为了得到TrueWill的代码味道的上面的例子,你不应该觉得自己这样做:

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

与只引发NotImplementedException的Java API类比较,特别是在集合中。适度使用这可能对于界面中的部分使用的方法很有用,但是例如使用MutableMap扩展Map也可以是替代方案。 – hexafraction 2013-09-30 00:18:40

0

下面是这一原则的真实世界的例子(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不关心它使用的各种内容类型的任何不相关的行为。它只关心它需要的行为,而不管这些行为与什么关联。因此,通过为每个需要讨论的内容类型提供一个接口,您正在使用接口隔离原则。

这也是抽象基类不是一个好主意的情况的一个很好的例子。论坛主题,用户档案和新闻文章甚至在概念上都不是同一件事,因此试图让他们继承讨论行为会导致与无关父母的奇怪耦合。使用代表讨论的特定界面,您可以确保您希望进行讨论的实体与将管理这些讨论的客户端代码兼容。

这个例子也可能是在PHP中使用Traits的一个很好的选择,因为它是值得的。

7

假设你有一个胖接口,有许多要实现的方法。

任何实现胖接口的类都必须为所有这些方法提供实现。有些方法可能不适用于那个具体的类。但仍然需要在没有界面分离原则的情况下提供实施。

让我们来看看中没有接口隔离​​的示例代码

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是一个通用的脂肪接口,其中包含的所有Shape实现比如RectangleCircleSquare所需的方法。但需要在各自的外形只有一些方法童车

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

    } 
} 

注:

现在单个形状像RectangleSquareCircle已经实现了,才需要接口和摆脱未使用的方法。

+0

我投了-1这个,但实际上这是一个错误,我喜欢这个答案:D – 2016-08-19 13:16:51

+0

我很惊讶关于投票与意见。如果系统允许,你可以纠正它。否则,你必须在24小时后来:) :) – 2016-08-19 13:25:52

+0

我有这样的消息:“”你最后投了这个答案3小时前。 “”,所以我想你必须更新信息,但是如果我可以在24小时内来到,那么我将删除这个downvote:D – 2016-08-19 13:27:31

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

有关更完整的示例,请转至here