ReverseSuper

role

One-click generation of dynamic code, support for interface/abstract class generation

reference

    implementation 'com. Licola: reversesuper - the annotation: 1.2.0'/ / comment
    annotationProcessor 'com. Licola: reversesuper - compiler: 1.2.0'// Code generation tool library
Copy the code

use

The dynamic generation capabilities of the library are shown below

/** * Created by LiCola on 2018/3/15. * Example, mainly for existing classes, the corresponding interface class can be generated after rebuilding. * Avoid manual writing, refactoring for projects, abstractions, etc., to speed up development. * Where the AccountManager interface class is dynamically generated, it abstracts the target class's public method */
@ReverseImpl
public class AccountManagerImpl implements AccountManager {

  /** * The target method of the abstract method generated in reverse **@paramInput Indicates the input value *@returnFixed return */
  @Override
  public String reverseMethod(String input) {
    return "Target method to which abstract method is generated in reverse";
  }

  /** * The target method to which the abstract method is generated in reverse - with arguments **@paramInteger Annotated input range *@returnFixed the return value */
  @Override
  public String reversMethod(@IntRange(from = 0, to = 10) Integer integer) {
    // Demonstrate the ability to generate method parameter annotations in reverse
    return "Target method that generates abstract methods backwards - with parameter annotations";
  }

  @Override
  @StringRes
  public int reversMethod(a) {
    // Demonstrate the ability to generate return value annotations in reverse
    return android.R.string.ok;
  }

  private String value = "Non-method information variables will not be processed.";

  private void privateMethod(a) {
    // Private methods that are not reverse-generated}}Copy the code

When the @ReverseImpl annotation is on the target class, click build-rebuild to dynamically generate the corresponding interface class. And the resulting generated code is actually in the same package as the target class (apK packaging process).

As you can see, one-click Rebuild dynamically generates code, omits the manual operation of abstract public methods of existing code, which is fast and efficient, and also has processing of method annotations and parameter annotations.

The @reverseExtend annotation also generates an abstract class for the target class. See Adapter for examples

Project background

In the project reconstruction, faced with some module code that had not been abstracted before because of some reasons, the function was directly realized without abstracting the interface, and the implementation class was directly exposed externally.

When an implementation class needs to be abstracted into an interface, exposing the interface so that it can add functionality to the interface’s mock operations or decorator patterns, or other operations.

Abstract implementation class interface is our goal, generally a small amount of code can be completed manually, but in the face of a large number of implementation classes need to abstract the interface, the workload is huge.

In the face of huge and simple repetitive work, the first thing we consider is that machines can replace automation and automatically generate code. Moreover, refactoring is a dynamic process that requires some flexibility because the function names and parameters of the implementation class can change.

Project Implementation Principle

Start by defining our goals

  • Auto-generated code
  • And flexibility, more than one generation.

This is reminiscent of Java providing an annotation processor, which has a chance to process the code before the project is compiled. With this processor, we can scan existing target classes and use tools to generate Java files based on the method information obtained from the scan.

The annotation processor also takes place before each project is compiled, providing the flexibility to generate new dynamic code once the implementation class is modified and compiled again.

About naming annotations Reverse:

The usual way to write code is from interface (top layer) -> implementation class (bottom layer).

Abstract refactoring generates interface classes instead of implementation classes (low-level), hence Reverse.

instructions

Here are some details

About Naming conventions

Refer to Alibaba Java development manual – programming specification – naming style.

14. Naming rules for interfaces and implementation classes:

[Mandatory] For Service and DAO classes, based on SOA philosophy, the exposed Service must be an interface, and the internal implementation class is distinguished from the interface by the Impl suffix. Example: CacheServiceImpl Implements the CacheService interface

[Recommended] If the interface name describes the capability, use the corresponding adjective as the interface name (usually in the form of – able). Example: AbstractTranslator implements Translatable.

There are two sets of rules, corresponding to two options in the @ReverseImpl annotation. The Impl naming style is enforced by default.

 /** * Reverse generation of high-level interface class annotations ** The tagged class generates interface classes */ in its sibling package in the build directory at compile time
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.CLASS)
 public @interface ReverseImpl {
   /** * The default suffix of the annotated class name is Impl * Default rules such as AccountMangerImpl(tag class)->AccountManager(generated interface) * Can also be modified based on the suffix of the actual implementation class. * Strictly check that arguments must be suffixes for the tagged class. * /
   String targetSuffix(a) default "Impl";

   /** * Specifies the generated interface name * Default: this field is not used by default, through {@link#targetSuffix} Crop the tag class name of the convention suffix to generate an interface * Non-empty input: specifies the generated interface name, ignoring suffix checking * e.g. : AbstractTranslator->Translatable */
   String interfaceName(a) default "";
 }

Copy the code

6. [Mandatory] Abstract names start with Abstract or Base

There are also two sets of rules, corresponding to the @reverseExtend annotation, where the Abstract starting naming style is implemented by default

/** * Created by LiCola on 2018/5/24. ** * Created by LiCola on /** * Created by LiCola on 2018/5/24
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface ReverseExtend {


  The default rule is Adapter ->AbstractAdapter * You can also pass in arguments specifying the prefix of the generated Abstract class name * for example: Pass in Base, calling Adapter(tag class)->BaseAdapter(generated abstract class) */
  String superPrefix(a) default "Abstract";

  /** * Specifies the generated abstract class name * default: this field does not work by default, through {@link#superName()} Directly concatenates the tag class and prefix to generate abstract classes * non-empty input: specifies the name of the generated abstract class * e.g., pass BaseAdapter, MyAdapter(tag class)->BaseAdapter(generated abstract class) */
  String superName(a) default "";

}
Copy the code

About the generated Java files

During the Rebuild project, the app-build-generated-sourceapt file will generate the same name package of the target class and dynamically generated high-level class. Finally in apK packaging process source. Java files and APT. Java files will be merged packaging. When we inherit an interface, implements AccountManager assumes that it is the code that imports the package with the same name, and there is no import statement.

Auxiliary refactoring – deprecated eventually

Refactoring is a gradual process of generating interface classes backwards from the original implementation classes. The interface class may change. Dynamic reversals can be convenient. As soon as you add/modify the implementation class method parameters/return values and their annotations, rebuild will generate the interface. Change it once (or change the interface class and implementation class methods twice).

When the refactoring is complete or the higher-level abstractions are separated (such as dynamic proxies, which directly abstract the implementation logic inside the method), our higher-level classes are finally determined and there is no need to dynamically generate the reverse interface classes when building the project.

Generating each build dynamically may actually slow down the project’s compile time. In this case, you can copy the interface class from the dynamic file package app-build-generated-sourceapt and put it into the appropriate package. Deprecate the @reverseImpl / @reverseExtend annotation. Fulfill its mission to assist in refactoring.

Project inspiration

Derived from the famous Butterknife’s use of the annotation processor.

future

In fact, following this idea can solve the MVP architecture of the V/P two layer interface method too much trouble. As long as we have a clear idea and one person is responsible for a V/P corresponding module, we can first write p-layer business code, and then generate abstract interfaces to expose public methods, which are provided to V layer for use. And each time P layer method new/modified parameters are dynamically generated, to avoid the trouble of two changes.