0

I am building a CLI tool which will potentially support many commands. Ideally, I want to abstract out each command to implement an interface that demands a "run" method. From there on, it would be a simple regex match that delegates to the appropriate command class.

The issue is, not all of these commands are stateless. One command may modify program state in a way that a future command relies on. For example, a command will build a search index, that a subsequent command uses.

I have a main CLI class handling the regex matching, and independent "Command" classes implementing the RunnableCommand interface.

Is it reasonable to pass the instance of the CLI class to individual "Command" instances in order to mutate state? (whether by getters/setters or by making CLI fields public). In C++ I would use a friend class for the commands, but that is not available here.

1
  • Wouldn't it be simpler in the case you explained that if the index hasn't been built you just say "search index has not been built, use the command foo tu build the index first" ? That's basically how command line stuff works
    – Walfrat
    Commented Feb 1, 2019 at 14:00

2 Answers 2

1

From reading your question,
the CLI object is manipulated and used by the Commands classes (which it itself invokes), it indicates a strong coupling between the Command classes and the CLI object (to the degree where you indicate that you would've used friend if it was cpp).
This should raise a red flag regarding your design decisions of your system.

I would advise you to rethink your system architecture, the CLI class should have a clear role; it shouldn't track dependencies between the commands (which you don't know, since it's using dynamic-dispatch). If you do it, it'll be very hard to maintain in the long-run.

Regarding your scenario mentioned; it sounds like there is a strong dependency between commands that build the search index and commands that manipulate it, in that case it indicates a strong cohesiveness between them and they should share this data member (via being inner-classes of the class that holds this data member for instance).
That way, an invocation of a command that uses the search-index can see that it wasn't build/load/verified, and generate an appropriate message to the user regarding this condition. (now your CLI is stateless, and the dependencies are communicated to the user in a clear way that is maintainable by the class that holds the search-index data member).

2
  • 1
    "an invocation of a command that uses the search-index can see that it wasn't build/load/verified, and generate an appropriate message to the user" - I think it would be more convenient if the command had an option to automatically run the search-index creating command in case the index is missing.
    – Doc Brown
    Commented Feb 2, 2019 at 7:30
  • Yes, and it can be achieved. I wanted to emphasize that the commands that uses the search index have access to the search index (by them being inner classes of it, or other means) and the search index itself is encapsulated and not directly manipulated by the CLI class. Thanks.
    – nadir
    Commented Feb 2, 2019 at 14:01
0

I'd suggest the repository pattern. With most Unix/Linux CLI tools, the repository is usually the current working directory, or a service that the CLI tool connects to that can be configured either from config files in the current directory, passed as arguments, environment variables, or globally through user config files in home directory/system settings.

When the tool creates an index, it should write that index to the current directory/service. Commands that uses the index would look for that index in the current directory/linked service, and commands which performs operations that requires an index update would either update the index or mark the index as stale.

The CLI argument parser itself shouldn't need to know the dependencies between commands, all it should know is how to locate the repository/service.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.