- **Visual Effects**: Upgraded line clear particles to use the game's block texture instead of simple circles, matching the reference web game's aesthetic. - **Particle Physics**: Tuned particle velocity, gravity, and fade rates for a more dynamic explosion effect. - **Rendering Integration**: Updated [main.cpp](cci:7://file:///d:/Sites/Work/tetris/src/main.cpp:0:0-0:0) and `GameRenderer` to pass the block texture to the effect system and correctly trigger animations upon line completion. - **Menu UI**: Fixed [MenuState](cci:1://file:///d:/Sites/Work/tetris/src/states/MenuState.cpp:19:0-19:55) layout calculations to use fixed logical dimensions (1200x1000), ensuring consistent centering and alignment of the logo, buttons, and settings icon across different window sizes. - **Code Cleanup**: Refactored `PlayingState` to delegate effect triggering to the rendering layer where correct screen coordinates are available.
3.6 KiB
3.6 KiB
SOLID Principles Refactoring Plan
Current Architecture Issues
1. Single Responsibility Principle (SRP) Violations
ApplicationManagerhandles initialization, coordination, rendering coordination, and asset managementGameclass mixes game logic with some presentation concerns
2. Open/Closed Principle (OCP) Opportunities
- Hard-coded piece types and behaviors
- Limited extensibility for new game modes or rule variations
3. Dependency Inversion Principle (DIP) Missing
- Concrete dependencies instead of interfaces
- Direct instantiation rather than dependency injection
Proposed Improvements
1. Extract Interfaces
// src/core/interfaces/IRenderer.h
class IRenderer {
public:
virtual ~IRenderer() = default;
virtual void clear(uint8_t r, uint8_t g, uint8_t b, uint8_t a) = 0;
virtual void present() = 0;
virtual SDL_Renderer* getSDLRenderer() = 0;
};
// src/core/interfaces/IAudioSystem.h
class IAudioSystem {
public:
virtual ~IAudioSystem() = default;
virtual void playSound(const std::string& name) = 0;
virtual void playMusic(const std::string& name) = 0;
virtual void setMasterVolume(float volume) = 0;
};
// src/core/interfaces/IAssetLoader.h
class IAssetLoader {
public:
virtual ~IAssetLoader() = default;
virtual SDL_Texture* loadTexture(const std::string& path) = 0;
virtual void loadFont(const std::string& name, const std::string& path, int size) = 0;
};
2. Dependency Injection Container
// src/core/ServiceContainer.h
class ServiceContainer {
private:
std::unordered_map<std::type_index, std::shared_ptr<void>> services;
public:
template<typename T>
void registerService(std::shared_ptr<T> service) {
services[std::type_index(typeid(T))] = service;
}
template<typename T>
std::shared_ptr<T> getService() {
auto it = services.find(std::type_index(typeid(T)));
if (it != services.end()) {
return std::static_pointer_cast<T>(it->second);
}
return nullptr;
}
};
3. Break Down ApplicationManager
// src/core/ApplicationLifecycle.h
class ApplicationLifecycle {
public:
bool initialize(int argc, char* argv[]);
void run();
void shutdown();
};
// src/core/SystemCoordinator.h
class SystemCoordinator {
public:
void initializeSystems(ServiceContainer& container);
void updateSystems(double deltaTime);
void shutdownSystems();
};
4. Strategy Pattern for Game Rules
// src/gameplay/interfaces/IGameRules.h
class IGameRules {
public:
virtual ~IGameRules() = default;
virtual int calculateScore(int linesCleared, int level) = 0;
virtual double getGravitySpeed(int level) = 0;
virtual bool shouldLevelUp(int lines) = 0;
};
// src/gameplay/rules/ClassicTetrisRules.h
class ClassicTetrisRules : public IGameRules {
public:
int calculateScore(int linesCleared, int level) override;
double getGravitySpeed(int level) override;
bool shouldLevelUp(int lines) override;
};
Implementation Priority
- Phase 1: Extract core interfaces (IRenderer, IAudioSystem)
- Phase 2: Implement dependency injection container
- Phase 3: Break down ApplicationManager responsibilities
- Phase 4: Add strategy patterns for game rules
- Phase 5: Improve testability with mock implementations
Benefits
- Testability: Easy to mock dependencies for unit tests
- Extensibility: New features without modifying existing code
- Maintainability: Clear responsibilities and loose coupling
- Flexibility: Easy to swap implementations (e.g., different renderers)