implement fall-fast anti-refactor machenism

This commit is contained in:
金戟 2021-03-26 01:12:30 +08:00
parent 3d6d3c9dd1
commit 27174afa93
3 changed files with 35 additions and 9 deletions

View File

@ -103,9 +103,9 @@ OmniAccessor.set(parent, "children[2]/*/value", 100);
> **你真的需要用到`OmniAccessor`吗?**
>
> `OmniAccessor`具有基于Fail-Fast机制的防代码重构能力当用户提供的访问路径无法匹配到任何成员时`OmniAccessor`将立即抛出异常,使单元测试提前终止。然而相比常规的成员访问方式,`OmniAccessor`在IDE重构方面的支持依然偏弱。
> `OmniAccessor`具有基于Fail-Fast机制的防代码重构能力当用户提供的访问路径无法匹配到任何成员时`OmniAccessor`将立即抛出`NoSuchMemberError`错误,使单元测试提前终止。然而相比常规的成员访问方式,`OmniAccessor`在IDE重构方面的支持依然偏弱。
>
> 对于复杂对象的内容赋值,大多数情况下,我们更推荐使用[构造者模式](https://developer.aliyun.com/article/705058)或者暴露Getter/Setter方法实现。这些常规手段虽然稍显笨拙尤其在需要为许多相似的成员批量赋值的时候但对业务逻辑的封装和重构都更加友好。
> 仅当原类型不适合改造,且没有其它可访问目标成员的方法时,`OmniAccessor`才是最后的终极工具
> 仅当原类型不适合改造,且没有其它可访问目标成员的方法时,`OmniAccessor`才是最后的终极手段
>
> 出于相同的原因,虽然技术上可行,但我们并不推荐在除单元测试之外的场景使用`OmniAccessor`方式来读写业务类的成员字段。
> 出于相同的原因,我们并不推荐在除单元测试之外的场景使用`OmniAccessor`方式来读写业务类的成员字段(虽然技术上可行)

View File

@ -0,0 +1,12 @@
package com.alibaba.testable.core.error;
/**
* @author flin
*/
public class NoSuchMemberError extends Error {
public NoSuchMemberError(String message) {
super(message);
}
}

View File

@ -1,5 +1,6 @@
package com.alibaba.testable.core.tool;
import com.alibaba.testable.core.error.NoSuchMemberError;
import com.alibaba.testable.core.util.CollectionUtil;
import com.alibaba.testable.core.util.FixSizeMap;
import com.alibaba.testable.core.util.TypeUtil;
@ -67,6 +68,9 @@ public class OmniAccessor {
}
}
}
if (values.isEmpty()) {
throw new NoSuchMemberError("Query \"" + queryPath + "\"" + " does not match any member!");
}
return values;
}
@ -86,8 +90,9 @@ public class OmniAccessor {
List<Object> parent = getByPath(target, toParent(memberPath), toParent(queryPath));
if (!parent.isEmpty()) {
for (Object p : parent) {
setByPathSegment(p, toChild(memberPath), toChild(queryPath), value);
count++;
if (setByPathSegment(p, toChild(memberPath), toChild(queryPath), value)) {
count++;
}
}
}
} catch (NoSuchFieldException e) {
@ -97,6 +102,9 @@ public class OmniAccessor {
}
}
}
if (count == 0) {
throw new NoSuchMemberError("Query \"" + queryPath + "\"" + " does not match any member!");
}
return count;
}
@ -237,21 +245,26 @@ public class OmniAccessor {
return fullQuerySegments;
}
private static void setByPathSegment(Object target, String memberSegment, String querySegment, Object value)
private static boolean setByPathSegment(Object target, String memberSegment, String querySegment, Object value)
throws IllegalAccessException {
String name = extraNameFromMemberRecord(memberSegment);
int nth = extraIndexFromQuery(querySegment);
boolean isFieldMatch = false;
if (target.getClass().isArray()) {
for (int i = 0; i < Array.getLength(target); i++) {
setFieldByName(Array.get(target, i), name, nth, value);
isFieldMatch |= setFieldByName(Array.get(target, i), name, nth, value);
}
} else {
setFieldByName(target, name, nth, value);
isFieldMatch = setFieldByName(target, name, nth, value);
}
return isFieldMatch;
}
private static void setFieldByName(Object target, String name, int nth, Object value) throws IllegalAccessException {
private static boolean setFieldByName(Object target, String name, int nth, Object value) throws IllegalAccessException {
Field field = TypeUtil.getFieldByName(target.getClass(), name);
if (field == null) {
return false;
}
field.setAccessible(true);
if (field.getType().isArray()) {
Object f = field.get(target);
@ -265,6 +278,7 @@ public class OmniAccessor {
} else {
field.set(target, value);
}
return true;
}
}