在接口级别解耦两个类意味着什么? [英] What does decoupling two classes at the interface level mean?

查看:114
本文介绍了在接口级别解耦两个类意味着什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们在包A中有A类,在包B中有B类。如果类A的对象引用了类B,则说这两个类之间有耦合。



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



这是在接口级别解耦两个类的例子。如果是的话,它如何消除类之间的耦合并在两个类耦合时保留相同的功能?

解决方案

让我们创建包含 packageA

  package packageA; 

import packageB.B;

公共等级A {
private B myB;

public A(){
this.myB = new B();
}

public void doSomethingThatUsesB(){
System.out.println(用myB做事);
this.myB.doSomething();
}
}

Class B in package packageB

  package packageB; 

公共类B {
public void doSomething(){
System.out.println(B做了些什么。);
}
}

如我们所见, A 取决于 B 。如果没有 B ,则无法使用 A 。但是如果我们希望将来用 BetterB 替换 B 呢?为此,我们在 packageA 中创建一个接口 Inter

  package packageA; 

public interface Inter {
public void doSomething();
}

要使用此界面,我们导入packageA.Inter ; 并让 B在 B 中实现Inter 并替换<$ c $的所有出现c> B 在 A 中, Inter 。结果是 A 的修改版本:

  package packageA; 

公共等级A {
private Inter myInter;

public A(){
this.myInter = ???; //这里做什么
}

public void doSomethingThatUsesInter(){
System.out.println(使用myInter做事);
this.myInter.doSomething();
}
}

此时,我们已经看到来自 A B 消失了: import packageB.B; 不再需要。只有一个问题:我们无法实例化接口的实例。但是控制反转得以拯救:而不是实例化 Inter wihtin 一个的构造函数,构造函数将要求实现Inter 的内容为参数:

  package packageA; 

公共等级A {
private Inter myInter;

public A(Inter myInter){
this.myInter = myInter;
}

public void doSomethingThatUsesInter(){
System.out.println(使用myInter做事);
this.myInter.doSomething();
}
}

通过这种方法,我们现在可以改变具体的实施方式 A 内的随意。假设我们写了一个新类 BetterB

  package packageB; 

import packageA.Inter;

公共类BetterB实现Inter {
@Override
public void doSomething(){
System.out.println(BetterB做了一些事。);
}
}

现在我们可以实例化 A s具有不同的 Inter -implementations:

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

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

我们无需在 A 。代码现在已经解耦,我们可以随意改变 Inter 的具体实现,只要合约 Inter 是满意的。最值得注意的是,我们可以支持代码,这些代码将在未来编写并实现 Inter






Adendum



两年前我写了这个答案。虽然总体上对答案感到满意,但我一直认为缺少了某些东西,我想我终于知道它是什么了。以下内容对于理解答案没有必要,但旨在引起读者的兴趣,并为进一步的自我教育提供一些资源。



在文献中,这种方法称为接口隔离原则,属于 SOLID -principles 。有一个鲍勃叔叔在YouTube上的精彩演讲(有趣的是大约15分钟)显示多态性和接口如何用于让编译时依赖性指向控制流(建议观察者自行决定,鲍勃叔叔会轻微地咆哮Java)。反过来,这意味着高级实现在通过接口进行segretaget时不需要了解更低级别的实现。因此,可以随意交换较低的水平,如上所示。


Lets say we have class A in package A and class B in package B . If object of class A has reference to class B, then the two classes are said to have coupling between them.

To address the coupling, it is recommended to define an interface in package A which is implemented by class in package B. Then object of class A can refer to interface in package A . This is often an example in "inversion of dependency".

Is this the example of "decoupling two classes at the interface level". If yes, how does it remove the coupling between classes and retain the same functionality when two classes were coupled?

解决方案

Let us create a fictive example.

Class A in package packageA:

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

Class B in package packageB:

package packageB;

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

As we see, A depends on B. Without B, A cannot be used. But what if we want to replace B in the future by a BetterB? For this, we create an Interface Inter within packageA:

package packageA;

public interface Inter {
    public void doSomething();
}

To utilize this interface, we import packageA.Inter; and let B implements Inter in B and replace all occurences of B within A with Inter. The result is this modified version of 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();
    }
}

At this point, we see already that the dependency from A to B is gone: the import packageB.B; is no longer needed. There is just one problem: we cannot instantiate an instance of an interface. But Inversion of control comes to the rescue: instead of instantiating something of type Inter wihtin A's constructor, the constructor will demand something that implements Inter as parameter:

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

With this approach we can now change the concrete implementation of Inter within A at will. Suppose we write a new class BetterB:

package packageB;

import packageA.Inter;

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

Now we can instantiante As with different Inter-implementations:

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

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

And we did not have to change anything within A. The code is now decoupled and we can change the concrete implementation of Inter at will, as long as the contract(s) of Inter is (are) satisfied. Most notably, we can support code, which will be written in the future and implements Inter.


Adendum

I wrote this answer over two years ago. While being overall satisfied with the answer, I always thought that something was missing and I think I finally know what it was. The following is not necessary to understand the answer, but is meant to spark interest in the reader, as well as provide some resources for further self-education.

In literature, this approach is known as Interface segregation principle and belongs to the SOLID-principles. There is a nice talk from uncle Bob on YouTube (the interesting bit is about 15 minutes long) showing how polymorphism and interfaces can be used to let the compile-time dependency point against the flow of control (viewer's discretion is advised, uncle Bob will mildly rant about Java). This, in return, means that the high level implementation does not need to know about lower level implementations when they are segretaget through interfaces. Thus lower levels can be swapped at will, as we have shown above.

这篇关于在接口级别解耦两个类意味着什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