04-31.eri-test 我第一次尝试泛型...

...事实证明我不需要它!

一段时间以来,我想尝试使用泛型。 当向我展示了使用Reflection的实现时,机会就击中了我。 这是我想让自己更加适应的两个主题,所以……理想的机会!

The Opportunity

我正在一个名为Restaurant的实体的控制器上实现PATCH HTTP请求,这是我正在学习的REST API课程的一部分。 讲师提出的实现是这样的(使用反射):

@PatchMapping("/{id}")
public ResponseEntity<?> partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> restaurantMap) {
    Restaurant restaurantToUpdate = restaurantCrudService.read(id);

    if (restaurantToUpdate == null) {
        return ResponseEntity.notFound().build();
    }

    merge(restaurantMap, restaurantToUpdate);

    return update(id, restaurantToUpdate);
}

public merge(Map<String, Object> objectMap, Restaurant restaurantToUpdate) {
    ObjectMapper objectMapper = new OjectMapper()
    Restaurant newObject = objectMapper.convertValue(objectMap, Restaurant.class);

    objectMap.forEach((fieldProp, valueProp) -> {
        Field field = ReflectionUtils.findField(Restaurant.class, fieldProp);
        field.setAccessible(true);

        Object newValue = ReflectionUtils.getField(field, newObject);

        ReflectionUtils.setField(field, objectToUpdate, newValue);
    });
}

而且,我必须在所有控制器上(对于其他实体)重新实现此方法。 因此,我尝试使用泛型创建解决方案。

The Solution

The Generic Class

这是表示上面代码的类,但牢记泛型:

public class ObjectMerger<T> {

    private ObjectMapper objectMapper;
    private Class<T> type;

    private ObjectMerger(Class<T> type) {
        this.objectMapper = new ObjectMapper();
        this.type = type;
    }

    public static ObjectMerger of(Class type) {
        return new ObjectMerger(type);
    }

    public void mergeRequestBodyToGenericObject(Map<String, Object> objectMap, T objectToUpdate) {
        T newObject = objectMapper.convertValue(objectMap, type);

        objectMap.forEach((fieldProp, valueProp) -> {
            Field field = ReflectionUtils.findField(type, fieldProp);
            field.setAccessible(true);

            Object newValue = ReflectionUtils.getField(field, newObject);

            ReflectionUtils.setField(field, objectToUpdate, newValue);
        });
    }
}

您可以看到,我可以使用以下任何其他类类型(因此称为“泛型”)实例化该对象的方法,该类将提供一种方法来映射Map<String, Object>到提供的对象类型。 这是泛型解决方案,用于通过PATCH HTTP请求对对象实施部分更新。

The new implementation for the PATCH HTTP method

从现在开始,这是任何Controller的PATCH HTTP方法的新实现(此处为Restaurant示例):

@PatchMapping("/{id}")
public ResponseEntity<?> partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> restaurantMap) {
    Restaurant restaurantToUpdate = restaurantCrudService.read(id);

    if (restaurantToUpdate == null) {
        return ResponseEntity.notFound().build();
    }

    ObjectMerger
        .of(Restaurant.class)
        .mergeRequestBodyToGenericObject(restaurantMap, restaurantToUpdate);

    return update(id, restaurantToUpdate);
}

Wait... I was wrong!

What I wanted to do was simply DRY.

不要重复自己,即一种解决方案,在这种情况下,我不会为PATCH HTTP请求重复我自己(在这种情况下)。 我以为泛型是解决方案,因为。。。原因! 好吧,我目前还不清楚。 我现在所记得的是,我还牢记我不想实例化对象新。

So, here it goes for historial purposes, the Generics implementation: the one where I was thinking with my ass.

And I didn't even need configuration.

public static ObjectMerger of(Class type) {
    return new ObjectMerger(type);
}

So I created a of method to return a new ObjectMerger: the one where someone helped me to make a bit of sense.

And to avoid instantiation I could cache on a map.

public static ObjectMerger of(Class type) {

    if (!cacheEnabled) {
        // Cache is not enabled. A new instance is always created.
        return new ObjectMerger(type);
    }

    if (!objectMergerCache.containsKey(type)) {
        ObjectMerger objectMerger = new ObjectMerger(type);
        objectMergerCache.put(type, objectMerger);
        // Cache enabled. Instance created (first request).
        return objectMerger;
    }

    // Cache enabled. Returning existing instance.
    return objectMergerCache.get(type);
}

This ideia led to this implementation: the one where I was almost there.

Well, what am I caching anyway?

public class ObjectMerger {

    public static void mergeRequestBodyToGenericObject(Map<String, Object> objectMap, Object objectToUpdate, Class type) {
        ObjectMapper objectMapper = new ObjectMapper();
        Object newObject = objectMapper.convertValue(objectMap, type);

        objectMap.forEach((fieldProp, valueProp) -> {
            Field field = ReflectionUtils.findField(type, fieldProp);
            field.setAccessible(true);

            Object newValue = ReflectionUtils.getField(field, newObject);

            ReflectionUtils.setField(field, objectToUpdate, newValue);
        });
    }
}

I didn't need any properties on the class, it could be a helper class, no state, just function. So I made a dead simple static method: the one where 一世 felt like an idiot.

And that's it

它正在工作,我学到了很多东西,我对自己感到非常满意。 哈哈哈哈哈。

You can see the history of this post here.
You can test the API here.


Image by U3100481

from: https://dev.to//brunodrugowick/my-first-attempt-at-generics-976

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值