4 回答
TA贡献1827条经验 获得超9个赞
考埃·西尔维拉提供了一个很好的例子,但没有提到另一个重要方面。
通常,接口的要点不是要有更少的类。我想你熟悉耦合和内聚这两个术语。您总是希望拥有松散耦合且高度内聚的代码。
这意味着,您不希望类相互依赖(耦合),而是以继承,多态性等形式共享一些逻辑。这些概念是质量对象导向设计的一些基本支柱。如果您不熟悉这些主题,绝对值得一读。
回到这个问题,如果你正在处理一个有很多特殊情况的复杂逻辑,你通常会有一些重复。但是,这些问题与其他设计模式和原则更相关,这些模式和原则仅用于遵守 DRY 原则,并以推广解决方案方法的方式解决情况。
接口背后的主要思想是为类设置一个逻辑结构,这有助于对象操作的统一性。
想象一个证券交易所系统。
此接口具有一个名为 execute 的方法,该方法将执行一些应用程序逻辑。
public interface Order{ void execute(); }
可能实现此接口的其他类可能是买入和卖出。它看起来像这样:
public class Buy implement Order{ @Override public void execute(){ //TODO: Some logic } }
现在,买入和卖出都有类似的代码,甚至可能有一些重复的代码,但更重要的是,当它们实现相同的接口时,你可以以统一的方式处理它们。您可以有一些股票管理器类,该类会在某些 . 由此可以得出结论,在使用此类队列时,您将能够在 Order 接口的任何实现上调用该方法。Queue<Order>
execute()
在前面的论点之上,通过使用接口和一些框架(如Spring),您可以进行自动布线。这大大减少了对较低级别实现类的依赖,使您可以更改低级类而不会影响顶级处理程序。这种类型的应用程序设计是面向服务的体系结构 (SOA) 中的常见做法。
TA贡献1810条经验 获得超4个赞
Robert 谈到了打破依赖关系,当然你甚至可以打破库存和汽车之间的依赖关系,但我认为这不会给你带来太多好处,因为你不会跨越架构边界。
最好将静态依赖关系中断为 ,因为 I/O 是一个架构边界,然后您可以替换汽车或库存的打印方式,这对于测试非常有用。System.out
打破静态依赖关系
class Car {
private String color;
private Integer numberOfTires;
private PrintStream output = System.out;
void setOutput(PrintStream output){
this.output = Objects.requireNotNull(output);
}
public void printCar() {
output.print("Color: " + color);
output.print("Number of tires: " + numberOfTires);
}
}
现在,您可以在测试中替换输出以捕获输出结果。
应用接口隔离原则
public interface Output {
public void print(String msg);
}
class Car {
private String color;
private Integer numberOfTires;
private Output output = (msg) -> System.out.println(msg);
void setOutput(Output output){
this.output = Objects.requireNotNull(output);
}
public void printCar() {
output.print("Color: " + color);
output.print("Number of tires: " + numberOfTires);
}
}
现在,您唯一的依赖项是输出接口,该接口在测试中更容易替换或模拟。
这个小小的变化使您的汽车独立于具体的输出系统,或者正如罗伯特所说,一个细节。我还可以想象实现一个 JTextArea输出,以便输出可以显示在 GUI 中。
干净的体系结构告诉我们,I/O 是一个细节,我们的业务代码不应该依赖于它。似乎汽车和库存是您的业务代码,因此我向您展示了如何将其与具体的输出系统分离 - 一个细节。
+-----+ uses +--------+ implements +--------------+
| Car | --------> | Output | <------------- | SystemOutput |
+-----+ +--------+ +--------------+
---------> control flow ------------->
我们还应用了依赖关系反转原则,因为源代码依赖关系指向控制流。
TA贡献1842条经验 获得超12个赞
据推测,您的是“车辆库存”,而不是“一辆汽车和一辆卡车的库存”。Inventory
考虑到这一点,也许这会有所帮助:
Car
是一个Vehicle
Truck
是一个Vehicle
Inventory
取决于 -- 而不是 or ,它对这些类型的一无所知Vehicle
Car
Truck
Vehicle::printDetails
由 和 实现Car
Truck
.
public class Scratch4 {
public static void main(String args[]) throws Exception {
Car car = new Car("Blue", 4);
Truck truck = new Truck();
Inventory inventory = new Inventory();
inventory.addVehicle(car);
inventory.addVehicle(truck);
inventory.printVehicleDetails();
}
}
interface Vehicle {
void printDetails();
}
class Car implements Vehicle {
private String color;
private Integer numberOfTires;
public Car(String color, Integer numberOfTires) {
this.color = color;
this.numberOfTires = numberOfTires;
}
public void printDetails() {
System.out.println("Color: " + color);
System.out.println("Number of tires: " + numberOfTires);
System.out.println();
}
}
class Truck implements Vehicle {
@Override
public void printDetails() {
System.out.println("Some kind of truck");
System.out.println();
}
}
class Inventory {
private List<Vehicle> vehicles = new ArrayList<>();;
public void addVehicle(Vehicle vehicle) {
vehicles.add(vehicle);
}
public void printVehicleDetails() {
vehicles.forEach(Vehicle::printDetails);
}
}
收益 率
Color: Blue
Number of tires: 4
Some kind of truck
TA贡献1802条经验 获得超6个赞
在生产环境中的运行时,B 将使用真正的类 A(或实现相同接口的另一个类),因此无需复制代码。
使用接口可以将另一个实现用于其他用途,例如,使用内存中的轻量级假类 A 进行 B 类的单元测试,从而避免重磅实类 A。
您(只能)打破“静态,构建时”的依赖关系。“动态,运行时”依赖项(始终)保留。
在代码中:
public interface AInterface {}
public class A implements AInterface {}
public class B {
AInterface a;
public B(AInterface a) {
this.a = a;
}
}
public class Main {
B b = B(A());
}
public class AFake implements AInterface {}
public class BTest {
B b = B(AFake())
}
此示例使用构造函数依赖关系注入。
添加回答
举报