ActionResult Architecture
Overview
ActionResult serves as the universal return type for all actions in the Brobot framework, encapsulating comprehensive information generated during action execution. Version 2.0 introduces a component-based architecture that transforms the original 1113-line monolithic class into a well-structured system of focused, single-responsibility components.
Architecture Evolution
Legacy Architecture (v1.x)
The original ActionResult was a monolithic class handling 30+ responsibilities:
- Match collection management
- Text extraction and aggregation
- Timing and duration tracking
- Scene analysis data
- Region definitions
- Movement tracking
- Execution history
- State management
- Logging and metrics
- Sorting and filtering operations
- Statistical calculations
- Serialization concerns
Component-Based Architecture (v2.0)
The refactored architecture delegates responsibilities to specialized components while maintaining complete backward compatibility through a facade pattern.
┌─────────────────────────────────────────────────────────────────┐
│ ActionResult │
│ (Facade) │
├─────────────────────────────────────────────────────────────────┤
│ Core Fields: │
│ - actionDescription: String │
│ - success: boolean │
│ - actionConfig: ActionConfig │
│ - outputText: String │
├─────────────────────────────────────────────────────────────────┤
│ Component Delegates: │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ MatchCollection │ │ TimingData │ │
│ │ - matches │ │ - startTime │ │
│ │ - sorting │ │ - endTime │ │
│ │ - filtering │ │ - duration │ │
│ └──────────────────┘ └──────────────────┘ │
│ ┌──── ──────────────┐ ┌──────────────────┐ │
│ │TextExtractionResult│ │ StateTracker │ │
│ │ - accumulated │ │ - activeStates │ │
│ │ - selected │ │ - stateMatches │ │
│ │ - matchText │ │ - activations │ │
│ └──────────────────┘ └──────────────────┘ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ RegionManager │ │ MovementTracker │ │
│ │ - definedRegions │ │ - movements │ │
│ │ - namedRegions │ │ - distances │ │
│ │ - unions │ │ - paths │ │
│ └──────────────────┘ └──────────────────┘ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ ActionAnalysis │ │ExecutionHistory │ │
│ │ - sceneAnalyses │ │ - records │ │
│ │ - masks │ │ - lifecycle │ │
│ │ - customAnalysis │ │ - timeline │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Core Components
1. MatchCollection
Responsibility: Manages all match-related operations
public class MatchCollection {
private List<Match> matches;
private List<Match> initialMatches;
private int maxMatches;
// Key operations
public void add(Match... matches);
public void sort(SortStrategy strategy);
public Optional<Match> getBest();
public MatchCollection filter(Predicate<Match> predicate);
public MatchStatistics getStatistics();
}
Features:
- Multiple sorting strategies (score, size, distance)
- Flexible filtering with predicates
- Statistical analysis delegation
- Set operations (minus, intersection)
- Automatic max match enforcement
2. TimingData
Responsibility: Handles all timing and duration tracking
public class TimingData {
private LocalDateTime startTime;
private LocalDateTime endTime;
private Duration totalDuration;
private List<TimeSegment> segments;
// Key operations
public void start();
public void stop();
public Duration getElapsed();
public void addSegment(String name, Duration duration);
}
Features:
- Automatic timing calculation
- Phase timing support
- Overhead calculation
- Human-readable formatting
3. TextExtractionResult
Responsibility: Manages text extraction and OCR results
public class TextExtractionResult {
private Text accumulatedText;
private String selectedText;
private Map<Match, String> matchTextMap;
// Key operations
public void addText(String text);
public void addMatchText(Match match, String text);
public String getCombinedText();
}
Features:
- Accumulated text tracking
- Match-specific text mapping
- Selected text management
- Multiple text source merging
4. StateTracker
Responsibility: Tracks state information during execution
public class StateTracker {
private Set<String> activeStates;
private Map<String, List<Match>> stateMatches;
private Map<String, Integer> stateActivationCounts;
// Key operations
public void recordActiveState(String stateName);
public void recordStateMatch(String stateName, Match match);
public Optional<String> getMostActiveState();
}
Features:
- Active state detection
- State-match associations
- Activation frequency tracking
- State activity analysis
5. RegionManager
Responsibility: Manages region definitions and operations
public class RegionManager {
private List<Region> definedRegions;
private Map<String, Region> namedRegions;
// Key operations
public void defineRegion(Region region);
public void defineNamedRegion(String name, Region region);
public Optional<Region> getUnion();
public Optional<Region> getIntersection();
}
Features:
- Anonymous and named regions
- Union/intersection calculations
- Area-based sorting
- Primary region access
6. MovementTracker
Responsibility: Tracks drag and movement operations
public class MovementTracker {
private List<Movement> movements;
// Key operations
public void recordMovement(Movement movement);
public double getTotalDistance();
public boolean isClosedPath(double tolerance);
public Optional<Region> getBoundingBox();
}
Features:
- Movement sequence tracking
- Distance calculations
- Path analysis (closed/open)
- Bounding box computation
7. ActionAnalysis
Responsibility: Manages analysis data and results
public class ActionAnalysis {
private SceneAnalyses sceneAnalyses;
private Mat mask;
private Map<String, Object> customAnalysis;
// Key operations
public void addSceneAnalysis(SceneAnalysis analysis);
public void addCustomAnalysis(String key, Object data);
public <T> Optional<T> getCustomAnalysis(String key, Class<T> type);
}
Features:
- Scene analysis aggregation
- Binary mask management
- Custom analysis storage
- Type-safe retrieval
8. ExecutionHistory
Responsibility: Tracks action execution history
public class ExecutionHistory {
private List<ActionRecord> records;
private ActionLifecycle lifecycle;
// Key operations
public void recordStep(ActionRecord record);
public List<ActionRecord> getSuccessfulSteps();
public double getSuccessRate();
public String formatTimeline();
}
Features:
- Step-by-step recording
- Success/failure analysis
- Timeline visualization
- Duration tracking
Support Components
MatchStatistics
Provides statistical analysis of match collections:
- Median region/location calculation
- Score distribution analysis
- Confidence level determination
- Density calculations
- Bounding box computation
MatchFilter
Static utility for filtering operations:
- Filter by state object/owner
- Score-based filtering
- Area-based filtering
- Distance-based filtering
- Duplicate removal
ActionResultBuilder
Fluent builder for constructing ActionResult instances:
ActionResult result = new ActionResultBuilder()
.withSuccess(true)
.withDescription("Found button")
.withMatches(matchList)
.withTiming(startTime, endTime)
.withActiveState("MainMenu")
.build();
Design Patterns
1. Facade Pattern
ActionResult acts as a facade, providing a simplified interface to the complex subsystem of components while maintaining backward compatibility.
2. Delegation Pattern
All operations are delegated to specialized components, keeping ActionResult focused on coordination rather than implementation.
3. Builder Pattern
ActionResultBuilder provides flexible construction with optional parameters and method chaining.
4. Strategy Pattern
MatchCollection uses strategy pattern for sorting operations with configurable strategies.
5. Repository Pattern
Components like RegionManager and StateTracker act as repositories for their respective domain objects.
Integration Points
With Action Classes
public class Click {
public ActionResult execute(ObjectCollection objects) {
ActionResult result = new ActionResult(clickOptions);
// Populate result using components
result.add(foundMatch);
result.setSuccess(true);
return result;
}
}
With Logging System
// Components provide formatted output
String matchSummary = result.getMatchCollection().getStatistics().format();
String timingSummary = result.getTimingData().format();
String stateSummary = result.getStateTracker().format();
With Testing Framework
// Fine-grained assertions on components
assertThat(result.getMatchCollection().size()).isEqualTo(3);
assertThat(result.getTimingData().getExecutionTimeMs()).isLessThan(1000);
assertThat(result.getStateTracker().isStateActive("LoginScreen")).isTrue();
Performance Characteristics
Memory Efficiency
- Lazy Initialization: Components created on demand
- Shared References: No unnecessary copying
- Bounded Collections: MaxMatches enforcement
Computational Efficiency
- O(1) Access: Direct component access
- O(n log n) Sorting: Efficient match sorting
- O(n) Filtering: Linear filtering operations
- Cached Statistics: Statistics calculated once
Garbage Collection
- Reduced Object Graph: Smaller, focused objects
- Clear Ownership: Components own their data
- Explicit Cleanup: Clear() methods on components
Extension Points
Custom Analysis Types
// Add custom analysis data
result.getActionAnalysis().addCustomAnalysis("colorProfile", colorData);
// Retrieve with type safety
Optional<ColorProfile> profile = result.getActionAnalysis()
.getCustomAnalysis("colorProfile", ColorProfile.class);
Custom Sorting Strategies
// Extend MatchCollection.SortStrategy
matchCollection.sort(SortStrategy.CUSTOM_RELEVANCE);
Custom Statistics
// Extend MatchStatistics for domain-specific metrics
public class GameMatchStatistics extends MatchStatistics {
public double getAverageHealthBarFullness() { ... }
}
Migration Guide
For API Consumers
No changes required! The facade maintains complete backward compatibility:
// Old code continues to work
ActionResult result = action.execute();
result.add(match);
result.sortMatchObjects();
List<Match> matches = result.getMatchList();
For Action Implementers
Use the builder for cleaner construction:
// Old way
ActionResult result = new ActionResult();
result.setSuccess(true);
result.add(matches);
result.setDuration(duration);
// New way (optional)
ActionResult result = ActionResultBuilder.successWith(matches)
.withTiming(startTime, endTime)
.build();
For Framework Extenders
Access components directly for advanced operations:
// Direct component access
MatchCollection matches = result.getMatchCollection();
MatchStatistics stats = matches.getStatistics();
double confidence = stats.getConfidence();
Testing Strategy
Component Unit Testing
Each component has focused unit tests:
@Test
void testMatchCollectionSorting() {
MatchCollection collection = new MatchCollection();
collection.add(match1, match2, match3);
collection.sortByScoreDescending();
assertThat(collection.getBest()).contains(match3);
}
Integration Testing
Test component interactions:
@Test
void testActionResultDelegation() {
ActionResult result = new ActionResult();
result.add(match);
// Verify delegation
assertThat(result.getMatchCollection().contains(match)).isTrue();
assertThat(result.getStateTracker().getActiveStates()).contains(stateName);
}
Performance Testing
Verify performance characteristics:
@Benchmark
public void benchmarkMatchSorting() {
result.sortMatchObjectsDescending();
}
Security Considerations
Input Validation
- Components validate input parameters
- Null-safe operations throughout
- Bounded collection sizes
Data Isolation
- Components encapsulate their data
- No shared mutable state
- Defensive copying where needed
Serialization Safety
- @JsonIgnore on non-serializable fields
- Clean separation of transient data
- Controlled exposure of internal state
Future Enhancements
Planned Improvements
- Async Support: Concurrent component operations
- Streaming API: Process large result sets efficiently
- Event System: Observable result changes
- Metrics Dashboard: Real-time performance visualization
- Plugin Architecture: Dynamic component registration
Potential Optimizations
- Object Pooling: Reuse component instances
- Lazy Loading: Defer expensive calculations
- Caching Layer: Cache computed statistics
- Compression: Compress large match collections
Best Practices
Do's
- ✅ Use components for focused operations
- ✅ Leverage builder for complex construction
- ✅ Access statistics for analysis
- ✅ Clear components when done
- ✅ Use appropriate sorting strategies
Don'ts
- ❌ Don't access component internals directly
- ❌ Don't modify collections while iterating
- ❌ Don't ignore null checks
- ❌ Don't bypass the facade for basic operations
- ❌ Don't store references to internal collections
Component Reference
Component | Lines | Responsibility | Key Methods |
---|---|---|---|
MatchCollection | 280 | Match management | add(), sort(), filter(), getBest() |
TimingData | 150 | Timing tracking | start(), stop(), getElapsed() |
MatchStatistics | 200 | Statistical analysis | getMedian(), getConfidence() |
TextExtractionResult | 120 | Text management | addText(), getCombinedText() |
StateTracker | 160 | State tracking | recordActiveState(), getMostActiveState() |
RegionManager | 180 | Region management | defineRegion(), getUnion() |
MovementTracker | 140 | Movement tracking | recordMovement(), getTotalDistance() |
ActionAnalysis | 140 | Analysis data | addSceneAnalysis(), addCustomAnalysis() |
ActionMetrics | 200 | Performance metrics | recordExecutionTime(), getEfficiencyScore() |
ExecutionHistory | 180 | Execution tracking | recordStep(), getSuccessRate() |
MatchFilter | 260 | Filtering utilities | byMinScore(), nearLocation() |
ActionResultBuilder | 400 | Result construction | withMatches(), build() |
Conclusion
The refactored ActionResult architecture transforms a monolithic class into a well-structured system of focused components. This design provides:
- Better Maintainability: Each component has a single, clear responsibility
- Enhanced Testability: Smaller units are easier to test in isolation
- Improved Extensibility: New features can be added to specific components
- Backward Compatibility: Existing code continues to work unchanged
- Performance Benefits: Lazy initialization and focused operations
The component-based architecture positions ActionResult for future growth while maintaining the stability and reliability expected by existing consumers.