This is the 26th day of my participation in the August Text Challenge.More challenges in August

preface

This article contains the following technical points, do not know the little friends suggest to read the article I prepared before (I will never admit that is trying to get a hand <( ̄)  ̄)>)

The generic

reflection

This article is long, I hope you can be patient and slowly taste, too fast to be careful in reading the book

What is a template pattern

Define a set of processes in the parent class, and implement the common parts of them, and delegate the different parts to the subclasses for implementation

From a UML perspective, this pattern is unbelievably simple

The code template

This is my summary for template mode under the code structure, an abstract class defines the processing method of one thing, and then some packaging for your own internal implementation details (to make reading the code will be more emphasis), if there are some details can not be public use, need according to the specific subclass implementation, is defined as the abstract methods

Abstract class and subclass definitions

abstract class AbstractSuper{
  public void doThing(a) {
    // do some things you like
    common();
    diff();
    // do other things
  }
  private void common(a) {}
  protected abstract void diff(a);
}
class ConcreteSub extends AbstractSuper{
  @Override
  protected void diff(a) {}}Copy the code

Client use

AbstractSuper sup = new ConcreteSub();
sup.doThing();

sup = new ConcreteOtherSub();
sup.doThing();
Copy the code

Project case

demand

In our system, there are two sets of Data templates. The first one is inherited from POJO, which is mainly used for Data interaction and flow within the system; the second one is inherited from RcsDto (RCS Data Transfor Object). Description refers to the Rcs system data transmission object, which is used to convert the data in the system into data that CAN be used by Rcs and sent to the Rcs, or to receive the data sent by Rcs and convert it into the data structure required by the internal operation of the system

Pojos and their implementation classes

@Data
public class Pojo implements Serializable {
  private Long id;
  private Integer status;
}
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
@ToString
public class AreaInfo extends Pojo{
  private java.lang.String name;
  private java.lang.String encode;
  private com.rlzz.r9.rcs.po.RcsInfo rcs;
  private com.rlzz.r9.rcs.po.MapInfo map;
  private java.lang.String description;
  private java.lang.String rid = "";
}
Copy the code

RcsDto and its implementation classes

@Data
public class RcsDto {
  protected String id = "";
  // Status code: 0- Normal, 1- Submitted for deletion, 2- Deleted
  protected Integer stateCode = 0;
  // Used for identity verification
  protected APICheck check = APICheck.check;
}
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
@ToString
public class AreaInfoDto extends RcsDto {
  private String mapId; // MapInfo
  private String rcsId; // RcsInfo
  private String name;// Region name
  private String encode; / / serial number
  private String description;  / / note
}
Copy the code

Details of data transformation

A closer look shows that rcsto. stateCode corresponds to Pojo.status, and AreaInfoDto uses mapId, rcsId, which corresponds to AreaInfo’s Map, Rcs object

Abstract class for transformation operations

A simplified version

Without the details, let’s read the design idea of this abstract class

First, two public methods D toDto() and P to Pojo() are defined.

In these two public methods, the corresponding methods are called separately:

  • A private public property cross method
    • It’s implemented internally, privately, in an abstract class
  • Cross – methods of different properties that require subclasses to implement
    • Define it as an abstract class and give it to subclasses
@SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class DataTemplate<P extends Pojo.D extends RcsDto> {
  public D toDto(P pojo, Class<D> dtoClz) {
    D dto = dtoClz.newInstance();

    setCommValPoToDo(pojo, dto);
    setDiffValPoToDo(pojo, dto);

    return dto;
  };

  public P toPojo(D dto, Class<P> pClz, Pojo... ps) {
    P pojo = pClz.newInstance();

    setDiffValDoToPo(pojo, dto, ps);
    setCommValDoToPo(pojo, dto);

    return pojo;
  }

  // Po -> dto to handle the values of different attributes
  public abstract void setDiffValPoToDo(P info, D dto);

  // dto -> Po, handle the values of different attributes
  public abstract void setDiffValDoToPo(P info, D dto, Pojo... ps);

  // Po -> dto, public attribute value conversion
  private void setCommValPoToDo(P pojo, D dto) throws Exception {}// dto -> Po, public attribute value conversion
  private void setCommValDoToPo(P pojo, D dto) throws Exception {}}Copy the code
Details of the version

The previous version was designed to govern a design idea for abstract classes, and this one lays out all the details that were deliberately omitted. It is recommended to read the comments in the code carefully

@SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class DataTemplate<P extends Pojo.D extends RcsDto> {
  public D toDto(P pojo, Class<D> dtoClz) {
    try {
      Class pClz = pojo.getClass();
      Field pField = null;
      D dto = dtoClz.newInstance();	// Initializes a blank example, followed by reflection to assign values to fields

      setCommValPoToDo(pojo, dto);
      setDiffValPoToDo(pojo, dto);

      for (Field dField : FieldClass.getDtoFields().get(dtoClz)) {	// Fields that can be interrotated are handled in another place
        pField = FieldClass.getPojoFieldMap().get(pClz).get(dField.getName()); RcsDto; // Select P from RcsDto
        dField.set(dto, pField.get(pojo));// Assign the value of P to D
      }

      return dto;
    } catch (Exception e) {
      LogUtil.exception(e);
    }
    return null; // Normally, data is returned
  };

  public P toPojo(D dto, Class<P> pClz, Pojo... ps) {	// Similar to the transformation in the previous step
    if(dto ! =null) {
      try {
        P pojo = pClz.newInstance();	// Initialize the POJO instance
        Class dClz = dto.getClass();
        Field dField = null;
        
        setDiffValDoToPo(pojo, dto, ps);
        setCommValDoToPo(pojo, dto);
        
        for (Field pField : FieldClass.getPojoFields().get(pClz)) {
          dField = FieldClass.getDtoFieldMap().get(dClz).get(pField.getName());
          pField.set(pojo, dField.get(dto));
        }
        return pojo;
      } catch(Exception e) { LogUtil.exception(e); }}return null; // Normally, data is returned
  }

  // Po -> dto to handle the values of different attributes
  public abstract void setDiffValPoToDo(P info, D dto);

  // dto -> Po, handle the values of different attributes
  public abstract void setDiffValDoToPo(P info, D dto, Pojo... ps);

  // Po -> dto, public attribute value conversion, corresponding field value conversion on their parent class
  private void setCommValPoToDo(P pojo, D dto) throws Exception {
    Class supClz = dto.getClass().getSuperclass();
    Field dField = null, sField = null;
    // Pojo.rid -> Dto.id
    dField = pojo.getClass().getDeclaredField(Constant.RID);
    dField.setAccessible(true);
    sField = supClz.getDeclaredField(Constant.ID);
    sField.setAccessible(true);
    sField.set(dto, dField.get(pojo));
    // stateCode
    sField = supClz.getDeclaredField(Constant.STATE_CODE);
    sField.setAccessible(true);
    sField.set(dto, 0);
  }

  // dto -> Po, public attribute value conversion, corresponding field value conversion on their parent class
  private void setCommValDoToPo(P pojo, D dto) throws Exception {
    Class pClz = pojo.getClass();
    Field dField = null, pField = null;
    // Dto.id -> Pojo.rid
    dField = dto.getClass().getSuperclass().getDeclaredField(Constant.ID);
    dField.setAccessible(true);
    pField = pClz.getDeclaredField(Constant.RID);
    pField.setAccessible(true); pField.set(pojo, dField.get(dto)); }}Copy the code

Subclasses source

The subclass implementation

Rcs <-> dto.rcsId, pojo.Map <-> dto.mapId

public class AreaV extends DataTemplate<AreaInfo.AreaInfoDto> {
  @Override
  public void setDiffValPoToDo(AreaInfo info, AreaInfoDto dto) {
    dto.setRcsId(info.getRcs().getRid());
    dto.setMapId(info.getMap().getRid());
  }
  @Override
  public void setDiffValDoToPo(AreaInfo info, AreaInfoDto dto, Pojo... ps) {
    info.setRcs((RcsInfo) ps[0]);
    info.setMap((MapInfo) ps[1]); }}Copy the code
Add static method optimization

Because of the transformation process defined in the abstract class, because each subclass introduces different entity classes, the number of imported is not the same, must use generic + variable arguments to handle

New AreaV().topojo (area, areainfo.class, Pojo… ps ??? ) I pass the last variable argument to which Pojo’s inheriting classes? For how many? In what order? These questions, when we first write the code, more or less in the mind, but over time, probably only God knows (God: come on, I don’t know →_→)

While we’re just designing it, and remember the details, it’s much more user-friendly to encapsulate it as a static method

@SuppressWarnings({ "unchecked", "rawtypes" })
public class AreaV extends DataTemplate<AreaInfo.AreaInfoDto> {
  private static DataTemplate<AreaInfo, AreaInfoDto> visit = new AreaV();

  public static AreaInfoDto infoToDto(AreaInfo info) {
    return visit.toDto(info, AreaInfoDto.class);
  }

  public static AreaInfo dtoToInfo(AreaInfoDto dto, RcsInfo rcs, MapInfo mapInfo) {
    return visit.toPojo(dto, AreaInfo.class, rcs, mapInfo);
  }

  public static AreaInfo dtoToInfo(AreaInfoDto dto) {
    return dtoToInfo(dto, null.null);
  }

  @Override
  public void setDiffValPoToDo(AreaInfo info, AreaInfoDto dto) {
    dto.setRcsId(info.getRcs().getRid());
    dto.setMapId(info.getMap().getRid());
  }

  @Override
  public void setDiffValDoToPo(AreaInfo info, AreaInfoDto dto, Pojo... ps) {
    info.setRcs((RcsInfo) ps[0]);
    info.setMap((MapInfo) ps[1]); }}Copy the code

Missing piece

In fact, in this case, there is also a third set of data templates, flat map structure. In The case of AreaInfo, the key in his map is as follows. In fact, this only needs to consider the processing of their nested classes, there is no specific details that need to be put into the subclass

id
status
name
encode
description
rid

rcsId
rcsName
rcsXXX ...

mapId
mapName
mapXXX ...
Copy the code

teasing

In fact, my design was to directly add toDto(), toPageMap(), fromPageMap(Map) to the corresponding Pojo class, and to add toPojo() to the corresponding Dto class. This was extremely efficient and made the code look like poop. Entity class will become very bloated, the number of lines of code and several times, and the code is very painful, some classes 20,30 fields, nearly 20 in the hand need to do so, the code will give nausea

Then I tried to optimize and iterate on many versions, and this is the final product. The time I spent optimizing the words on this module was probably enough to write them several times in the most stupid way possible, and of course, it was worth it

Using the template design pattern, when we need to add new classes and handle their transformation relationships, we directly inherit the abstract class, a few lines of code to implement the details of the process, it is very elegant, no waddling, no progress

Modules like this one combine a lot of knowledge, such as using generic programming, using reflection to process data dynamically, and using design patterns to layout the functional layout of code