I’m working on a personal project that has a fairly complex UI. As much as possible I would like to remove mouse interaction. To help with that I am working on a kind of in app command line, where you can find all the actions by opening a popup and type in the names to run them. (think intellij ctrl-shift-a, or eclipse ctrl-f12). I would also like a way to add a way to pass an arg to the command (e.g.
I am having a hard time figuring out how to design this. Currently my idea is to have a common
Commands object that contains all the commands, and a
Command interface that contains logic, a user friendly name, and an arg description (if any). Part of the problem is that some actions can only be performed in certain contexts, and the other problem is where to put the individual
Commands (this is an issue because scala objects are lazy, and they won’t be added to the
Commands object until after being referenced). I’m also unsure how to keep type safety, since thees
Commands will also be linked to from UI hooks inside the app.
Any ideas on how to fix this and/or improve the current model?
One way to accomplish what you’re looking for is to implement the Observer pattern.
For example, instead of having a single, centralized “commands” object that defines all possible commands, you’d have a centralized event dispatcher. Each “command”, when instantiated, would register itself with the dispatcher.
When the command palette is called, an “event” is emitted containing the current context data. The dispatcher will send that event to each command registered. Using the context data sent with the event, the command can then decide what to do: include itself in the list, ignore the event, modify itself based on the context data provided, etc.
A more sophisticated model would be to emit different types of events for each context. Commands could then, when registering with the dispatcher, state explicitly which types of events they care about. The dispatcher would then decide which commands should receive the event in the first place.
One possible solution is below.
These take in a data structure and return another data structure. These delegate and have no ifs or loops. This should have idempotent functions only so it is unit testable.
For things depending on the environment, make a interface for a pitiful are role. Then make roles implement the interface with dependency injection (integration tests, one thing at a time with mocks for what’s not under test). Then make a class return one of the choices.
Your UI is now variable, separate from business rules.
As for context, your packages or namespaces should scream it. Make names after job titles. Those are user roles. Locations in the company building have a few work flows. Each workflow can be a interface and a situation can be one implementation. Make sure the interface allows failure and partial completion. A graceful failure class for each could redirect what people do to get around the problem.