简体   繁体   English

设计 - 在执行每个方法之前检查条件

[英]Design - Check a condition before executing every method

I have a POJO named Document.java with 100+ member variables. 我有一个名为Document.java的POJO,包含100多个成员变量。 There is a transformation layer, where I get the required data, transform it and store it in the Document class. 有一个转换层,我获取所需的数据,转换它并将其存储在Document类中。

In the tranformation layer, I would like to set a member variable only if satisfies a certain criteria (based on available context). 在转换层中,我想仅在满足特定条件(基于可用上下文)时才设置成员变量。

So it would look something like this: 所以它看起来像这样:

if(shouldGetExecuted1(context.getXXX())){
  document.setField1(tranformDataForField1(availableData1));
}

if(shouldGetExecuted2(context.getXXX())){
  document.setField2(tranformDataForField2(availableData2));
}

I want to do this for all the 100+ fields. 我想为所有100多个领域做到这一点。 Is there a clean way to do this? 有干净的方法吗?

Additional information 附加信息

I don't want to use Strategy here as it would create too many classes as the no of strategies grow. 我不想在这里使用策略,因为它会创建太多的类,因为策略的增长。

Try to use AOP. 尝试使用AOP。 AspectJ allows you to define pointcuts (for example, some filtered set of methods) and control their execution via advices ( before method call, after , around ): AspectJ允许您定义切入点 (例如,一些过滤的方法集)并通过建议控制它们的执行( 方法调用之前之后周围 ):

@Aspect
class ClassName {
...

@PointCut("call(public void ClassName.*(..))") //includes all void methods of ClassName object 
public void myPointCut(){}

@Around("myPointCut()")
public void myLogicMethod(ProceedingJoinPoint thisJoinPoint) {

    if(shouldGetExecuted1(context.getXXX())){
        thisJoinPoint.proceed()
    }
}
}

Here thisJoinPoint.proceed() will execute the body of the intercepted method. 这里, thisJoinPoint.proceed()将执行截获方法的主体。

Read docs about how to define pointcuts. 阅读有关如何定义切入点的文档。 In this example the same logic will be applied to all void methods of this class. 在此示例中,相同的逻辑将应用于此类的所有void方法。 You can define more accurate pointcuts via special expressions to provide different logic for each. 您可以通过特殊表达式定义更准确的切入点,以便为每个切入点提供不同的逻辑。

No, there is no clean way to do it in Java. 不,在Java中没有干净的方法。 You can find methods using reflection but there is no way to find variables such as "availableDataN". 您可以使用反射找到方法,但无法找到诸如“availableDataN”之类的变量。 So you necessarily need to make "availableDataN" a field in order to find it using reflection. 因此,您必须将“availableDataN”设为字段才能使用反射来查找它。

The final code would be something as ugly as the following: 最终的代码将是如下的丑陋:

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class X {

    public static void main(String[] args) {

        for (int i = 0; i < 100; i++) {

            Method shouldGetExecuted = X.class.getMethod("shouldGetExecuted" + i, String.class);
            boolean b = (boolean) shouldGetExecuted.invoke(null, context.getXXX());
            if (b) {
                Method tranformDataForField = X.class.getMethod("tranformDataForField");
                Field data = X.class.getField("availableData" + i); 
                Object result = tranformDataForField.invoke(null, data.get(null));
                Method set = X.class.getMethod("setField" + i, TransformDataType.class);
                set.invoke(null, result);
            }
        }
    }

}

You need to adapt to your specific case. 您需要适应您的具体情况。 For instance, here I am assuming all fields and methods are static. 例如,在这里我假设所有字段和方法都是静态的。 If they are not, then you need to replace null with an instance reference. 如果不是,则需要将null替换为实例引用。

If you are consistent in the naming of your methods, reflection could help a lot. 如果您对方法的命名保持一致,那么反思可能会有很大帮助。

The following code assumes the following: 以下代码假定以下内容:

  • A Document class with fields like xxx or xxYy (getters/setters would be usually present but are not required for the code to work) 具有xxxxxYy等字段的Document类(通常会出现getter / setter但代码不需要这些字段)
  • A Transformer class that has 一个Transformer
    • the capability to determine based on context information, if a field should be processed. 如果应该处理字段,则基于上下文信息确定的能力。 These methods are named shouldTransformXxx(context) . 这些方法名为shouldTransformXxx(context)
    • the capability to transform the content of the field (with input and output of the same type as the corresponding field in Document ). 转换字段内容的能力(输入和输出与Document相应字段的类型相同)。 These methods are named T transformXxx(T) . 这些方法命名为T transformXxx(T)
  • A DataProvider class that has methods to provide the untransformed data. 一个DataProvider类,它具有提供未转换数据的方法。 these methods are named findXxx() 这些方法名为findXxx()

