脱离了实际业务场景的架构设计和算法将是没有价值,或称为“耍流氓”。
本章将根据实际业务案例(请允许我将业务模糊一下)讲解 “匹配算法” 的设计和实现;
业务:在众多的数据行项目下,通过数据源匹配目标源,找到最优匹配项
分析:数据源作为标准,并定义匹配的元素以及优先级,目标源中可能匹配到多个,但是需要找到最优的并且占用掉;简单点讲,就像根据自己的标准找对象一样,最多一个。
实现:数据源sourceItems,目标源 targetItems,匹配因素 tokens,优先级:order
咱们根据实现一步一步讲解:
先定义注解,用于对匹配因素的解释,
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SmartMatch {
}
@Target({
ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@SmartMatch
public @interface SmartMatchCol {
String value() default "";
/**
* alias
* @return
*/
String name();
/**
* 序列
* @return
*/
int index() default 0;
/**
* 优先级
* @return
*/
int order() default 0;
}
注释的类统一解释放入容器中 ,
public class FieldToken implements Serializable, Cloneable {
private final String field;
private final String name;
private transient final SmartMatchCol smartMatchCol;
private final int order;
private boolean match;
public FieldToken(String field, SmartMatchCol smartMatchCol) {
this.field = field;
this.smartMatchCol = smartMatchCol;
this.name = smartMatchCol.name();
this.order = smartMatchCol.order();
this.match = false;
}
public String getField() {
return field;
}
public SmartMatchCol getSmartMatchCol() {
return smartMatchCol;
}
public String getName() {
return name;
}
public int getOrder() {
return order;
}
public boolean isMatch() {
return match;
}
public void setMatch(boolean match) {
this.match = match;
}
@Override
public FieldToken clone() throws CloneNotSupportedException {
return (FieldToken) super.clone();
}
}
对以上注解的类进行统一处理,
@Slf4j
@Component
public class SmartMatchSelector {
/**
* 需要扫描的包
*/
private final String rootPackage = "com.xx";
private final Map<Class<?>, List<FieldToken>> fieldMaps = new ConcurrentHashMap<>();
private final Object locker = new Object();
@PostConstruct
private void init() {
try {
findMatchingClasses().forEach(a -> fieldMaps.put(a, findMatchingFields(a)));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private <T> List<FieldToken> getFieldToken(Class<T> clazz) {
if (!fieldMaps.containsKey(clazz)) {
synchronized (locker) {
if (!fieldMaps.containsKey(clazz)) {
fieldMaps.put(clazz, findMatchingFields