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