25

我正在阅读有关 SOLID 和其他设计原则的信息。我认为 ISP 与“编程接口,而不是实现”相同。但看起来这些是不同的原则?

有区别吗?

4

6 回答 6

45

Robert Martin 在他的“UML for Java Programmers”一书中对接口隔离原则(ISP)有很好的解释。基于此,我不认为 ISP 是关于“专注于”一个合乎逻辑的、连贯的一组事物的接口。因为,这是不言而喻的;或者,至少它应该不言而喻。每个类、接口或抽象类都应该这样设计。

那么,什么是ISP?让我用一个例子来解释它。假设你有一个类 A 和一个类 B,它是类 A 的客户端。假设类 A 有十个方法,其中 B 只使用了两个。那么,B 是否需要知道 A 的所有十个方法? 可能不是——信息隐藏原理。你暴露的越多,你创造的耦合机会就越多。出于这个原因,您可以在两个类之间插入一个接口,称为 C(隔离)。该接口只会声明 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 知道超出其应有的信息。

于 2012-02-13T03:01:01.537 回答
37

ISP 专注于每个接口代表一个离散和内聚行为的想法。

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

于 2012-02-12T15:13:24.270 回答
16

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

任何实现该胖接口的类都必须为所有这些方法提供实现。某些方法可能不适用于该具体类。但它仍然必须在没有接口隔离​​原则的情况下提供实现。

让我们看一下没有 Interface segregation的示例代码。

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实现所需的方法,如Rectangle,CircleSquare. 但是在各个 Shape 子项中只需要一些方法

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

    }
}

笔记:

现在个别形状像Rectangle,Square并且Circle只实现了必需的接口并摆脱了未使用的方法。

于 2016-02-13T16:16:56.890 回答
1

同意楼上两个答案。只是举一个上面TrueWill代码味道的例子,你不应该发现自己这样做:

@Override
public void foo() {
    //Not used: just needed to implement interface
}
于 2013-05-06T09:14:57.597 回答
1
  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");
    
         }
    
    }
    

如需更完整的示例,请访问此处

于 2017-06-20T17:58:41.580 回答
0

这是此原理的真实示例(在 PHP 中)

问题陈述:

我希望各种形式的内容都有与之相关的评论/讨论。该内容可能是任何内容,从论坛主题到新闻文章,到用户的个人资料,再到对话式的私人消息。

建筑学

我们需要一个可重用的DiscussionManager类,它将 a 附加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 中使用 Traits 的一个很好的候选,因为它是值得的。

于 2014-03-18T16:43:30.227 回答