Several common uses of Java interfaces

Interface is a very important concept in object-oriented programming. It is the declaration of a series of methods, but there is no concrete implementation. Some programming languages, such as swift, interpret interfaces as "protocols", which I think is also very appropriate. An interface is a set of specifications for methods.

Summarizes the common usage of the following interfaces

1. As a specific description of the execution process within the class

This is the most fundamental role of the interface. Sometimes a certain link of the internal process of the class is not determined by the class itself. The class does not know exactly what will be executed at this step. Therefore, an interface needs to be set up here to provide extensions to the outside world.

There is a very good example for this function.

A stopwatch class: used to measure the time it takes to execute a certain piece of code to execute.

Preliminary code looks like this:

public class Stopwatch {
    
    
  
    private long deltaTime;
 
    public void clear() {
    
    
        deltaTime = 0L;
    }
 
    public long getDeltaTime() {
    
    
        return deltaTime;
    }
 
    public void start() {
    
    
        long startTime = System.currentTimeMillis();
        // do something
        deltaTime = System.currentTimeMillis() - startTime;
    }
}

And the code using Stopwatch looks like this:

public static void main(String[] args) {
    
    
  Stopwatch stopwatch = new Stopwatch();
  stopwatch.clear();	//时间清零
  stopwatch.start();	//开始运行
  System.out.printf("time: %dms\n",stopwatch.getDeltaTime());
}

Then we know that for the stopwatch class itself, it can't decide what code it will measure at the position of do something.

That is, the code here will be determined Stopwatchexternally by . So here we can negotiate a protocol, which is the interface. StopwatchDecided to open up an interface called NeedMonitoringProgramthe program that needs to be monitored, in which the doProgram() method is to execute the code that needs to be monitored. External use Stopwatchonly needs to implement NeedMonitoringProgramthe interface to define the code that needs to be monitored.

public interface NeedMonitoringProgram {
    
    
  void doProgram();
}

give Stopwatchto the NeedMonitoringProgramobject

public class Stopwatch {
    
    
 
    private long deltaTime;
    private NeedMonitoringProgram program;
 
    public void setProgram(NeedMonitoringProgram program) {
    
    
        this.program = program;
    }
 
    public void start() {
    
    
        long startTime = System.currentTimeMillis();
        program.doProgram();
        deltaTime = System.currentTimeMillis() - startTime;
    }
 
    public interface NeedMonitoringProgram {
    
    
        void doProgram();
    }
  
  //省略clear()和getDeltaTime()方法
}

Simulate the time required to measure 1 to 1,000,000 superposition and summation

 
Stopwatch stopwatch = new Stopwatch();
Stopwatch.NeedMonitoringProgram program = new Stopwatch.NeedMonitoringProgram() {
    
    
 
  @Override
  public void doProgram() {
    
    
    int sum = 0;
    int number = 1_000_000;
    for (int i = 0; i <= number; i++) {
    
    
      sum += i;
    }
    System.out.printf("sum: %d\n", sum);
  }
};
stopwatch.setProgram(program);
stopwatch.clear();
stopwatch.start();
System.out.printf("time: %dms\n",stopwatch.getDeltaTime());

Suppose that the code to be tested needs external parameters, and also needs to return to the external execution result. The interface method can also be improved.

Add a Paramclass to store the parameters.

private Param params = new Param();
 
public void addParam(String key, Object value) {
    
    
  	params.put(key, value);
}
 
public interface NeedMonitoringProgram<RETURN_OBJECT> {
    
    
  RETURN_OBJECT doProgram(Param params);
}
 
public class Param extends HashMap<String, Object> {
    
    
 
}

The start method can also directly add NeedMonitoringProgramobjects to reduce the steps in use. And NeedMonitoringProgramthe generic type of the class specifies the return type.

public <T> T start(NeedMonitoringProgram<T> program) {
    
    
  long startTime = System.currentTimeMillis();
  T returnObject = program.doProgram(params);
  deltaTime = System.currentTimeMillis() - startTime;
  return returnObject;
}

Use addParam() to assign values ​​to the code before executing start(). And start() will also return the result of the code execution.

Stopwatch stopwatch = new Stopwatch();
stopwatch.clear();
stopwatch.addParam("number", 1_000_000);
int sum = stopwatch.start(new Stopwatch.NeedMonitoringProgram<Integer>() {
    
    
 
    @Override
    public Integer doProgram(Stopwatch.Param params) {
    
    
      int sum = 0;
      int number = (Integer)params.get("number");
      for (int i = 0; i <= number; i++) {
    
    
      	sum += i;
      }
      return sum;
  }
});
System.out.printf("sum: %d\n", sum);
System.out.printf("time: %dms\n",stopwatch.getDeltaTime());

Using JDK8, lambda expressions can even be abbreviated as

int sum = stopwatch.start(params -> {
    
    
  int s = 0;
  int number = (Integer)params.get("number");
  for (int i = 0; i <= number; i++) {
    
    
    s += i;
  }
  return s;
});

Although java does not have the concept of a function object, the use of lambda can fully express the idea of ​​a functional programming, where the interface is equivalent to representing a function object.

The above are the most fundamental functions of the interface, which are widely used in graphical programs such as AWT, Swing, and Android. Interfaces like XXXListener that handle control events use this feature.

