You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免OpenSearch Java客户端TypeMapping构建的if-else代码异味

针对OpenSearch Java客户端映射构建的代码异味解决方案

1. 用策略模式重构getTypeProperty,消灭大量if-else

不要用枚举实现策略,改用职责单一的Handler类+映射Map的方案,完美适配第三方库的异构Builder:

  • 定义统一的PropertyHandler接口,包含主属性和子属性的构建方法(解决子属性处理问题):
    public interface PropertyHandler {
        Property.Builder buildProperty(FieldMetadata metadata);
        Property.Builder buildSubProperty(FieldMetadata subFieldMetadata);
    }
    
  • 为每种Property.Kind写独立的Handler实现类,比如TextPropertyHandler、NestedPropertyHandler,每个类只负责对应类型的Builder构建逻辑,包括子属性的递归处理(比如Nested类型内部的字段):
    public class NestedPropertyHandler implements PropertyHandler {
        @Override
        public Property.Builder buildProperty(FieldMetadata metadata) {
            NestedProperty.Builder builder = NestedProperty.builder();
            // 配置嵌套类型专属参数,如include_in_parent
            for (FieldMetadata subField : metadata.getSubFields()) {
                Property.Builder subProp = HANDLER_MAP.get(subField.getKind()).buildSubProperty(subField);
                builder.subField(subField.getName(), subProp.build());
            }
            return builder;
        }
    
        @Override
        public Property.Builder buildSubProperty(FieldMetadata subFieldMetadata) {
            return buildProperty(subFieldMetadata);
        }
    }
    
  • 用静态初始化的EnumMap存储类型与Handler的映射,启动时完成注册:
    private static final Map<Property.Kind, PropertyHandler> HANDLER_MAP;
    
    static {
        HANDLER_MAP = new EnumMap<>(Property.Kind.class);
        HANDLER_MAP.put(Property.Kind.TEXT, new TextPropertyHandler());
        HANDLER_MAP.put(Property.Kind.KEYWORD, new KeywordPropertyHandler());
        HANDLER_MAP.put(Property.Kind.NESTED, new NestedPropertyHandler());
        // 其他类型依次添加即可
    }
    
  • 原getTypeProperty方法简化为一行映射调用,新增类型只需加Handler和Map条目,完全符合开闭原则:
    public Property.Builder getTypeProperty(FieldMetadata metadata) {
        PropertyHandler handler = HANDLER_MAP.get(metadata.getKind());
        if (handler == null) {
            throw new IllegalArgumentException("Unsupported property kind: " + metadata.getKind());
        }
        return handler.buildProperty(metadata);
    }
    

2. 抽象工厂统一两种构建入口

针对TypeMappingBuilder和注解式MappingFactory的重复映射逻辑,用抽象工厂统一封装:

  • 定义TypeMappingFactory抽象接口,覆盖两种构建场景:
    public interface TypeMappingFactory {
        TypeMapping.Builder buildTypeMapping(Class<?> entityClass);
        TypeMapping.Builder buildTypeMapping(Map<String, FieldMetadata> fieldMetadataMap);
    }
    
  • 实现两个具体工厂:
    • AnnotationBasedMappingFactory:负责解析实体类注解,转换为FieldMetadata集合后,复用上面的PropertyHandler构建所有属性;
    • BuilderBasedMappingFactory:封装原生TypeMappingBuilder的链式调用,内部同样通过HANDLER_MAP处理不同类型的属性添加。
  • 这样两种构建方式共享同一套属性构建逻辑,新增类型时无需分别修改两个入口,消除重复代码。

3. 为何放弃枚举策略?

枚举策略的问题在于:所有逻辑硬编码在枚举类中,类型增多后会变得极度臃肿;枚举实例是静态单例,无法灵活适配不同的配置场景;而且枚举类无法拆分,违反单一职责原则。而独立的Handler类可以单独测试、单独维护,扩展性更强。

额外优化(Spring环境)

如果是Spring项目,可通过@Component标记所有PropertyHandler,再用@Autowired自动注入到Map中,无需手动维护静态Map的初始化:

@Autowired
private Map<Property.Kind, PropertyHandler> handlerMap;

Spring会自动将实现PropertyHandler的Bean以Property.Kind为Key注入,进一步降低维护成本。

内容的提问来源于stack exchange,提问作者contemplator

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 18:56:14