详解为什么java要先定义接口再在impl包定义接口的实现类

学习过java的同学都知道,java中一般都是先定义一个业务层的接口,然后再在impl包里定义这个接口的实现类,来实现某个业务逻辑。那为什么不跳过定义接口的步骤直接写业务层的服务实现类呢?这样可以少定义一个接口,不是更简单吗?下面我们就来探讨一下为什么要先定义一个接口,这个接口到底有什么用。

在Java中,定义接口是一种良好的编程习惯和设计原则,被称为“面向接口编程”。它的好处主要有以下三点:

第一,接口可以抽象出业务逻辑的共性,使得代码更加灵活、可扩展和可维护。

如果没有接口,所有的实现类都必须具有相同的方法和属性,这样会导致代码的冗余和耦合度的增加。

当一个业务逻辑有多个实现类时,这些实现类可能会有一些共性的方法和属性,如果不使用接口进行抽象,那么每个实现类都需要拥有相同的方法和属性,这样会导致代码的冗余和耦合度的增加。

例如,假设有一个图形类,它有一个计算面积的方法和一个颜色属性:

public class Shape {
    private String color;

    public Shape(String color) {
        this.color = color;
    }

    public double getArea() {
        return 0;
    }

    public String getColor() {
        return color;
    }
}

现在,我们需要实现一个圆形类和一个矩形类,它们都继承自Shape类:

public class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public class Rectangle extends Shape {
    private double width;
    private double height;

    public Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }

    public double getArea() {
        return width * height;
    }
}

可以看到,Circle和Rectangle类都需要继承Shape类,并且实现getArea方法和color属性。如果没有接口,Circle和Rectangle类都必须具有相同的方法和属性,这样会导致代码的冗余和耦合度的增加。

而如果使用接口进行抽象,可以将Shape类抽象成一个接口,如下所示:

public interface Shape {
    double getArea();
    String getColor();
}

这样,Circle和Rectangle类就可以分别实现Shape接口,而不需要继承Shape类,代码会更加简洁和灵活。

那如果只有一个实现类的情况下,是不是可以不用定义接口,直接写成单独一个业务层实现类就行了?

是的,如果一个业务逻辑只有一个实现类,那么可以不使用接口,直接编写业务层的实现类。接口的作用在于抽象出业务逻辑的共性,使得代码更加灵活、可扩展和可维护。如果只有一个实现类,就没有必要再定义接口了。

但是,即使只有一个实现类,也可以考虑使用接口进行抽象。这样可以让代码更加规范和易于理解,也方便后续对业务逻辑进行扩展。但是,这样做需要根据具体情况来考虑,没有必要为了使用接口而强行设计接口。

第二,接口可以提高代码的可读性和可理解性。

通过接口定义,开发人员可以清晰地了解业务逻辑的实现方式和调用方式。

接口可以提高代码的可读性和可理解性的原因在于其定义了一个标准的契约,明确了业务逻辑的实现方式和调用方式。

接口定义了业务逻辑的方法和属性,这些方法和属性都是业务逻辑的公共接口,不同的实现类都必须实现这些接口。通过接口定义,开发人员可以清晰地了解业务逻辑的实现方式和调用方式,从而更容易理解代码的逻辑。

例如,如果我们定义了一个计算器接口:

public interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);
    int multiply(int a, int b);
    int divide(int a, int b);
}

我们可以很清晰地了解到这个计算器的实现方式和调用方式,即它必须支持加减乘除四个操作,每个操作都接受两个整数参数,并且返回一个整数结果。

如果我们直接编写一个计算器类,而不是使用接口,那么开发人员就需要自己去了解这个计算器的实现方式和调用方式,容易出现混淆和误解。

总之,通过接口定义业务逻辑的公共接口,可以提高代码的可读性和可理解性,使得代码更加规范、易于理解和维护。

第三,接口可以降低代码的依赖性和耦合度。

通过依赖接口而不是实现类,可以使得代码更加灵活,易于维护和扩展。

接口可以降低代码的依赖性和耦合度的原因在于其可以将接口和实现类分离,通过依赖接口而不是实现类,使得代码更加灵活,易于维护和扩展。

在一个系统中,各个模块之间需要相互调用,如果模块之间的依赖关系过于紧密,就会导致代码的可维护性和可扩展性变差。接口的作用在于将模块之间的依赖关系解耦,降低代码的耦合度。

举个例子,假设我们有一个图形计算器,它可以计算各种形状的面积和周长,现在我们需要添加一个新的形状——梯形。如果我们直接修改图形计算器的代码,添加梯形的计算逻辑,那么代码的耦合度就会变高,同时也会影响到图形计算器原有的功能。

使用接口可以解决这个问题。我们可以定义一个Shape接口,包含计算面积和周长的方法:

public interface Shape {
    double getArea();
    double getPerimeter();
}

然后每个形状都实现这个接口,例如:

public class Circle implements Shape {
    // ...
}

public class Rectangle implements Shape {
    // ...
}

public class Trapezoid implements Shape {
    // ...
}

这样,图形计算器只需要依赖Shape接口,而不是具体的实现类,可以通过Shape接口调用各种形状的计算方法,而不需要知道具体的实现细节。当我们需要添加新的形状时,只需要实现Shape接口即可,不需要修改原有的代码,从而降低了代码的耦合度,使得代码更加灵活、易于维护和扩展。

总之,通过依赖接口而不是实现类,可以降低代码的依赖性和耦合度,使得代码更加灵活、易于维护和扩展。

因此,尽管在某些情况下直接编写实现类可能更加简单,但是使用接口是一种更加规范、可扩展和可维护的做法。

%title插图%num

相关文章 推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注