The code below is pretty optimistic - it will fail, if a shouldTransformXxx for any field misses, or if it returns true, the same applies for the findXxx and transformXxx methods. 下面的代码非常乐观 - 它将失败,如果任何字段的shouldTransformXxx未命中,或者如果它返回true,则同样适用于findXxxtransformXxx方法。 So you would have to create classes with 100 methods each, which seems non-ideal for me. 所以你必须创建每个包含100个方法的类,这对我来说似乎不太理想。 But on the other hand, having a class with 100 members seems to lead to awkward situations anyway... 但另一方面,拥有100名成员的班级似乎无论如何都会导致尴尬的局面......

So here's the code: 所以这是代码:

public class Document {
    private String name;
    private int size;

    @Override
    public String toString() {
        return "Document [name=" + name + ", size=" + size + "]";
    }
}

public class Transformer {
    public enum ContextType {
        NAME, SIZE
    }

    public boolean shouldTransformName(Set<ContextType> context) {
        return context.contains(ContextType.NAME);
    }

    public boolean shouldTransformSize(Set<ContextType> context) {
        return context.contains(ContextType.SIZE);
    }

    public String transformName(String name) {
        return "::" + name;
    }

    public int transformSize(int size) {
        return size + 1;
    }
}

public class DataProvider {
    private final String name;
    private final int size;

    public DataProvider(String name, int size) {
        this.name = name;
        this.size = size;
    }

    public String findName() {
        return name;
    }

    public int findSize() {
        return size;
    }
}

public class Main {

    private static final String TRANSFORM_METHOD_PREFIX = "transform";
    private static final String CHECK_METHOD_PREFIX = "shouldTransform";
    private static final String DATAPROVIDER_METHOD_PREFIX = "find";

    private final DataProvider dataProvider;
    private final Transformer transformer;

    public Main(DataProvider dataProvider, Transformer transformer) {
        this.dataProvider = dataProvider;
        this.transformer = transformer;
    }

    public Document transformFields(Set<ContextType> context)
            throws ReflectiveOperationException {
        Document document = new Document();

        for (Field field : Document.class.getDeclaredFields()) {

            String capitalizedfieldName = capitalize(field.getName());
            Class<?> fieldType = field.getType();

            if (shouldTransform(context, capitalizedfieldName)) {

                Object data = findData(capitalizedfieldName);
                Object transformed = transformData(capitalizedfieldName,
                        fieldType, data);

                // in presence of a security manager, a reflective call of the
                // setter could be performed
                field.setAccessible(true);
                field.set(document, transformed);
            }
        }

        return document;
    }

    private Object transformData(String capitalizedfieldName,
            Class<?> fieldType, Object data)
            throws ReflectiveOperationException {

        String methodName = TRANSFORM_METHOD_PREFIX + capitalizedfieldName;
        Method method = Transformer.class.getMethod(methodName, fieldType);
        return method.invoke(transformer, data);
    }

    private Object findData(String capitalizedfieldName)
            throws ReflectiveOperationException {

        String methodName = DATAPROVIDER_METHOD_PREFIX + capitalizedfieldName;
        Method method = DataProvider.class.getMethod(methodName);
        return method.invoke(dataProvider);
    }

    private boolean shouldTransform(Set<ContextType> context,
            String capitalizedfieldName) throws ReflectiveOperationException {

        String methodName = CHECK_METHOD_PREFIX + capitalizedfieldName;
        Method method = Transformer.class.getMethod(methodName, Set.class);
        return (Boolean) method.invoke(transformer, context);
    }

    private String capitalize(String fieldName) {
        char upperCaseFirstChar = Character.toUpperCase(fieldName.charAt(0));

        if (fieldName.length() > 1) {
            return upperCaseFirstChar + fieldName.substring(1);
        } else {
            return Character.toString(upperCaseFirstChar);
        }
    }

    public static void main(String[] args) throws ReflectiveOperationException {
        DataProvider dataProvider = new DataProvider("sample", 1);
        Set<ContextType> context = EnumSet.of(ContextType.NAME,
                ContextType.SIZE);

        Main main = new Main(dataProvider, new Transformer());
        Document document = main.transformFields(context);

        System.out.println(document);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM