Build a reusable CLI command framework using abstract classes, inheritance, and polymorphism. This is the foundation for professional command-line applications.
Requirements
Create an abstract Command class with name, description, and abstract execute method
Create a CommandRunner class that registers and executes commands
Implement HelpCommand that displays available commands
Implement EchoCommand that repeats user input
Implement MathCommand that performs calculations
Support command-line argument parsing
Handle unknown commands gracefully
Expected Output
Terminal
$ dart run cli.dart help
Available commands:
help - Show help information
echo - Repeat the input text
math - Perform mathematical calculations
greet - Display a greeting
$ dart run cli.dart echo Hello World
Echo: Hello World
$ dart run cli.dart math add 5 10
5 + 10 = 15
$ dart run cli.dart math multiply 6 7
6 ร 7 = 42
// Step 1: Abstract Command class
abstract class Command {
final String name;
final String description;
Command(this.name, this.description);
// Abstract method - subclasses must implement
String execute(List<String> arguments);
String get usage => '$name - $description';
}
// Step 2: CommandRunner class
class CommandRunner {
final Map<String, Command> _commands = {};
void register(Command command) {
_commands[command.name] = command;
}
void run(List<String> arguments) {
if (arguments.isEmpty) {
print('No command provided. Type "help" for available commands.');
return;
}
String commandName = arguments.first;
List<String> commandArgs = arguments.length > 1
? arguments.sublist(1)
: [];
Command? command = _commands[commandName];
if (command == null) {
print('Unknown command: $commandName');
print('Type "help" for available commands.');
return;
}
try {
String result = command.execute(commandArgs);
print(result);
} catch (e) {
print('Error executing $commandName: $e');
}
}
List<Command> get commands => _commands.values.toList();
}
// Step 3: HelpCommand
class HelpCommand extends Command {
final CommandRunner runner;
HelpCommand(this.runner) : super('help', 'Show help information');
@override
String execute(List<String> arguments) {
final buffer = StringBuffer();
buffer.writeln('Available commands:');
for (var command in runner.commands) {
buffer.writeln(' ${command.name.padRight(10)} - ${command.description}');
}
return buffer.toString();
}
}
// Step 4: EchoCommand
class EchoCommand extends Command {
EchoCommand() : super('echo', 'Repeat the input text');
@override
String execute(List<String> arguments) {
if (arguments.isEmpty) {
return 'Usage: echo <text>';
}
return 'Echo: ${arguments.join(" ")}';
}
}
// Step 5: GreetCommand
class GreetCommand extends Command {
GreetCommand() : super('greet', 'Display a greeting');
@override
String execute(List<String> arguments) {
String name = arguments.isNotEmpty ? arguments[0] : 'stranger';
String timeOfDay = _getTimeOfDay();
return 'Good $timeOfDay, $name! ๐';
}
String _getTimeOfDay() {
int hour = DateTime.now().hour;
if (hour < 12) return 'morning';
if (hour < 17) return 'afternoon';
return 'evening';
}
}
// Step 6: MathCommand with subcommands
class MathCommand extends Command {
MathCommand() : super('math', 'Perform mathematical calculations');
@override
String execute(List<String> arguments) {
if (arguments.length < 3) {
return 'Usage: math <operation> <num1> <num2>\n'
'Operations: add, subtract, multiply, divide';
}
String operation = arguments[0].toLowerCase();
double? num1 = double.tryParse(arguments[1]);
double? num2 = double.tryParse(arguments[2]);
if (num1 == null || num2 == null) {
return 'Error: Please provide valid numbers.';
}
return switch (operation) {
'add' => '$num1 + $num2 = ${num1 + num2}',
'subtract' => '$num1 - $num2 = ${num1 - num2}',
'multiply' => '$num1 ร $num2 = ${num1 * num2}',
'divide' => num2 != 0
? '$num1 รท $num2 = ${(num1 / num2).toStringAsFixed(2)}'
: 'Error: Division by zero',
_ => 'Unknown operation: $operation. Use add, subtract, multiply, or divide.',
};
}
}
// Step 7: Main program
void main(List<String> arguments) {
final runner = CommandRunner();
// Register all commands
runner.register(HelpCommand(runner));
runner.register(EchoCommand());
runner.register(GreetCommand());
runner.register(MathCommand());
// Run the CLI
runner.run(arguments);
}