Admin CLI Interface
Part of the Object Oriented Programming section of Coddy's GO journey. Lesson 103 of 107.
Challenge
EasyLet's build a command-line interface for our Library Management System using the Command Pattern! You'll create a CLI that processes admin commands like adding books, registering users, and managing borrowing operations: all through a clean, extensible architecture.
You'll organize your code across six files:
book.go: YourBookstruct with JSON tags forID,Title,Author,ISBN, andAvailable. Include theNewBookconstructor.user.go: YourUserstruct with JSON tags forID,Name,Email, andBorrowedBooks. Include theNewUserconstructor.library.go: YourLibrarystruct with maps for books and users, along withAddBook,AddUser,Borrow, andReturnmethods from previous lessons.commands.go: This is where the Command Pattern comes to life! Define aCommandinterface with anExecutemethod that takes a*Libraryand[]stringargs, returning a string result. Then implement these command structs:AddBookCommand: expects 4 args: id, title, author, isbn. Adds the book and returnsBook added: [title]AddUserCommand: expects 3 args: id, name, email. Adds the user and returnsUser added: [name]BorrowCommand: expects 2 args: userID, bookID. ReturnsBorrowed: [bookID]on success, orError: [message]on failureReturnCommand: expects 2 args: userID, bookID. ReturnsReturned: [bookID]on success, orError: [message]on failureListBooksCommand: takes no args. Returns each book's ID and title on separate lines in format[id]: [title], orNo booksif empty (list books sorted by ID)
If a command receives insufficient arguments, return
Error: insufficient argumentscli.go: Create aCLIstruct that holds a pointer to aLibraryand a map of command names toCommandimplementations. Build:NewCLIconstructor that initializes the CLI with a library and registers all commands:addbook,adduser,borrow,return,listbooksRunmethod that takes an input string, parses it usingstrings.Fields, looks up the command, and executes it. ReturnNo command providedfor empty input, orUnknown command: [name]for unrecognized commands
main.go: Create a library and CLI, then process commands from input. Read the number of commands, then read each command string on a separate line. For each command, callRunand print the result.
The following inputs will be provided:
- Number of commands
- Each command as a complete string on its own line
For example, given:
6
addbook B001 Go-Programming Alan-Donovan 978-0134190440
addbook B002 Clean-Code Robert-Martin 978-0132350884
adduser U001 Alice alice@test.com
borrow U001 B001
listbooks
borrow U001 B001Your output should be:
Book added: Go-Programming
Book added: Clean-Code
User added: Alice
Borrowed: B001
B001: Go-Programming
B002: Clean-Code
Error: book not availableAnd given:
4
listbooks
addbook B100 Design-Patterns Gang-of-Four 978-0201633610
listbooks
invalidcmd testYour output should be:
No books
Book added: Design-Patterns
B100: Design-Patterns
Unknown command: invalidcmdAnd given:
5
adduser U001 Bob bob@email.com
borrow U001 B001
addbook B001 Golang-Guide John-Smith 123-456
borrow U001 B001
return U001 B001Your output should be:
User added: Bob
Error: book not found
Book added: Golang-Guide
Borrowed: B001
Returned: B001Notice how the CLI doesn't need to know the details of each operation. It simply looks up the command and delegates execution. This makes adding new commands as simple as creating a new struct that implements the Command interface and registering it in the CLI!
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// Read the number of commands
var n int
fmt.Scanln(&n)
// Create a scanner to read full lines
scanner := bufio.NewScanner(os.Stdin)
// TODO: Create a new Library
// TODO: Create a new CLI with the library
// TODO: Process each command
// For each of the n commands:
// - Read the command line using scanner.Scan() and scanner.Text()
// - Run the command through the CLI
// - Print the result
_ = scanner // Remove this line when you use scanner
}
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool12Advanced OOP Concepts
Functional Options PatternBuilder Pattern in GoMethod ChainingType Aliases vs DefinitionsReflection BasicsCode Generation Overview15Project: Library Management
Project Overview & StructureBook and User StructsPractice on your own: Online Go compiler