-
Notifications
You must be signed in to change notification settings - Fork 116
Refactoring: Split BasicBody.ts into focused modules #10
Open
Description
BasicBody.ts at 1,774 lines is the second largest file in the codebase. It handles multiple responsibilities that should be separated:
- Tree node structure and traversal
- Value computation and caching
- Formula text generation
- Serialization/deserialization
- Event handling for changes
This violates the Single Responsibility Principle and makes the code difficult to maintain.
Current Class Structure
// BasicBody.ts - 1,774 lines class BasicBody { // Tree structure (~400 lines) parent: BasicBody | null children: BasicBody[] addChild(), removeChild(), getRoot()... // Value computation (~500 lines) getValue(), computeValue(), evaluateExpression()... // Text generation (~300 lines) getTextValue(), getCharacterValue(), formatFormula()... // Serialization (~250 lines) toJSON(), fromJSON(), clone()... // Event handling (~150 lines) onChange(), notifyParent()... // Utility methods (~174 lines) // ... various helpers }
Proposed Architecture
Option A: Composition Pattern (Recommended)
src/data/models/
├── basic-body/
│ ├── index.ts - Re-exports for backward compatibility
│ ├── BasicBody.ts (~300 lines) - Core class with composition
│ ├── TreeOperations.ts (~200 lines) - Tree traversal mixin
│ ├── ValueComputation.ts (~250 lines) - Calculation logic
│ ├── TextGenerator.ts (~200 lines) - Formula text formatting
│ ├── Serializer.ts (~150 lines) - JSON serialization
│ └── types.ts - Shared interfaces
Option B: Inheritance Hierarchy
src/data/models/
├── AbstractNode.ts - Base tree node
├── ComputedNode.ts - Adds computation (extends AbstractNode)
├── BasicBody.ts - Full implementation (extends ComputedNode)
└── types.ts
Implementation: Option A Details
TreeOperations.ts
export class TreeOperations<T extends TreeNode> { getRoot(node: T): T getDepth(node: T): number traverse(node: T, callback: (n: T) => void): void findByPredicate(node: T, predicate: (n: T) => boolean): T | null getPath(node: T): T[] isAncestorOf(node: T, potential: T): boolean }
ValueComputation.ts
export class ValueComputation { private cache: Map<string, CachedValue> compute(node: BasicBody, context: EvaluationContext): number invalidateCache(nodeId: string): void getCacheStats(): CacheStatistics }
TextGenerator.ts
export class TextGenerator { generateText(node: BasicBody, format: TextFormat): string generateCharacterRepresentation(node: BasicBody): string formatWithPrecedence(node: BasicBody): string } export enum TextFormat { PLAIN = 'plain', LATEX = 'latex', ASCII = 'ascii' }
Serializer.ts
export class BasicBodySerializer { serialize(node: BasicBody): SerializedBasicBody deserialize(data: SerializedBasicBody): BasicBody clone(node: BasicBody, deep?: boolean): BasicBody }
Updated BasicBody.ts
import { TreeOperations } from './TreeOperations' import { ValueComputation } from './ValueComputation' import { TextGenerator } from './TextGenerator' import { BasicBodySerializer } from './Serializer' export class BasicBody { private static treeOps = new TreeOperations() private static computation = new ValueComputation() private static textGen = new TextGenerator() private static serializer = new BasicBodySerializer() // Delegate to specialized classes getRoot() { return BasicBody.treeOps.getRoot(this) } getValue() { return BasicBody.computation.compute(this, this.context) } getTextValue() { return BasicBody.textGen.generateText(this, TextFormat.PLAIN) } toJSON() { return BasicBody.serializer.serialize(this) } }
Migration Strategy
- Create new module files without changing BasicBody
- Extract logic method by method with tests
- Update BasicBody to delegate to new classes
- Ensure 100% backward compatibility
- Deprecate direct method usage (optional)
- Update documentation
Backward Compatibility
The public API of BasicBody remains unchanged:
const node = new BasicBody() node.addChild(childNode) // Still works node.getValue() // Still works node.getTextValue() // Still works
Metadata
Metadata
Assignees
Type
Fields
Give feedbackNo fields configured for issues without a type.