还在用if-else?快用策略模式写出优雅代码吧!!

1,171 阅读3分钟

日常中经常会用到策略模式完成业务:这里举例三个功能模块,分别是竣工文档,项目过程和会议文档,这三个都会调用一个通用接口,但是会由前端参数的区别而去调用自己的业务逻辑

对于这种通用接口实现,根据前端传递的参数调用不同业务逻辑,可以按照以下方式设计:

1. 接口定义

设计一个统一的接口入口,参数中包含明确区分模块类型的字段(例如 moduleType),后端根据该字段调用对应模块的业务逻辑。

示例接口定义:

@RestController
@RequestMapping("/document")
public class DocumentController {

    @PostMapping("/process")
    public ResponseEntity<?> processDocument(@RequestBody DocumentRequest request) {
        String moduleType = request.getModuleType();
        //也可能会用if-else
        switch (moduleType) {
            case "completionDocument":
                return ResponseEntity.ok(completionDocumentService.process(request));
            case "projectProcess":
                return ResponseEntity.ok(projectProcessService.process(request));
            case "meetingDocument":
                return ResponseEntity.ok(meetingDocumentService.process(request));
            default:
                return ResponseEntity.badRequest().body("Invalid module type");
        }
    }
}

2. 业务逻辑接口抽象

为了更好的扩展性,可以将每个模块的业务逻辑抽象成一个接口,各模块实现自己的逻辑。

定义接口:

public interface DocumentService {
    Object process(DocumentRequest request);
}

模块实现:

@Service
public class CompletionDocumentService implements DocumentService {
    @Override
    public Object process(DocumentRequest request) {
        // 处理竣工文档逻辑
        return "Processed Completion Document";
    }
}

@Service
public class ProjectProcessService implements DocumentService {
    @Override
    public Object process(DocumentRequest request) {
        // 处理项目过程逻辑
        return "Processed Project Process Document";
    }
}

@Service
public class MeetingDocumentService implements DocumentService {
    @Override
    public Object process(DocumentRequest request) {
        // 处理会议文档逻辑
        return "Processed Meeting Document";
    }
}

3. 优化:策略模式

通过 Spring 的策略模式 优化代码,避免在控制器中硬编码 switch

定义策略注解和工厂类:

创建模块类型枚举:

public enum ModuleType {
    COMPLETION_DOCUMENT, PROJECT_PROCESS, MEETING_DOCUMENT
}

模块类型注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ModuleHandler {
    ModuleType value();
}

策略工厂:

@Component
public class DocumentStrategyFactory {
    private final Map<ModuleType, DocumentService> strategyMap = new HashMap<>();

    @Autowired
    public DocumentStrategyFactory(List<DocumentService> strategies) {
        for (DocumentService strategy : strategies) {
            ModuleHandler annotation = strategy.getClass().getAnnotation(ModuleHandler.class);
            if (annotation != null) {
                strategyMap.put(annotation.value(), strategy);
            }
        }
    }

    public DocumentService getStrategy(ModuleType type) {
        return strategyMap.get(type);
    }
}

各模块实现:

在各服务类上添加注解:

@Service
@ModuleHandler(ModuleType.COMPLETION_DOCUMENT)
public class CompletionDocumentService implements DocumentService {
    @Override
    public Object process(DocumentRequest request) {
        // 处理竣工文档逻辑
        return "Processed Completion Document";
    }
}

控制器调用:

@RestController
@RequestMapping("/document")
public class DocumentController {

    private final DocumentStrategyFactory strategyFactory;

    @Autowired
    public DocumentController(DocumentStrategyFactory strategyFactory) {
        this.strategyFactory = strategyFactory;
    }

    @PostMapping("/process")
    public ResponseEntity<?> processDocument(@RequestBody DocumentRequest request) {
        ModuleType type = ModuleType.valueOf(request.getModuleType().toUpperCase());
        DocumentService service = strategyFactory.getStrategy(type);
        if (service == null) {
            return ResponseEntity.badRequest().body("Invalid module type");
        }
        return ResponseEntity.ok(service.process(request));
    }
}

4. 优点

  • 高内聚低耦合:不同模块的业务逻辑分离,便于扩展新模块。
  • 可维护性强:使用策略模式,新增模块时无需修改原代码,只需实现 DocumentService 并添加注解。
  • 统一接口:前端只需调用一个接口,后端根据业务需求分发到不同逻辑。

这种设计可以满足你的需求,并且对未来需求变更具有较好的扩展性。

5.更改

注解在上面的写法中只起到一个作为Map的Key来当身份标识的作用,这里不想用注解可以在枚举中添加属性。

@Getter
@AllArgsConstructor
public enum ModuleType {
    PROJECT_PROCESS(1,"项目过程),
    MEETING_DOCUMENT(2,"会议文档"),
    COMPLETION_DOCUMENT(3,"竣工文档");

    private Integer value;
    private String desc;

    //根据value获取枚举
    public static FileNameSchema getEnum(Integer value) {
        return Arrays.stream(FileNameSchema.values()).filter(item -> Objects.equals(item.getValue(), value)).findFirst().orElse(null);
    }
}

定义接口:

public interface DocumentService {
    Integer getIdentity();
    Object process(DocumentRequest request);
}

模块实现:

@Service
public class CompletionDocumentService implements DocumentService {
    @Override
    public Integer getIdentity() {
        return ModuleType.COMPLETION_DOCUMENT.getValue();
    }
    
    @Override
    public Object process(DocumentRequest request) {
        // 处理竣工文档逻辑
        return "Processed Completion Document";
    }
}

@Service
public class ProjectProcessService implements DocumentService {
    @Override
    public Integer getIdentity() {
        return ModuleType.PROJECT_PROCESS.getValue();
    }
    
    @Override
    public Object process(DocumentRequest request) {
        // 处理项目过程逻辑
        return "Processed Project Process Document";
    }
}

@Service
public class MeetingDocumentService implements DocumentService {
    @Override
    public Integer getIdentity() {
        return ModuleType.MEETING_DOCUMENT.getValue();
    }
    
    @Override
    public Object process(DocumentRequest request) {
        // 处理会议文档逻辑
        return "Processed Meeting Document";
    }
}

控制器调用:

@RestController
@RequestMapping("/document")
public class DocumentController {

    private final Map<Integer, DocumentService> strategyMap = new HashMap<>();

    @Resource
    private List<DocumentService> strategies;

    @PostConstruct
    void initStrategyMap() {
        for (DocumentService strategy : strategies) {
            strategyMap.put(strategy.getIdentity(), strategy);
        }
    }

    public DocumentService getStrategy(FileNameSchema schema) {
        return strategyMap.get(schema.getValue());
    }

    @PostMapping("/process")
    public ResponseEntity<?> processDocument(@RequestBody DocumentRequest request) {
        ModuleType TypeEnum = ModuleType.getEnum(param.getAttachmentType());
        DocumentService documentService = getStrategy(TypeEnum);
        if (service == null) {
            return ResponseEntity.badRequest().body("Invalid module type");
        }
        return ResponseEntity.ok(service.process(request));
    }
}