Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#10 Only allow depositing after logging in
Refactoring: Introducing a CommandProcessor
- Loading branch information
1 parent
951d66a
commit 4c250a3
Showing
12 changed files
with
164 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package id.mustofa.atm.model | ||
|
||
import id.mustofa.atm.model.base.Command | ||
import id.mustofa.atm.model.base.Outputter | ||
import id.mustofa.atm.model.base.SingleArgCommand | ||
import id.mustofa.atm.util.handled | ||
import java.math.BigDecimal | ||
|
||
abstract class BigDecimalCommand constructor( | ||
private val outputter: Outputter | ||
) : SingleArgCommand() { | ||
|
||
override fun handleArg(arg: String): Command.Result { | ||
val amount = tryParse(arg) | ||
when { | ||
amount == null -> outputter.output("$arg is not a valid number") | ||
amount.signum() <= 0 -> outputter.output("amount must be positive") | ||
else -> handleAmount(amount) | ||
} | ||
return Command.Result.handled | ||
} | ||
|
||
private fun tryParse(arg: String): BigDecimal? { | ||
return try { | ||
BigDecimal(arg) | ||
} catch (e: NumberFormatException) { | ||
null | ||
} | ||
} | ||
|
||
protected abstract fun handleAmount(amount: BigDecimal) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,19 @@ | ||
package id.mustofa.atm.model | ||
|
||
import id.mustofa.atm.model.base.Command | ||
import id.mustofa.atm.model.base.Command.Status | ||
import id.mustofa.atm.model.base.Outputter | ||
import id.mustofa.atm.router.Database | ||
import java.math.BigDecimal | ||
import javax.inject.Inject | ||
|
||
class DepositCommand @Inject constructor( | ||
private val database: Database, | ||
private val account: Database.Account, | ||
private val outputter: Outputter | ||
) : Command { | ||
) : BigDecimalCommand(outputter) { | ||
|
||
override fun key() = "deposit" | ||
|
||
override fun handleInput(input: List<String>): Status { | ||
if (input.size != 2) { | ||
return Status.INVALID | ||
} | ||
|
||
val account = database.getAccount(input[0]) | ||
account.deposit(BigDecimal(input[1])) | ||
override fun handleAmount(amount: BigDecimal) { | ||
account.deposit(amount) | ||
outputter.output("${account.username} now has: ${account.balance}") | ||
return Status.HANDLED | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,24 @@ | ||
package id.mustofa.atm.model | ||
|
||
import id.mustofa.atm.model.base.Command.Status | ||
import id.mustofa.atm.model.base.Command.Result | ||
import id.mustofa.atm.model.base.Outputter | ||
import id.mustofa.atm.model.base.SingleArgCommand | ||
import id.mustofa.atm.router.Database | ||
import id.mustofa.atm.router.UserCommandsRouter | ||
import id.mustofa.atm.util.enterNestedCommandSet | ||
import javax.inject.Inject | ||
|
||
class LoginCommand @Inject constructor( | ||
private val database: Database, | ||
private val outputter: Outputter | ||
private val outputter: Outputter, | ||
private val userCommandsRouterFactory: UserCommandsRouter.Factory | ||
) : SingleArgCommand() { | ||
|
||
override fun key() = "login" | ||
|
||
override fun handleArg(arg: String): Status { | ||
override fun handleArg(arg: String): Result { | ||
val account = database.getAccount(arg) | ||
outputter.output("${account.username} is logged in with balance: ${account.balance}.") | ||
return Status.HANDLED | ||
return Result.enterNestedCommandSet(userCommandsRouterFactory.create(account).router()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,24 @@ | ||
package id.mustofa.atm.model.base | ||
|
||
import id.mustofa.atm.router.CommandRouter | ||
import java.util.* | ||
|
||
interface Command { | ||
|
||
fun key(): String | ||
|
||
fun handleInput(input: List<String>): Status | ||
fun handleInput(input: List<String>): Result | ||
|
||
class Result( | ||
val status: Status, | ||
val nestedCommandRouter: Optional<CommandRouter> | ||
) { | ||
companion object | ||
} | ||
|
||
enum class Status { | ||
INVALID, | ||
HANDLED | ||
HANDLED, | ||
INPUT_COMPLETED | ||
} | ||
} |
9 changes: 5 additions & 4 deletions
9
src/main/kotlin/id/mustofa/atm/model/base/SingleArgCommand.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,13 @@ | ||
package id.mustofa.atm.model.base | ||
|
||
import id.mustofa.atm.model.base.Command.Status | ||
import id.mustofa.atm.model.base.Command.Result | ||
import id.mustofa.atm.util.invalid | ||
|
||
abstract class SingleArgCommand : Command { | ||
|
||
final override fun handleInput(input: List<String>): Status { | ||
return if (input.size == 1) handleArg(input[0]) else Status.INVALID | ||
final override fun handleInput(input: List<String>): Result { | ||
return if (input.size == 1) handleArg(input[0]) else Result.invalid | ||
} | ||
|
||
abstract fun handleArg(arg: String): Status | ||
abstract fun handleArg(arg: String): Result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package id.mustofa.atm.router | ||
|
||
import id.mustofa.atm.model.base.Command.Status | ||
import java.util.* | ||
import javax.inject.Inject | ||
import javax.inject.Singleton | ||
|
||
@Singleton | ||
class CommandProcessor @Inject constructor( | ||
firstCommandRouter: CommandRouter | ||
) { | ||
|
||
private val commandRouterStack: Deque<CommandRouter> = ArrayDeque() | ||
|
||
init { | ||
commandRouterStack.push(firstCommandRouter) | ||
} | ||
|
||
fun process(input: String): Status { | ||
val result = commandRouterStack.peek().route(input) | ||
if (result.status == Status.INPUT_COMPLETED) { | ||
commandRouterStack.pop() | ||
return if (commandRouterStack.isEmpty()) | ||
Status.INPUT_COMPLETED else Status.HANDLED | ||
} | ||
|
||
result.nestedCommandRouter.ifPresent(commandRouterStack::push) | ||
return result.status | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
src/main/kotlin/id/mustofa/atm/router/UserCommandsRouter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package id.mustofa.atm.router | ||
|
||
import dagger.BindsInstance | ||
import dagger.Module | ||
import dagger.Subcomponent | ||
import id.mustofa.atm.module.UserCommandsModule | ||
import id.mustofa.atm.router.Database.Account | ||
|
||
@Subcomponent(modules = [UserCommandsModule::class]) | ||
interface UserCommandsRouter { | ||
|
||
fun router(): CommandRouter | ||
|
||
@Subcomponent.Factory | ||
interface Factory { | ||
|
||
fun create(@BindsInstance account: Account): UserCommandsRouter | ||
} | ||
|
||
@Module(subcomponents = [UserCommandsRouter::class]) | ||
interface InstallationModule | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package id.mustofa.atm.util | ||
|
||
import id.mustofa.atm.model.base.Command | ||
import id.mustofa.atm.model.base.Command.Status | ||
import id.mustofa.atm.router.CommandRouter | ||
import java.util.* | ||
|
||
val Command.Result.Companion.invalid | ||
get() = Command.Result(Status.INVALID, Optional.empty()) | ||
|
||
val Command.Result.Companion.handled | ||
get() = Command.Result(Status.HANDLED, Optional.empty()) | ||
|
||
val Command.Result.Companion.inputCompleted | ||
get() = Command.Result(Status.INPUT_COMPLETED, Optional.empty()) | ||
|
||
fun Command.Result.Companion.enterNestedCommandSet(nestedCommandRouter: CommandRouter): Command.Result { | ||
return Command.Result(Status.HANDLED, Optional.of(nestedCommandRouter)) | ||
} |