2. Polymorphism

The specific subclass objects can be regarded as the parent class to shield the differences between different subclass objects.

public interface Animal {
    
    
    void sayHello();
}
public class Cat implements Animal{
    
    
    @Override
    public void sayHello() {
    
    
        System.out.println("I'm a cat.");
    }
}
public class Dog implements Animal{
    
    
    @Override
    public void sayHello() {
    
    
        System.out.println("I'm a dog.");
    }
}
public static void main(String[] args) {
    
    
  Animal[] animals = new Animal[]{
    
    new Cat(), new Dog()};
  for (Animal animal : animals) {
    
    
    animal.sayHello();
  }
}

Here, Cat and Dog are put into the Animal array equally, and processed in batches. The function of polymorphism is actually the same as that of abstract class.

3. The specific implementation process used to hide the method

A simple Dao

public interface UserDao {
    
    
  
  	User findUser(int id);
  
    void addUser(User user);
    
    void updateUser(User user);
    
    void deleteUser(int id);
}

This DaoFactoryfactory class represents the logic of generating Dao within a framework (super simplified)

public class DaoFactory {
    
    
  
  	public <T> T getDaoInstance(Class<T> clazz) {
    
    
     	if(clazz == UserDao.class) {
    
    
          	return new UserDaoImpl();
     	} else if(……) {
    
    
          
     	}
  	}
}

Then when we use the framework, we only need DaoFactoryto obtain the specific implementation object of the Dao through the type of Dao.

We only care about the methods we can use in the Dao interface, but not how the Dao implements additions, deletions, and changes. With this Dao object, we can complete the operation of the database. That is to say, the interface shields the specific implementation of the method for us, and shields some disorganized intermediate methods in the specific implementation class that are useless to the user.

4. Expose distributed services

public interface XXXService {
    
    
  
}

In large-scale website projects, the underlying services are based on distributed deployment. For example, in the Dubbo framework, in a distributed project, the interface and implementation are separated into two sub-projects. The advantage of distributed is that the stability of service provision is maintained by setting up multiple service providers. As long as not all service providers hang up, service consumers (consumers) can still get stable services. For the service consumer, it does not care who provides the service, but only which services are available, so the interface not only shields the specific implementation of the method, but also can receive services from different service providers. Here, the interface plays the role of a protocol, which services the consumer needs are described by the interface, and the provider implements its own processing service logic according to the interface.

5. Give a class a capability

We often encounter some classes such as XXXable, which end with able

For example java.io.Serializable, this gives the class the ability to be serializable

We can also write interfaces and implementations that empower classes ourselves.

Anyone who has done WeChat payment should know that connecting to WeChat payment requires sending an HTTP request containing an xml string.

Then you can encapsulate a client (Client) for the WeChat payment function.

Then the general steps are as follows:

public class WechatClient{
    
    
 
    /**
     * 统一下单
     */
    public void unifiedOrder() {
    
    
        //1. 生成xml
      	//2. 发送HTTP请求
      	//3. 处理请求结果
    }
}

Next, give the client class the ability to send HTTP requests and declare an interface:

public interface HttpSendable {
    
    
	//发送GET请求
    HttpResponse getHttps(String url);
	//发送包含raw富文本的POST请求
    HttpResponse postHttps(String url, String raw);
}

HttpSendableAs the name implies, it is the ability to send HTTP requests

Then add this capability to the client

public class WechatClient implements HttpSendable

The implementation of this capability can be implemented using an abstract parent class, without causing secondary code to pollute the logic of the main class

public abstract class HttpSender implements HttpSendable{
    
    
    @Override
    public HttpResponse getHttps(String url) {
    
    
        return null;
    }
 
    @Override
    public HttpResponse postHttps(String url, String raw) {
    
    
        return null;
    }
}

HttpSenderIt is the sender of HTTP, of course it also has the ability to send HTTP HttpSendableand then the client inherits it

public class WechatClient extends HttpSender implements HttpSendable{
    
    
 
    /**
     * 统一下单
     */
    public void unifiedOrder() {
    
    
      	//1. 生成xml
      	String xml = "";//代码略
      	//2. 发送HTTP请求
        HttpResponse response = super.postHttps("https://", xml);
      	//3. 处理请求结果
      	//代码略
    }
}

Does writing code like this increase the comprehensibility of the code?

6. As a constant interface

Generally, old java programmers like to use it like this, including the internal use of JDK. Throw some constants in the interface.

//申请状态常量
public interface ApplyStatusContants {
    
    
  	public static final String STATIC_UNTREATED = "STATIC_UNTREATED";//未处理
  	public static final String STATIC_ACCEPT = "STATIC_ACCEPT"; //通过
  	public static final String STATIC_REJECT = "STATIC_REJECT"; //拒绝
}

However, the enum enumeration type has been added since JDK1.5. We don't need a constant interface, we can do it as follows:

public enum ApplyStatus {
    
    
  	UNTREATED, ACCEPT, REJECT
}

Simple and crude.

The usage of the interface is still very flexible and changeable, and there may be other usages that are not listed. You are welcome to add them.

Guess you like

Origin blog.csdn.net/qq_43842093/article/details/123587331