Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何编写可维护的merge()方法?_Java_Merge_Maintainability - Fatal编程技术网

Java 如何编写可维护的merge()方法?

Java 如何编写可维护的merge()方法?,java,merge,maintainability,Java,Merge,Maintainability,假设我们有个人实体: class Person { /* once the id is assigned, then must not be modified! assume that the id will be assigned by the ORM framework */ int id; String givenName; String familyName; } 我有两个人:原始人和更新人: Person or

假设我们有个人实体:

class Person {
    /*
        once the id is assigned, then must not be modified! 
        assume that the id will be assigned by the ORM framework
    */
    int id;
    String givenName;
    String familyName;
}
我有两个人:原始人和更新人:

Person original = new Person("Frantisek", "Makovicka");
Person updated = new Person("Viktor", "Makovicka");
我想将更新的Person与原始Person合并,因此我编写了以下简单方法:

// return number of changed fields
public int merge(Person original, Person updated) {
    int changes = 0;

    String oldGivenName = original.givenName;
    original.givenName = updated.givenName;
    if (changed(oldGivenName, original.givenName)) changes++;

    String oldFamilyName = original.familyName;
    original.familyName = updated.familyName;
    if (changed(oldFamilyName, original.familyName)) changes++;

    return changes;
}
它工作正常,但我发现了一些问题:
每次向Person类添加新字段时,程序员都不应该忘记更新merge()方法,如果Person有很多字段,那么很难维护此方法


所以我的问题是:有没有更聪明/健壮的方法来合并对象的状态,而不使用语言的反射特性,这样您就可以确保合并所有和唯一需要的字段? 提前谢谢

UPD

最初我问是否有不使用反射的方式来写它,但忘了说这不是限制!我还应该说,我有一个想法,写这个方法时使用反射+注释“可合并”字段和一些自定义注释,然后跳过没有注释的字段。 因此,这句话的用意:“不使用反射”是为了发现其他可能不太明显的解决方案:)

这个问题的灵感来自于函数式方法:(它确保了资源将被关闭,仅将其作为不太明显的解决方案和安全编程的示例)

publicstaticvoiddowithresource(字符串名称,使用者){
Resource res=新资源(名称);
消费者接受(res);
res.close();
}

可能对您的
copyProperties(objecttocopyto,Obj ToCopyFrom)有帮助
可能对您的
copyProperties(objecttocopyto,Obj ToCopyFrom)有帮助
我看到了您需要的三个功能:

  • “id”不能修改
  • 应返回更改计数
  • 对“更新”的任何更改应适用于“原始”
特别是前两个需求是一个问题。我认为没有任何基于库的解决方案能够做到这一点。但是,您可以使用一些Java反射编写自己的“合并”:

private int merge(Person p1, Person p2) throws IllegalAccessException {
    int changes = 0;
    for(Field field: Person.class.getDeclaredFields()) {
        if(!field.getName().equals("id")) {
            field.setAccessible(true);
            Object originalField = field.get(p1);
            Object updatedField = field.get(p2);
            if(!originalField.equals(updatedField)) {
                field.set(p1, updatedField);
                changes++;
            }
        }
    }
    return changes;
}

我看到了您需要的3个功能:

  • “id”不能修改
  • 应返回更改计数
  • 对“更新”的任何更改应适用于“原始”
特别是前两个需求是一个问题。我认为没有任何基于库的解决方案能够做到这一点。但是,您可以使用一些Java反射编写自己的“合并”:

private int merge(Person p1, Person p2) throws IllegalAccessException {
    int changes = 0;
    for(Field field: Person.class.getDeclaredFields()) {
        if(!field.getName().equals("id")) {
            field.setAccessible(true);
            Object originalField = field.get(p1);
            Object updatedField = field.get(p2);
            if(!originalField.equals(updatedField)) {
                field.set(p1, updatedField);
                changes++;
            }
        }
    }
    return changes;
}
您可以使用来获取。这接近于使用反射,事实上java.beans包在内部使用反射,但它至少更干净一些,并且(大部分)仅限于bean方法(get/set/is方法):

但是,这始终执行浅复制。如果任何属性具有可变类型(如数组或集合类型,或可变对象类型,如理论上的地址),并且如果您的方法没有对此类类型进行防御性复制,则这两个对象将共享对象,这将导致令人沮丧的休眠bug

如果您的编码人员都知道执行防御性复制,或者如果您确定没有人会添加具有可变类型的属性,那么这不是问题。否则,它将变得足够复杂,可能不值得尝试自动复制属性;至少,您需要检查数组或集合类型并克隆值。

您可以使用来获取。这接近于使用反射,事实上java.beans包在内部使用反射,但它至少更干净一些,并且(大部分)仅限于bean方法(get/set/is方法):

但是,这始终执行浅复制。如果任何属性具有可变类型(如数组或集合类型,或可变对象类型,如理论上的地址),并且如果您的方法没有对此类类型进行防御性复制,则这两个对象将共享对象,这将导致令人沮丧的休眠bug


如果您的编码人员都知道执行防御性复制,或者如果您确定没有人会添加具有可变类型的属性,那么这不是问题。否则,它将变得足够复杂,可能不值得尝试自动复制属性;至少,您需要检查数组或集合类型并克隆值。

未定义更改。你一定是说改变++。按照现在的设置方式,您将始终从上述方法返回0。您正在使用更新的指针覆盖原始指针,在这种情况下,您可能应该只覆盖原始指针,而不调用合并方法。如果你必须这样做,我想你是在寻求反思。(主要区别在于,如果这两个类是扩展Person的不同类,那么合并是否应该关心?)您可以进行一些反射,并查看类的每个(可能是注释的)字段。“不使用语言的反射功能”——为什么不呢?@很可能您是对的,这只是一个输入错误,fixedchanged没有定义。你一定是说改变++。按照现在的设置方式,您将始终从上述方法返回0。您正在使用更新的指针覆盖原始指针,在这种情况下,您可能应该只覆盖原始指针,而不调用合并方法。如果你必须这样做,我想你是在寻求反思。(主要区别在于,如果这是两个扩展Person的不同类,合并是否应该关心?)您可以进行一些反射,并查看类的每个(可能是注释的)字段。“不使用语言的反射功能”——为什么不呢?@很可能您是对的,这只是一个键入错误,修复了这将覆盖所有属性,包括“id”不应修改。这将覆盖所有属性,包括不应修改的“id”。问题是“不使用反射”-我不知道为什么。@Thomas Uhrig谢谢你的回答!我想知道是否
public int merge(Person original, Person updated) {
    int changes = 0;

    try {
        BeanInfo info = Introspector.getBeanInfo(Person.class, Object.class);
        for (PropertyDescriptor property : info.getPropertyDescriptors()) {
            if (property.getName().equalsIgnoreCase("id")) {
                continue;
            }

            Method get = property.getReadMethod();
            Method set = property.getWriteMethod();
            if (set == null) {
                // Ignore read-only property.
                continue;
            }

            Object oldValue = get.invoke(original);
            Object newValue = get.invoke(updated);

            set.invoke(original, newValue);

            if (changed(oldValue, newValue)) {
                changes++;
            }
        }
    } catch (IntrospectionException | ReflectiveOperationException e) {
        // We should never get here.
        throw new RuntimeException(
            "Could not update properties of " + Person.class + ": " + e, e);
    }

    return changes;
}