Java对象中更新字段的正确方法(设计模式) [英] Correct approach(design pattern) for update fields in java object

查看:215
本文介绍了Java对象中更新字段的正确方法(设计模式)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有Java类 Person :

I have java class Person:

public class Person {
    private String name;
    private int age;
    private String marriedStatus;
    private Date dob;
    //getters and setters
}

当获得该对象某些字段的新值时,可以对其进行更新.但是新字段会以以下格式对收入进行估价: Map< String,String>newValues 其中key-字段号和value-字段的值.我创建了此服务:

When I get new values for some fields of this object I can update it. But new fields values income in this format: Map<String, String> newValues where key - field number and value - the value of the field. I create this service:

public class UpdateService {
    public Person updateFields(Person targetPerson, Map<String, String> newValues){
        return null;
    }
}

我创建了一个单元测试,并请您帮助实现它.

I create a unit test and ask you to help implement this.

public class UpdateServiceTest {

    /*associations between field number and field name
    12 - name (length min: 2, max: 20. First letter must uppercase )
    18 - marriedStatus (only married, divorced, single)
    21  - age (only between 18 and 120)
    14  - dob (some format)
     */

    private Date dob;

    @Before
    public void setUp() {
        dob = new GregorianCalendar(2000, Calendar.NOVEMBER, 20).getTime();
    }

    @Test
    public void returnPersonWithUpdatedFields() {
        UpdateService updateService = new UpdateService();

        Person targetPerson = new Person();
        targetPerson.setName("Name");
        targetPerson.setMarriedStatus("MarriedStatus");
        targetPerson.setAge(20);
        targetPerson.setDob(dob);

        Map<String, String> newValues = new HashMap<String, String>();
        newValues.put("12", "Bill");
        newValues.put("18", "married ");
        newValues.put("21", "25");

        Person person = updateService.updateFields(targetPerson, newValues);

        assertEquals("Bill", person.getName());
        assertEquals("married", person.getMarriedStatus());
        assertEquals(25, person.getAge());
        assertEquals(dob, person.getDob());
    }
}

我需要找人并且只更新在 Map< String,String>newValues .并对其进行验证.

I need to get the person and update only fields which income in Map<String, String> newValues. And validate it.

推荐答案

以下是有关如何执行此操作的建议.

Here's a suggestion how you could do this.

将每个字段建模为一个单独的类,实现一个通用的 Field 接口.

Model each field as a separate class, implementing a common Field interface.

通过构造函数注入所有已知字段,并在收到映射时,为每个条目查找匹配的字段类以处理验证和更新.

Inject all known fields via the constructor and when a map is received, for each entry look up the matching field class to handle validation and updating.

使用这种方法,您可以分别测试每个字段的验证和更新逻辑.添加人员字段不会使您的服务等级增加.测试PersonUpdateService只需使用一个或两个模拟字段即可验证查找和执行逻辑.我会说的关注点很好地分开了.

With this approach you can test each field's validation and updating logic separately. Adding person fields will not make your service class grow. Testing the PersonUpdateService will only need to use one or two mocked Fields to validate the lookup and execution logic. A nice separation of concerns I'd say.

import java.util.*;

@Component
public class PersonUpdateService {

    private final List<Field> fields;

    @Autowired
    public PersonUpdateService(final List<Field> fields) {
        this.fields = fields;
    }

    public void updatePerson(final Person person, final Map<String, String> update) {
        final boolean updated = false;
        update.forEach((key, value) -> this.findField(key).update(person, value));
    }

    private Field findField(final String index) {
        return this.fields.stream().filter(f -> f.index().equals(index)).findAny().orElseThrow(
                () -> new IllegalArgumentException("Field not found: " + index));
    }

}

现场界面:

public interface Field {
    String index();

    void update(Person person, String newValue);
}

示例字段实现:

import java.util.regex.Pattern;

@Component
public class NameField implements Field {

    private static final String INDEX = "12";
    private static final String REGEX = "/^[A-Z][a-z0-9_-]{1,19}$/";
    private static final String CONSTRAINTS = "length min: 2, max: 20. First letter must uppercase";

    @Override
    public String index() {
        return INDEX;
    }

    @Override
    public void update(final Person person, final String newValue) {
        if (!Pattern.matches(REGEX, newValue)) {
            throw new ValidationException(CONSTRAINTS);
        }
        person.setName(newValue);
    }

}

添加了@Component和@Autowired批注,以指示在Spring中如何使用依赖项注入.Spring会自动收集实现Field接口的可用组件,并通过构造函数将其注入.在服务的单元测试中,您可以注入一个或两个模拟字段.不要在服务测试中测试字段实现的实际验证/更新逻辑,而是为每个字段类创建单独的单元测试.

added @Component and @Autowired annotations to indicate how dependency injection could be used in Spring. The available components implementing the Field interface will automatically be collected by Spring and injected via the constructor. In your unit test for the service you can inject one or two mock Fields. Don't test the actual validation/updating logic of the field implementations in the service test, but create separate unit tests for each field class.

以上关于编写单元测试的建议是从我(模拟主义者)的角度来看的.古典的单元测试人员可能会编写一个测试来涵盖完整的规格(例如您在文章中提供的测试).我不喜欢这样做的原因是,在这种集成样式的单元测试中,边缘案例更容易丢失,并且由于您对代码的工作方式做出错误的假设或者必须在代码中反复执行相同的代码而增加了机会测试.但是,这是一个长期的辩论,有多种观点,各有千秋.

EDIT 2: the above advice about writing unit tests is from my (mockist) perspective. Classical unit testers would probably write a single test to cover the complete specs (like the test you provided in the post). The reason I don't prefer that is that edge cases get lost easier in such an integration-style unit test and the chance increases that you are making false assumptions about how the code works or that you have to repeatedly exercise the same code in your tests. However this is a long-standing debate and there are multiple points of view which all have their merits.

这篇关于Java对象中更新字段的正确方法(设计模式)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