针对不同Sample子类实例调用不同方法的设计模式咨询(Spring MVC)
Great question! This is a classic scenario where you want to keep your DTOs clean (free of business logic) while organizing type-specific behavior in separate components. Let’s break down the best approaches for Java (especially with Spring MVC) and non-Java technologies:
Java + Spring MVC Solutions
1. Visitor Pattern (Perfect for Separation of Concerns)
The Visitor pattern is tailor-made for this situation—it lets you define new operations on your hierarchy of Sample subclasses without modifying the DTOs themselves beyond a tiny hook. Here's how to implement it:
First, define a Visitor interface that declares a method for each subclass:
public interface SampleVisitor { void visit(Class1 class1); void visit(Class2 class2); void visit(Class3 class3); }
Then, add an accept method to your base Sample class (this is the only change needed in your DTOs, and it's just a routing hook, not business logic):
public abstract class Sample { public abstract void accept(SampleVisitor visitor); } // For each subclass: public class Class1 extends Sample { @Override public void accept(SampleVisitor visitor) { visitor.visit(this); } } // Repeat the accept method for Class2 and Class3
Now, create a concrete visitor that holds your business logic (mark it as a Spring @Component so it's managed by the container):
@Component public class SampleBusinessVisitor implements SampleVisitor { @Override public void visit(Class1 class1) { // Logic from method1 goes here validateClass1Data(class1); syncClass1ToDatabase(class1); } @Override public void visit(Class2 class2) { // Logic from method2 goes here generateClass2Report(class2); } @Override public void visit(Class3 class3) { // Logic from method3 goes here sendClass3Notification(class3); } // Helper methods with your business logic private void validateClass1Data(Class1 class1) { /* ... */ } private void syncClass1ToDatabase(Class1 class1) { /* ... */ } // ... }
In your Spring service/controller, inject the visitor and use it like this:
@Autowired private SampleBusinessVisitor visitor; public void processSample(Sample obj) { obj.accept(visitor); }
2. Strategy Pattern + Type Matching (No DTO Modifications)
If you don’t want to add any code to your DTOs at all, this approach is more flexible. Create a handler interface for type-specific processing:
public interface SampleHandler { boolean supports(Sample sample); void handle(Sample sample); }
Then create a handler for each subclass (use @Component for Spring management):
@Component public class Class1Handler implements SampleHandler { @Override public boolean supports(Sample sample) { return sample instanceof Class1; } @Override public void handle(Sample sample) { Class1 class1 = (Class1) sample; // Logic from method1 goes here } } // Repeat for Class2Handler and Class3Handler
Create a manager component to resolve the right handler automatically:
@Component public class SampleHandlerManager { private final List<SampleHandler> handlers; // Spring injects all SampleHandler beans automatically via constructor public SampleHandlerManager(List<SampleHandler> handlers) { this.handlers = handlers; } public void processSample(Sample sample) { handlers.stream() .filter(handler -> handler.supports(sample)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("No handler found for sample type")) .handle(sample); } }
Now you can inject this manager into your controllers/services and process DTOs without touching the original classes.
Non-Java Solutions
If you’re open to other languages, here are some concise alternatives that avoid polluting DTOs:
Kotlin
Kotlin’s when expression with smart casts and extension functions makes this incredibly clean:
// Define extension functions for each subclass (kept separate from DTOs) fun Class1.process() { /* method1 logic */ } fun Class2.process() { /* method2 logic */ } fun Class3.process() { /* method3 logic */ } // Usage fun handleSample(obj: Sample) { when(obj) { is Class1 -> obj.process() is Class2 -> obj.process() is Class3 -> obj.process() else -> throw IllegalArgumentException("Unsupported sample type") } }
Go
Use type assertions with a switch case to route to type-specific functions:
type Sample interface{} type Class1 struct{} type Class2 struct{} type Class3 struct{} func processClass1(c *Class1) { /* method1 logic */ } func processClass2(c *Class2) { /* method2 logic */ } func processClass3(c *Class3) { /* method3 logic */ } func HandleSample(obj Sample) { switch v := obj.(type) { case *Class1: processClass1(v) case *Class2: processClass2(v) case *Class3: processClass3(v) default: panic("unsupported sample type") } }
Python (3.10+)
Use pattern matching with match statements to handle type-specific logic:
class Sample: pass class Class1(Sample): pass class Class2(Sample): pass class Class3(Sample): pass def process_sample(obj: Sample): match obj: case Class1(): # method1 logic print("Processing Class1") case Class2(): # method2 logic print("Processing Class2") case Class3(): # method3 logic print("Processing Class3") case _: raise ValueError("Unsupported sample type")
Both the Visitor and Strategy patterns integrate seamlessly with Spring MVC—you can inject the visitor/handler manager into your controllers to process incoming DTOs directly from HTTP requests.
内容的提问来源于stack exchange,提问作者Farrukh Chishti

