Challenge: Solution Review
This lesson will explain the solution to the problem from the last coding challenge.
We'll cover the following
Solution #
class Command {execute(args) {};}//Withdraw commandclass WithDrawAmount extends Command {constructor(bankaccount) {super();this.bankaccount = bankaccount;}execute(args) {this.bankaccount.withdrawMoney(args);}}//CheckAmount commandclass CheckAmount extends Command {constructor(bankaccount) {super();this.bankaccount = bankaccount}execute() {this.bankaccount.checkAmount()}}//DepositAmount commandclass DepositAmount extends Command {constructor(bankaccount) {super();this.bankaccount = bankaccount}execute(args) {this.bankaccount.depositAmount(args)}}//Invokerclass AccountManager {request(command,args) {command.execute(args);}}//Reciever:class BankAccount {constructor(amount){this.amount = amount}checkAmount() {console.log(this.amount)}withdrawMoney(withdrawamount) {if(withdrawamount > this.amount){console.log("Not enough money")}else{this.amount -= withdrawamount}}depositAmount(money){this.amount += money}}const manager = new AccountManager();const account = new BankAccount(100)const check = new CheckAmount(account);manager.request(check)const withdraw = new WithDrawAmount(account);const deposit = new DepositAmount(account);manager.request(withdraw,10)manager.request(check)manager.request(deposit,50)manager.request(check)
Explanation #
Let’s start by looking at the original code first:
class BankAccount {constructor(amount){this.amount = amount}checkAmount() {console.log(this.amount)}withdrawMoney(withdrawamount) {if(withdrawamount > this.amount){console.log("Not enough money")}else{this.amount -= withdrawamount}}depositAmount(money){this.amount += money}}var account = new BankAccount(100)account.checkAmount()account.withdrawMoney(10)account.checkAmount()account.depositAmount(50)account.checkAmount()
In the example, there is a BankAccount
class which contains the following functions:
-
checkAmount
: return theamount
in the account. -
withdrawMoney
: withdraws an amount. -
depositAmount
: deposits an amount.
An account object will directly be able to call on these functions. With the command pattern, we will change that; meaning, the object executing the function will be separated from the one requesting. As mentioned in the question, the command pattern will consist of the following:
-
commands:
WithDraw
,DepositAmount
, andCheckAmount
. -
receiver:
BankAccount
. -
invoker: an
AccountManager
carrying out the operations requested using arequest
function.
Let’s start by looking at the commands that request some operation on the account.
//Withdraw command
class WithDrawAmount extends Command {/*code*/}
//CheckAmount command
class CheckAmount extends Command {/*code*/}
//DepositAmount command
class DepositAmount extends Command {/*code*/}
All three commands: WithDrawAmount
, CheckAmount
, and DepositAmount
inherit from the abstract class Command
.
class Command {
execute(args) {};
}
It contains the abstract function execute(args)
and each command defines it accordingly.
class WithDrawAmount extends Command {
constructor(bankaccount) {
super();
this.bankaccount = bankaccount;
}
execute(args) {
this.bankaccount.withdrawMoney(args);
}
}
The constructor
of the WithDrawAmount
class takes the bankaccount
as an argument, using it to initialize the bankaccount
property on which it will perform the operation of withdrawing the money. The execute
function carries out the operation by calling the withdrawMoney
function on the bankaccount
.
The constructor
for both CheckAmount
and DepositAmount
are the same as WithDrawAmount
. The difference is in the definition of their execute
functions.
class CheckAmount extends Command {
//code...
execute() {
this.bankaccount.checkAmount()
}
}
//DepositAmount command
class DepositAmount extends Command {
//code...
execute(args) {
this.bankaccount.depositAmount(args)
}
}
CheckAmount
calls the checkAmount
function in the definition of execute
and DepositAmount
calls the depositAmount
function in execute
.
We have a BankAccount
and we want to send commands to carry out certain operations such as withdrawing, depositing, and checking money. So, how are these commands executed? The AccountManager
will be responsible for invoking the operations requested for by the commands.
class AccountManager {
request(command,args) {
command.execute(args);
}
}
When the client sends a command, the AcccountManager
processes the request by executing the command
and carrying out the required operation. Let’s look at an example:
const account = new BankAccount(100)
const manager = new AccountManager()
const withdraw = new WithDrawAmount(account);
manager.request(withdraw,10)
We have an account
and a manager
to handle the operations performed on the account
. A command to withdraw
money is created, and the request
to withdraw money is sent to the manager
. The manager
will carry out the request and the operation will be performed.
Let’s discuss the iterator pattern in the next lesson.