Skip to content

Latest commit

 

History

History
282 lines (215 loc) · 10 KB

File metadata and controls

282 lines (215 loc) · 10 KB

Universe Explorer - Refactored Architecture

Overview

This document describes the refactored architecture of the Universe Explorer application, which has been redesigned following SOLID principles to improve maintainability, extensibility, and code organization.

Problems with the Original Code

The original app.js file was a monolithic class of 2,907 lines that violated multiple SOLID principles:

  • Single Responsibility Principle (SRP): The UniverseExplorer class handled rendering, UI, physics, textures, animations, and model creation
  • Open/Closed Principle (OCP): Adding new models required modifying the main class
  • Interface Segregation Principle (ISP): Large interfaces with methods not relevant to all consumers
  • Dependency Inversion Principle (DIP): Tight coupling to specific implementations

New Architecture

The refactored architecture follows SOLID principles and separates concerns into focused, maintainable components:

js/
├── core/                           # Core application components
│   ├── UniverseExplorer.js        # Main orchestrator (follows SRP)
│   ├── SceneRenderer.js           # 3D rendering and lighting (SRP)
│   ├── TextureManager.js          # Texture loading and generation (SRP)
│   ├── UIController.js            # User interface management (SRP)
│   ├── AnimationSystem.js         # Animation calculations (SRP)
│   └── PhysicsEngine.js           # Physics simulation (SRP)
├── models/                         # Universe model implementations
│   ├── BaseModel.js               # Abstract base class (Template Method)
│   ├── ModelFactory.js           # Factory pattern (OCP)
│   ├── AristotleModel.js         # Geocentric model
│   ├── PtolemaicModel.js         # Epicycle model
│   ├── CopernicanModel.js        # Heliocentric model
│   ├── GalileanModel.js          # With Jupiter's moons
│   ├── KeplerModel.js            # Elliptical orbits
│   └── NewtonianModel.js         # Physics simulation
└── app-refactored.js              # New entry point

SOLID Principles Applied

1. Single Responsibility Principle (SRP)

Each class now has a single, well-defined responsibility:

  • SceneRenderer: Only handles 3D scene setup, lighting, and rendering
  • TextureManager: Only manages texture loading and procedural generation
  • UIController: Only handles user interface interactions and state
  • AnimationSystem: Only calculates celestial body animations
  • PhysicsEngine: Only handles physics simulation and collision detection

2. Open/Closed Principle (OCP)

The system is now open for extension but closed for modification:

// Adding a new model doesn't require changing existing code
export class CustomModel extends BaseModel {
    async createCelestialBodies() {
        // Implement your model here
    }
}

// Register the new model
modelFactory.registerModel('custom', CustomModel);

3. Liskov Substitution Principle (LSP)

All model classes properly extend BaseModel and can be used interchangeably:

// All models follow the same interface
const model = await modelFactory.createModel(modelName, scene, textureManager);
model.updateAnimations(time, speed);

4. Interface Segregation Principle (ISP)

Components only depend on the interfaces they actually use:

// TextureManager only provides texture-related methods
textureManager.getTexture(name);
textureManager.hasTexture(name);

// SceneRenderer only provides rendering methods
renderer.updateLighting(settings);
renderer.setVisualElementVisible(type, visible);

5. Dependency Inversion Principle (DIP)

High-level modules depend on abstractions, not concrete implementations:

// UniverseExplorer depends on abstractions
this.renderer = new SceneRenderer();        // Abstraction
this.modelFactory = new ModelFactory();     // Abstraction

// Models depend on the abstract BaseModel
export class NewModel extends BaseModel {    // Depends on abstraction
    // Implementation details...
}

Design Patterns Used

1. Template Method Pattern

BaseModel defines the algorithm for model creation:

// Template method defines the steps
async create() {
    await this.createCelestialBodies();    // Abstract - must implement
    this.setupOrbits();                    // Optional override
    this.setupAdditionalElements();        // Optional override
}

2. Factory Pattern

ModelFactory creates models without exposing instantiation logic:

// Factory encapsulates object creation
async createModel(modelName, scene, textureManager) {
    const ModelClass = this.modelRegistry[modelName];
    const model = new ModelClass(modelName, scene, textureManager);
    await model.create();
    return model;
}

3. Observer Pattern

UI events are handled through callbacks:

// UI notifies other components of changes
this.uiController.onModelChange = (modelName) => {
    this.switchModel(modelName);
};

Adding New Models

To add a new universe model, follow these steps:

Step 1: Create the Model Class

// js/models/MyCustomModel.js
import { BaseModel } from './BaseModel.js';

export class MyCustomModel extends BaseModel {
    async createCelestialBodies() {
        // Create your celestial bodies
        const centralBody = this.createCelestialBody('Center', 2, 0xff0000, 0, 0, 0);
        this.celestialBodies.center = { object: centralBody };
        
        // Add planets, moons, etc.
        const planet = this.createCelestialBody('Planet', 1, 0x0000ff, 10, 0, 0);
        this.celestialBodies.planet = {
            object: planet,
            distance: 10,
            speed: 1.0
        };
    }
    
    updateAnimations(time, speed) {
        // Override default animation behavior if needed
        super.updateDefaultAnimations(time, speed);
        
        // Add custom animation logic
    }
}

Step 2: Register the Model

// js/models/ModelFactory.js
import { MyCustomModel } from './MyCustomModel.js';

export class ModelFactory {
    constructor() {
        this.modelRegistry = {
            // ... existing models
            custom: MyCustomModel,  // Add your model here
        };
    }
}

Step 3: Add UI Elements (Optional)

Update the HTML to include your new model in the UI:

<button class="model-btn" data-model="custom">My Custom Model</button>

Benefits of the Refactored Architecture

1. Maintainability

  • Focused classes: Each class has a single responsibility
  • Clear separation: Easy to locate and modify specific functionality
  • Reduced complexity: Smaller, more manageable code files

2. Extensibility

  • Easy model addition: New models require minimal changes to existing code
  • Plugin architecture: Components can be easily replaced or extended
  • Future-proof: Architecture supports adding new features without breaking existing code

3. Testability

  • Isolated components: Each component can be tested independently
  • Mockable dependencies: Easy to create mocks for unit testing
  • Clear interfaces: Well-defined inputs and outputs for testing

4. Reusability

  • Modular components: Components can be reused in other projects
  • Abstract base classes: Common functionality is shared through inheritance
  • Utility methods: Shared utilities are centralized and reusable

Performance Considerations

The refactored architecture maintains the same performance characteristics as the original while providing better organization:

  • Lazy loading: Components are only initialized when needed
  • Efficient memory management: Proper cleanup and disposal methods
  • Optimized rendering: Scene management is centralized in SceneRenderer
  • Texture caching: TextureManager handles efficient texture reuse

Migration Path

To migrate from the original to the refactored architecture:

1. Update HTML

Change the script reference in your HTML:

<!-- Old -->
<script type="module" src="js/app.js"></script>

<!-- New -->
<script type="module" src="js/app-refactored.js"></script>

2. No Other Changes Required

The refactored version maintains the same external interface and UI, so no other changes are needed.

Future Enhancements

The new architecture makes several future enhancements easier to implement:

1. Additional Models

  • Tycho Brahe's Model: Hybrid geocentric-heliocentric system
  • Modern Cosmological Models: Dark matter, dark energy visualization
  • Exoplanet Systems: Multiple star systems with discovered exoplanets

2. Enhanced Physics

  • Gravitational Waves: Visualization of spacetime distortions
  • Relativistic Effects: Time dilation and length contraction
  • N-body Simulation: More complex gravitational interactions

3. Educational Features

  • Guided Tours: Step-by-step explanations of each model
  • Interactive Experiments: What-if scenarios and parameter modification
  • Assessment Tools: Quizzes and knowledge checks

4. Technical Improvements

  • WebGL 2.0: Enhanced graphics capabilities
  • Web Workers: Background physics calculations
  • Progressive Loading: Improved startup performance

Conclusion

The refactored architecture transforms a monolithic 2,907-line class into a well-organized, maintainable system that follows SOLID principles. This improves code quality, makes the system easier to understand and modify, and provides a solid foundation for future enhancements.

Key improvements:

  • Single Responsibility: Each class has one clear purpose
  • Open/Closed: Easy to add new models without modifying existing code
  • Better Organization: Logical file structure and separation of concerns
  • Enhanced Maintainability: Smaller, focused classes are easier to maintain
  • Future-Proof: Architecture supports easy extension and modification

The new architecture maintains all existing functionality while providing a much better foundation for future development and maintenance.