Design Patterns - SOEN390-ConUNav/SOEN390-Mini-Cap-Project GitHub Wiki

Our design patterns

Facade

In our frontend we have a layared architecture aswell, for that reason we have a few classes that only have methods related to the calling our backend endpoints. For each backend service the frontend has a class that serves as a facade for the backend. This allows the frontend to make use of the related backend buisness logic without needing direct acces to the Service object. Instead the facade object only has a set of methods to expose the backend endpoints in the frontend to allow it to reach the backend as needed. This makes it so that the fronend is not tightly coupled to any backend implementation, instead it only depends on the endpoints themselves. This means that if the backend is refactored the frontend would require very little to no changes in order to work with the refacatored backend.

We can also extract duplicated logic from our API classes related to response handling and base URL mangement. This would then be a smaller facade that our individual API classes would make use of.

Template

In our project we have multiple pop ups that share the same UI design with little changes in terms of their content. With the template design pattern we want to have a generic pop up that is then reused by the more specialized pop ups for our building information or calendar event pop ups.

The sequence diagram below shows how the template design pattern work in our application.
This was implemented with PR:https://github.com/SOEN390-ConUNav/SOEN390-Mini-Cap-Project/pull/140

Refer to the table here regarding the template design pattern for more details regarding to this refactoring: 9. Refactoring Report

Singleton

Our implementation centralizes caching through a singleton service in cacheService.ts, where CacheService.getInstance() exposes one shared in-memory Map plus AsyncStorage-backed persistence, and each cached entry is addressed by a deterministic key (${namespace}:${key}) that effectively acts as the cached “name”identifier; this singleton is then used by feature caches like shuttleScheduleCache.ts (persistent TTL cache), searchHistory.ts (persistent raw list), and SearchPanel.tsx (memory cache keyed by place type + rounded coordinates), giving a consistent cache flow of singleton lookup, miss fallback, store/update, and reuse across the app.

This sequence diagram shows how the singleton works on our application.
This was implemented on this Pull Request

For more details on this refactoring, look at the singleton table in : 9. Refactoring Report

Strategy

The backend uses the Strategy pattern to choose the pathfinding behavior at runtime based on user preferences such as avoiding stairs for wheelchair users. The AccessibilityRoutingStrategy interface defines allowsStairs() and preferStairsForConnectors(), implemented by StairsAllowedStrategy and StairsAvoidedStrategy. The factory method AccessibilityRoutingStrategy.fromAvoidStairs(boolean) selects the strategy from the user’s avoidStairs setting. IndoorDirectionService.getIndoorDirections() uses this strategy when calling calculateRoute(), calculateCrossFloorRoute(), and filterPois(), and passes it to PathfindingService.findPathThroughWaypoints(), which selects either the full graph or the graphsNoStairs graph depending on strategy.allowsStairs(). This keeps routing logic separate from accessibility rules and allows new strategies to be added without changing existing code.

Commit: https://github.com/SOEN390-ConUNav/SOEN390-Mini-Cap-Project/pull/169/changes/7a2db5967dc23162350150bb6455ba8332725745

Planned Design Patterns in the future

State

The navigation view displayed on-screen while outside consists of four distinct states. While each state may consist of a variety of user interface components, the background process for each state will vary. Currently, the current state is represented by a string stored in a single variable, and the logic for transitioning between the four states is distributed throughout the component with little structure. By using the State design pattern, we can allow each state to determine their own behavior and specify which legal states they are able to transition to. This will create a cleaner flow of the program, and help us prevent bugs caused by entering an invalid state, or failing to properly clean-up prior to transitioning into another state.

Mediator

On our frontend, we have several components on the same screen that need to interact with each other, such as the search bar, building popup, campus switcher, floor plan viewer, and navigation configuration panel. Instead of letting these components directly reference or control one another, we use a central coordinating layer at the page level that manages all communication between them. Each component only communicates with this central coordinator. When something happens, the coordinator decides how the other components should respond. This keeps the components loosely coupled and avoids tight dependencies between them. Right now, this coordinating logic already exists inside our main page components, but it is mixed in with the rendering logic. Our goal is to clean this up by extracting the coordination logic into dedicated mediator hooks. That way, the page components focus purely on rendering the UI, while the mediator handles the interaction logic. This will make the codebase easier to maintain, reason about, and test independently.