检查对象是否在Java中正确构建

检查对象是否在Java中正确构建,java,design-patterns,object-oriented-analysis,Java,Design Patterns,Object Oriented Analysis,这是我遇到的一个普遍问题。我想知道是否有人知道任何适合的设计模式或技术 private ExternalObject personObject; private String name; private int age; private String address; private String postCode; public MyBuilderClass(ExternalObject obj) this.personObject=obj; build(); } p

这是我遇到的一个普遍问题。我想知道是否有人知道任何适合的设计模式或技术

private ExternalObject personObject; 
private String name;
private int age;
private String address;
private String postCode;

public MyBuilderClass(ExternalObject obj)
     this.personObject=obj;
     build();
}

public build() {
    setName(personObject.getName());
    setAge(personObject.getAge());
    setAddress(personObject.getAddress());
    setPostCode(personObject.getPostCode());
    .
    .
    . many more setters
}
上面的类从队列中获取外部对象并构造MyBuilderClass对象

如果所有字段都已设置为非null非空值,则会成功生成MyBuilderClass对象

将有许多MyBuilderClass对象无法生成,因为ExternalObject中缺少数据

我的问题是,检测对象是否正确构建的最佳方法是什么

  • 我可以在set方法中检查null或空值,并抛出异常。这种方法的问题是抛出异常代价高昂,并且会阻塞日志文件,因为会有许多无法构建对象的实例

我还可以使用其他方法吗?

根据我遇到的情况,您可以覆盖对象的equals方法,并将其与有效的示例对象进行比较。它很脏,可能只在某些情况下起作用


你的方法是我能想到的最好的方法。编写一个单独的方法或类,例如静态验证方法。您可以在任何地方重复使用它。

正如前面的答案所示,这里有两个选项,在您尝试设置变量后,应该添加其中一个

使用反射检查是否有任何变量为空。(如注释中所述,这将检查此对象中的所有字段,但请注意任何超类中的字段)

对每个变量执行空检查

    boolean isValidObject = !Stream.of(name, age, ...).anyMatch(Objects::isNull);

如果我错了,请纠正我:您正在尝试找到一种检查对象是否有效的好方法,如果无效,请在不使用异常的情况下告诉客户端代码

您可以尝试工厂方法:

private MyBuilderClass(ExternalObject obj)
     this.personObject=obj;
     build();
}

public static MyBuilderClass initWithExternalObject(ExternalObject obj) {
    // check obj's properties...
    if (obj.getSomeProperty() == null && ...) {
        //  invalid external object, so return null
        return null;
    } else {
        // valid
        MyBuilderClass builder = new MyBuilderClass(obj);
        return builder.build();
    }
}

现在您知道了对象是否在不使用异常的情况下有效。您只需要检查
initWithExternalObject
返回的值是否为null。

在非异常的情况下,我不会抛出异常。由于构造函数不生成对象的唯一方法是抛出,因此不应延迟对构造函数的验证

如果构造函数的结果无效,我仍然建议它抛出,但是在这之前应该有一个验证,所以你甚至不用无效的
ExternalObject
调用构造函数


如果您想将其作为静态方法实现,则由您决定。
布尔MyBuilderClass.validate(ExternalObject)
,或者使用此验证的生成器模式。

这种验证的另一种方法是使用java
注释。

  • 制作一个简单的注释类,比如说
    Validate

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface Validate {
    boolean required() default true;
    }
    
  • 然后将要显示的字段注释为
    @Validate(required=true)

  • 然后在
    MyBuilderClass
    类中添加此方法,以检查对象是否正确构建:

    public boolean isCorrectlyBuilt() throws IllegalAccessException {
       boolean retVal = true;
      for (Field f : getClass().getDeclaredFields()) {
        f.setAccessible(true);
        boolean isToBeChecked = f.isAnnotationPresent(Validate.class);
        if (isToBeChecked) {
            Validate validate = f.getAnnotation(Validate.class);
            if (validate.required()/*==true*/) {
                if (f.get(this) == null) {
                    retVal = false;
                    break;
                    /* return false; */
                }
            }
        }
    }
    return retVal;
    }
    
  • 以下是一个使用示例:

     public static void main(String[] args) throws Exception {
      ExternalObject personObject = new ExternalObject();
      personObject.setAge(20);
      personObject.setName("Musta");
      personObject.setAddress("Home");
      personObject.setPostCode("123445678");
    
      MyBuilderClass myBuilderClass = new MyBuilderClass(personObject);
      System.out.println(myBuilderClass.isCorrectlyBuilt());
    
    }

  • 输出
    true
    ,因为对象已正确构建


    这将允许您通过反射选择要包含在结构中的字段,而无需从基类继承这些字段。

    您尝试过生成器模式吗?要么是对的,要么不是built@Stultuske我不能100%确定生成器模式如何处理缺少的必需值,即尝试将必需值设置为null或空?这取决于您如何实现它。很多公司都有一个断言,如果不是所有的强制信息都存在,那么构建就会失败。因此,要么equals方法是垃圾,要么所有的“有效”实例都需要具有相同的值?这非常像是一个注释,而不是问题的答案。我的意思是,我通过equals尝试给出了提示。值得一想。他几乎用最有效的尝试回答了自己的问题。我没有投票,因为反射方法检查这个类中声明的所有字段是否为非null。继承字段(在您的方法中丢失)或助手字段(选中,尽管null对它们来说可能还可以)怎么样?我认为您的逻辑是错误的。所有字段都不应为空,但您正在检查它们是否都为空。这两个点都很公平,让我澄清一下。然而,它们只是用来说明可以使用的方法。反射确实是不合适的,而且速度很慢。我不认为反射应该作为一种选择而被忽视。我知道这比直接访问字段值要慢,但这些检查只在创建时执行一次,因此开销可以很好地接受。与使用直接访问检查每个字段值相比,这种方法的好处是,如果构建器类被扩展为具有其他字段,那么开发人员就不需要记住将这些字段添加到空检查中。此外,反射检查可以在其他构建器上重用
    public boolean isCorrectlyBuilt() throws IllegalAccessException {
       boolean retVal = true;
      for (Field f : getClass().getDeclaredFields()) {
        f.setAccessible(true);
        boolean isToBeChecked = f.isAnnotationPresent(Validate.class);
        if (isToBeChecked) {
            Validate validate = f.getAnnotation(Validate.class);
            if (validate.required()/*==true*/) {
                if (f.get(this) == null) {
                    retVal = false;
                    break;
                    /* return false; */
                }
            }
        }
    }
    return retVal;
    }
    
     public static void main(String[] args) throws Exception {
      ExternalObject personObject = new ExternalObject();
      personObject.setAge(20);
      personObject.setName("Musta");
      personObject.setAddress("Home");
      personObject.setPostCode("123445678");
    
      MyBuilderClass myBuilderClass = new MyBuilderClass(personObject);
      System.out.println(myBuilderClass.isCorrectlyBuilt());