1. Introduction
1.绪论
In this tutorial, we’ll be discussing the Interface Segregation Principle, one of the SOLID principles. Representing the “I” in “SOLID”, interface segregation simply means that we should break larger interfaces into smaller ones.
在本教程中,我们将讨论界面隔离原则,这是SOLID原则中的一项。接口隔离代表了 “SOLID “中的 “I”,简单地说,我们应该将较大的接口分成较小的接口。
Thus ensuring that implementing classes need not implement unwanted methods.
从而确保实现类不需要实现不需要的方法。
2. Interface Segregation Principle
2.界面隔离原则
This principle was first defined by Robert C. Martin as: “Clients should not be forced to depend upon interfaces that they do not use“.
这一原则最早是由Robert C. Martin定义的。”客户不应被迫依赖他们不使用的接口“。
The goal of this principle is to reduce the side effects of using larger interfaces by breaking application interfaces into smaller ones. It’s similar to the Single Responsibility Principle, where each class or interface serves a single purpose.
这一原则的目标是通过将应用接口分解成较小的接口来减少使用较大接口的副作用。它类似于单一责任原则,即每个类或接口只服务于单一目的。
Precise application design and correct abstraction is the key behind the Interface Segregation Principle. Though it’ll take more time and effort in the design phase of an application and might increase the code complexity, in the end, we get a flexible code.
精确的应用设计和正确的抽象是接口隔离原则背后的关键。尽管在应用程序的设计阶段会花费更多的时间和精力,并且可能会增加代码的复杂性,但最终我们会得到一个灵活的代码。
We’ll look into some examples in the later sections where we have a violation of the principle, and then we’ll fix the problem by applying the principle correctly.
我们将在后面的章节中研究一些违反该原则的例子,然后通过正确应用该原则来解决这个问题。
3. Sample Interface and Implementation
3.接口和实施样本
Let’s look into a situation where we’ve got a Payment interface used by an implementation BankPayment:
让我们看看这样一种情况:我们有一个Payment接口,被一个实现BankPayment使用。
public interface Payment {
void initiatePayments();
Object status();
List<Object> getPayments();
}
And the implementation:
而实施。
public class BankPayment implements Payment {
@Override
public void initiatePayments() {
// ...
}
@Override
public Object status() {
// ...
}
@Override
public List<Object> getPayments() {
// ...
}
}
For simplicity, let’s ignore the actual business implementation of these methods.
为了简单起见,让我们忽略这些方法的实际业务实现。
This is very clear — so far, the implementing class BankPayment needs all the methods in the Payment interface. Thus, it doesn’t violate the principle.
这一点非常清楚–到目前为止,实现类BankPayment需要Payment接口中的所有方法。因此,它没有违反这个原则。
4. Polluting the Interface
4.污染界面
Now, as we move ahead in time, and more features come in, there’s a need to add a LoanPayment service. This service is also a kind of Payment but has a few more operations.
现在,随着时间的推移,更多的功能出现,有必要添加一个LoanPayment服务。这个服务也是Payment的一种,但有一些更多的操作。
To develop this new feature, we’ll add the new methods to the Payment interface:
为了开发这个新功能,我们将把新方法添加到Payment接口。
public interface Payment {
// original methods
...
void intiateLoanSettlement();
void initiateRePayment();
}
Next, we’ll have the LoanPayment implementation:
接下来,我们将有LoanPayment实现。
public class LoanPayment implements Payment {
@Override
public void initiatePayments() {
throw new UnsupportedOperationException("This is not a bank payment");
}
@Override
public Object status() {
// ...
}
@Override
public List<Object> getPayments() {
// ...
}
@Override
public void intiateLoanSettlement() {
// ...
}
@Override
public void initiateRePayment() {
// ...
}
}
Now, since the Payment interface has changed and more methods were added, all the implementing classes now have to implement the new methods. The problem is, implementing them is unwanted and could lead to many side effects. Here, the LoanPayment implementation class has to implement the initiatePayments() without any actual need for this. And so, the principle is violated.
现在,由于支付接口发生了变化,增加了更多的方法,所有的实现类现在都必须实现这些新方法。问题是,实现这些方法是不需要的,可能会导致许多副作用。在这里,LoanPayment实现类必须实现initiatePayments() ,而没有任何实际需要。因此,这个原则被违反了。
So, what happens to our BankPayment class:
那么,我们的BankPayment类会发生什么。
public class BankPayment implements Payment {
@Override
public void initiatePayments() {
// ...
}
@Override
public Object status() {
// ...
}
@Override
public List<Object> getPayments() {
// ...
}
@Override
public void intiateLoanSettlement() {
throw new UnsupportedOperationException("This is not a loan payment");
}
@Override
public void initiateRePayment() {
throw new UnsupportedOperationException("This is not a loan payment");
}
}
Note that the BankPayment implementation now has implemented the new methods. And since it does not need them and has no logic for them, it’s just throwing an UnsupportedOperationException. This is where we start violating the principle.
请注意,BankPayment的实现现在已经实现了新的方法。由于它不需要这些方法,也没有相应的逻辑,所以它只是抛出一个不支持操作的异常。这就是我们开始违反原则的地方。。
In the next section, we’ll see how we can solve this problem.
在下一节,我们将看到如何解决这个问题。
5. Applying the Principle
5.应用该原则
In the last section, we have intentionally polluted the interface and violated the principle. In this section, we’ll look into how to add the new feature for loan payment without violating the principle.
在上一节中,我们故意污染了界面,违反了原则。在本节中,我们将研究如何在不违反原则的情况下添加贷款支付的新功能。
Let’s break down the interface for each payment type. The current situation:
让我们来分析一下每种支付类型的界面。目前的情况。
Notice in the class diagram, and referring to the interfaces in the earlier section, that the status() and getPayments() methods are required in both the implementations. On the other hand, initiatePayments() is only required in BankPayment, and the initiateLoanSettlement() and initiateRePayment() methods are only for the LoanPayment.
在类图中注意到,参照前面的接口,status()和getPayments()方法在两种实现中都是必需的。另一方面,initiatePayments()只在BankPayment中需要,而initiateLoanSettlement()和initiateRePayment()方法只针对LoanPayment。
With that sorted, let’s break up the interfaces and apply the Interface Segregation Principle. Thus, we now have a common interface:
有了这个分类,让我们把接口拆开,应用接口隔离原则。因此,我们现在有了一个共同的接口。
public interface Payment {
Object status();
List<Object> getPayments();
}
And two more interfaces for the two types of payments:
还有两个接口,用于两种类型的支付。
public interface Bank extends Payment {
void initiatePayments();
}
public interface Loan extends Payment {
void intiateLoanSettlement();
void initiateRePayment();
}
And the respective implementations, starting with BankPayment:
以及各自的实现,从BankPayment开始。
public class BankPayment implements Bank {
@Override
public void initiatePayments() {
// ...
}
@Override
public Object status() {
// ...
}
@Override
public List<Object> getPayments() {
// ...
}
}
And finally, our revised LoanPayment implementation:
最后,我们修订了LoanPayment实现。
public class LoanPayment implements Loan {
@Override
public void intiateLoanSettlement() {
// ...
}
@Override
public void initiateRePayment() {
// ...
}
@Override
public Object status() {
// ...
}
@Override
public List<Object> getPayments() {
// ...
}
}
Now, let’s review the new class diagram:
现在,让我们回顾一下新的类图。
As we can see, the interfaces don’t violate the principle. The implementations don’t have to provide empty methods. This keeps the code clean and reduces the chance of bugs.
正如我们所看到的,接口并没有违反这一原则。实现不需要提供空方法。这就保持了代码的简洁,并减少了出现错误的机会。
6. Conclusion
6.结语
In this tutorial, we looked at a simple scenario, where we first deviated from following the Interface Segregation Principle and saw the problems this deviation caused. Then we showed how to apply the principle correctly in order to avoid these problems.
在本教程中,我们看了一个简单的场景,首先我们偏离了界面隔离原则,并看到了这种偏离造成的问题。然后,我们展示了如何正确应用该原则,以避免这些问题。
In case we’re dealing with polluted legacy interfaces that we cannot modify, the adapter pattern can come in handy.
如果我们处理的是无法修改的受污染的遗留接口,那么adapter模式就可以派上用场。
The Interface Segregation Principle is an important concept while designing and developing applications. Adhering to this principle helps to avoid bloated interfaces with multiple responsibilities. This eventually helps us to follow the Single Responsibility Principle as well.
接口分离原则是设计和开发应用程序时的一个重要概念。遵循这一原则有助于避免具有多重责任的臃肿接口。这最终有助于我们也遵循单一责任原则。
As always, the code is available over on GitHub.
像往常一样,代码可在GitHub上获得。