当前位置: 首页 > 知识库问答 >
问题:

在接口级别解耦两个类意味着什么?

笪健
2023-03-14

假设A包中有A类,B包中有B类。如果类A的对象引用了类B,那么这两个类之间就称为耦合。

为了解决耦合问题,建议在包A中定义一个接口,该接口由包B中的类实现。然后,类A的对象可以引用包A中的接口。这通常是《依赖倒置》中的一个例子。

这是不是“在接口层解耦两个类”的例子,如果是,它是如何去除类之间的耦合,并在两个类耦合时保留相同的功能的?

共有3个答案

程彭祖
2023-03-14

您描述的情况消除了类A对类B的特定实现的依赖,并将其替换为接口。现在,类A可以接受实现接口的任何类型的对象,而不是只接受类B。设计保留了相同的功能,因为类B用于实现该接口。

卢枫涟
2023-03-14

假设B的功能是向某个数据库写入日志。类B取决于类DB的功能,并为其与其他类的日志功能提供一些接口。

ClassA需要B的日志记录功能,但不关心日志写入的位置。它不关心DB,但因为它依赖于B,所以它也依赖于DB。这不是很理想。

因此,您可以做的是将类B分为两个类:一个抽象L,描述日志功能(不依赖于DB),另一个实现依赖于DB

然后,您可以将类A与B解耦,因为现在A将只依赖于L。

由于A现在只依赖于精益L,因此您可以轻松地将其与其他日志机制结合使用,而不依赖于DB。例如,您可以创建一个简单的基于控制台的记录器,实现L中定义的接口。

但是由于现在A不依赖于B,而是(在源代码中)在运行时只依赖于抽象接口L,因此必须设置它来使用L(例如B)。因此,需要有其他人告诉A在运行时使用B(或其他东西)。这被称为控制反转,因为在A决定使用B之前,但现在有人(例如容器)告诉A在运行时使用B

葛骏
2023-03-14

让我们创建一个虚构的例子,介绍两个类aB

包装packageA中的类别A

package packageA;

import packageB.B;

public class A {
    private B myB;
    
    public A() {
        this.myB = new B();
    }
    
    public void doSomethingThatUsesB() {
        System.out.println("Doing things with myB");
        this.myB.doSomething();
    }
}

包中的类B

package packageB;

public class B {
    public void doSomething() {
        System.out.println("B did something.");
    }
}

正如我们所看到的,A依赖于B。如果没有BA就不能使用。我们说AB紧密耦合。如果我们将来想用一个更好的B来代替B呢?为此,我们在packageA中创建了一个接口Inter

package packageA;

public interface Inter {
    public void doSomething();
}

为了利用这个接口,我们

  • import packageA。国际米兰并让B在B

结果是A的修改版本:

package packageA;

public class A {
    private Inter myInter;
    
    public A() {
        this.myInter = ???; // What to do here?
    }
    
    public void doSomethingThatUsesInter() {
        System.out.println("Doing things with myInter");
        this.myInter.doSomething();
    }
}

我们已经可以看到,从AB的依赖关系消失了:导入包B。B 不再需要。只有一个问题:我们不能实例化接口的实例。但控制反转起到了解救作用:在A的构造函数中,构造函数不会实例化Inter类型的东西,而是要求实现Inter的东西作为参数:

package packageA;

public class A {
    private Inter myInter;
    
    public A(Inter myInter) {
        this.myInter = myInter;
    }
    
    public void doSomethingThatUsesInter() {
        System.out.println("Doing things with myInter");
        this.myInter.doSomething();
    }
}

通过这种方法,我们现在可以随意更改AInter的具体实现。假设我们编写一个新类BetterB

package packageB;

import packageA.Inter;

public class BetterB implements Inter {
    @Override
    public void doSomething() {
        System.out.println("BetterB did something.");
    }
}

现在我们可以使用不同的内部实现来实例化As:

Inter b = new B();
A aWithB = new A(b);
aWithB.doSomethingThatUsesInter();

Inter betterB = new BetterB();
A aWithBetterB = new A(betterB);
aWithBetterB.doSomethingThatUsesInter();

我们不需要在A中更改任何内容。代码现在是解耦的,只要满足Inter的合同,我们可以随意更改Inter的具体实现。最值得注意的是,我们可以支持将来编写并实现Inter的代码。

增编

我在2015年写下了这个答案。虽然我对答案总体上感到满意,但我一直认为遗漏了什么,我想我终于知道了它是什么。以下不是理解答案的必要条件,但旨在激发读者的兴趣,并为进一步的自我教育提供一些资源。

在文献中,这种方法被称为界面分离原理,属于固体原理。YouTube上有一篇来自Bob叔叔的精彩演讲(有趣的部分大约有15分钟长),展示了如何使用多态性和接口来让编译时依赖点与控制流对抗(建议观众自行决定,Bob叔叔会温和地抱怨Java)。作为回报,这意味着高级实现在通过接口时不需要了解低级实现。因此,较低的级别可以随意交换,正如我们上面所示。

 类似资料:
  • 问题内容: 假设我们在A包中有A类,在B包中有B类。如果类A的对象引用了类B,则称这两个类之间具有耦合。 为了解决耦合问题,建议在包A中定义一个接口,该接口由包B中的类实现。然后,类A的对象可以引用包A中的接口。这通常是“依赖倒置”的一个例子。 这是“在接口级别将两个类解耦”的示例。如果是,当两个类耦合时,它如何消除类之间的耦合并保持相同的功能? 问题答案: 让我们创建一个虚拟的例子。 套餐类别:

  • 问题内容: 假设我们在A包中有A类,在B包中有B类。如果类A的对象引用了类B,则称这两个类在它们之间具有耦合。 为了解决这种耦合,建议在包A中定义一个接口,该接口由包B中的类实现。然后,类A的对象可以引用包A中的接口。这通常是“依赖倒置”的一个例子。 这是“在接口级别将两个类解耦”的示例。如果是,当两个类耦合时,它如何消除类之间的耦合并保持相同的功能? 问题答案: 让我们创建一个虚拟的例子。 套餐

  • 问题内容: 我刚刚看到一个成员函数,如下所示: 但是Cat是这样的接口: 因此,我对如何解释这一点感到困惑。我知道什么东西返回一个对象或原语是什么意思。但是返回接口意味着什么?如何使用此函数的返回值? 问题答案: 考虑一下这种方式:如果在常规类的哪里,当您想在其上调用某些方法时,您 究竟 在乎什么呢? 您会关心方法定义:它们的名称,它们的参数类型,它们的返回值。你 并不 需要关心实际的实现! 由于

  • 我想创建一个

  • 根据C 11标准(草案n3337)§5/9: -如果两个操作数的类型相同,则无需进一步转换。 -否则,如果两个操作数都具有有符号整数类型或都具有无符号整数类型,则整数转换秩较小的操作数类型应转换为秩较大的操作数类型。 -否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数类型的秩,则应将具有有符号整数类型的操作数转换为具有无符号整数类型的操作数的类型。 -否则,如果有符号整数类型的操作数