Skip to content

Commit b4c68e6

Browse files
authored
ATM low level design (#7)
* Added meeting scheduler low level design * Added mutex and Getroom * Add feedback and readme for meeting scheduler design * Update IsFree method for room * ATM LLD * added atm LLD feeback for improvement * added enter pin in atm state floe
1 parent 0def644 commit b4c68e6

14 files changed

+514
-0
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ The sixth project in this repository is a Meeting Scheduler, a system that simul
110110
- Room calendar management, tracking when a room is booked or available.
111111
- Concurrency control, preventing scheduling conflicts in real time.
112112

113+
## ATM
114+
115+
The seventh project in this repository is a ATM machine, a system that simulates ATM. It demonstrates the following concepts:
116+
117+
- State design pattern for different state of ATM.
118+
- Chain of responsibility principle for withdrawing cash.
119+
- Template pattern for handling error of each place in one place.
120+
- Concurrency control, preventing atm to conflict.
121+
113122
### Features
114123

115124
- Book and cancel meetings seamlessly.

atm-machine/atm/Account.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package atm
2+
3+
import "errors"
4+
5+
type Account interface {
6+
GetBalance() float64
7+
}
8+
9+
type AccountType string
10+
11+
const (
12+
Saving AccountType = "SAVING"
13+
Current AccountType = "CURRENT"
14+
)
15+
16+
func AccountFactory(accType AccountType) (Account, error) {
17+
switch accType {
18+
case Current:
19+
return &CurrentAccount{}, nil
20+
case Saving:
21+
return &SavingAccount{}, nil
22+
default:
23+
return nil, errors.New("Not a valid account type")
24+
}
25+
}
26+
27+
type BaseAccount struct {
28+
acctNo string
29+
balance float64
30+
}
31+
32+
func (b *BaseAccount) GetBalance() float64 {
33+
return b.balance
34+
}
35+
36+
type SavingAccount struct {
37+
BaseAccount
38+
}
39+
40+
type CurrentAccount struct {
41+
BaseAccount
42+
}

atm-machine/atm/atm.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package atm
2+
3+
import (
4+
"atm-machine/model"
5+
"fmt"
6+
"sync"
7+
)
8+
9+
type WithdrawNote struct {
10+
FiveHundred int
11+
Hundred int
12+
Left int
13+
}
14+
15+
type ATM struct {
16+
countOfNotes map[string]int
17+
card model.Card
18+
account Account
19+
uiOption []string
20+
WithdrawAs *WithdrawNote
21+
insertPin ATMState
22+
insertCard ATMState
23+
readCard ATMState
24+
selectAccount ATMState
25+
dispenserAmount ATMState
26+
currentState ATMState
27+
mu sync.RWMutex
28+
}
29+
30+
func (a *ATM) SetState(s ATMState) {
31+
a.currentState = s
32+
}
33+
34+
func (a *ATM) CleanPreviousAtmTransaction() {
35+
a.mu.Lock()
36+
defer a.mu.Unlock()
37+
a.WithdrawAs = &WithdrawNote{}
38+
a.card = model.Card{}
39+
a.account = nil
40+
}
41+
42+
func (a *ATM) PrintMoney() {
43+
a.mu.Lock()
44+
defer a.mu.Unlock()
45+
fmt.Printf("\n500 note present:%d, 100 not present:%d", a.countOfNotes["500"], a.countOfNotes["100"])
46+
}
47+
48+
func (a *ATM) StateName() string {
49+
a.mu.Lock()
50+
defer a.mu.Unlock()
51+
return a.currentState.StateName()
52+
}
53+
54+
func (a *ATM) InsertCard() error {
55+
a.mu.Lock()
56+
defer a.mu.Unlock()
57+
return a.currentState.InsertCard()
58+
}
59+
60+
func (a *ATM) GetCardDetail() error {
61+
a.mu.Lock()
62+
defer a.mu.Unlock()
63+
return a.currentState.GetCardDetail()
64+
}
65+
66+
func (a *ATM) InsertPin() error {
67+
a.mu.Lock()
68+
defer a.mu.Unlock()
69+
return a.currentState.InsertPin()
70+
}
71+
72+
func (a *ATM) DispenserAmount() error {
73+
a.mu.Lock()
74+
if err := a.currentState.DispenserAmount(); err != nil {
75+
return err
76+
}
77+
78+
a.mu.Unlock()
79+
80+
a.CleanPreviousAtmTransaction()
81+
82+
return nil
83+
}
84+
85+
func (a *ATM) SelectAccount() error {
86+
a.mu.Lock()
87+
defer a.mu.Unlock()
88+
return a.currentState.SelectAccount()
89+
}
90+
91+
func (a *ATM) Execute(operation func() error) {
92+
err := operation()
93+
if err != nil {
94+
a.CleanPreviousAtmTransaction()
95+
a.SetState(a.insertCard)
96+
fmt.Println("All operation will be nil operation:")
97+
fmt.Println("Error while operation:", err.Error())
98+
}
99+
}
100+
101+
func NewATM() *ATM {
102+
atm := &ATM{
103+
countOfNotes: map[string]int{
104+
"500": 1000,
105+
"200": 2000,
106+
"100": 1000,
107+
},
108+
WithdrawAs: &WithdrawNote{},
109+
}
110+
111+
atm.insertCard = &InsertCard{
112+
atm: atm,
113+
}
114+
115+
atm.readCard = &ReadCard{
116+
atm: atm,
117+
}
118+
119+
atm.insertPin = &InsertPin{
120+
atm: atm,
121+
}
122+
123+
atm.selectAccount = &SelectAccount{
124+
atm: atm,
125+
}
126+
127+
atm.dispenserAmount = &DispenserAmount{
128+
atm: atm,
129+
}
130+
131+
atm.SetState(atm.insertCard)
132+
133+
return atm
134+
}

atm-machine/atm/atm_state.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package atm
2+
3+
type ATMState interface {
4+
StateName() string
5+
InsertCard() error
6+
InsertPin() error
7+
SelectAccount() error
8+
GetCardDetail() error
9+
DispenserAmount() error
10+
}
11+
12+
type ATMAbstract struct{}
13+
14+
func (s *ATMAbstract) InsertCard() error {
15+
return nil
16+
}
17+
18+
func (s *ATMAbstract) InsertPin() error {
19+
return nil
20+
}
21+
22+
func (s *ATMAbstract) AuthticateCard() error {
23+
return nil
24+
}
25+
26+
func (s *ATMAbstract) DispenserAmount() error {
27+
return nil
28+
}
29+
30+
func (s *ATMAbstract) SelectAccount() error {
31+
return nil
32+
33+
}
34+
35+
func (s *ATMAbstract) GetCardDetail() error {
36+
return nil
37+
38+
}
39+
40+
func (s *ATMAbstract) StateName() string {
41+
return "ATMAbstract"
42+
}

atm-machine/atm/card_reader.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package atm
2+
3+
import "atm-machine/model"
4+
5+
const (
6+
InvalidCard = "invalid"
7+
)
8+
9+
// This will be actuall implementation of card reader // i am returning a card from here
10+
type CardReader struct {
11+
}
12+
13+
func NewCardReader() CardReader {
14+
return CardReader{}
15+
}
16+
17+
// for now it will return some dummy card depend of input for acting as multiple car
18+
func (c CardReader) ReadCard(cardType string) model.Card {
19+
switch cardType {
20+
case InvalidCard:
21+
return model.Card{
22+
Status: model.NoCard,
23+
}
24+
default:
25+
return model.Card{
26+
BankName: "hdfc",
27+
CardNo: "34567-sd55498459",
28+
AccountNo: "bshy4859-sdhhj66",
29+
Status: model.Active,
30+
UserName: "Himanshu sharma"}
31+
32+
}
33+
34+
}

atm-machine/atm/insert_card.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package atm
2+
3+
type InsertCard struct {
4+
atm *ATM
5+
ATMAbstract
6+
}
7+
8+
func (d *InsertCard) InsertCard() error {
9+
d.atm.SetState(d.atm.readCard)
10+
return nil
11+
}
12+
13+
func (d *InsertCard) StateName() string {
14+
return "InsertCard"
15+
}

atm-machine/atm/insert_pin.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package atm
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
)
7+
8+
type InsertPin struct {
9+
atm *ATM
10+
ATMAbstract
11+
}
12+
13+
func (s *InsertPin) InsertPin() error {
14+
// logic to valid pin, we can call api client inside it, i will return sucess and error depend on input
15+
fmt.Println("Enter 4 letter pin for card:\n")
16+
17+
var pin string
18+
19+
fmt.Scanf("%s", &pin)
20+
21+
if pin == "1111" {
22+
s.atm.SetState(s.atm.selectAccount)
23+
return nil
24+
} else {
25+
return errors.New("Pin is not valid")
26+
}
27+
}
28+
29+
func (d *InsertPin) StateName() string {
30+
return "InsetPin"
31+
}

atm-machine/atm/read_card.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package atm
2+
3+
type ReadCard struct {
4+
atm *ATM
5+
ATMAbstract
6+
}
7+
8+
func (d *ReadCard) GetCardDetail() error {
9+
card := NewCardReader().ReadCard("valid")
10+
if card.CardNo == "" {
11+
d.atm.SetState(d.atm.insertCard)
12+
}
13+
d.atm.card = card
14+
d.atm.SetState(d.atm.insertPin)
15+
return nil
16+
}
17+
18+
func (d *ReadCard) StateName() string {
19+
return "ReadCard"
20+
}

atm-machine/atm/select_account.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package atm
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
type SelectAccount struct {
8+
atm *ATM
9+
ATMAbstract
10+
}
11+
12+
func (s *SelectAccount) SelectAccount() error {
13+
14+
var accountType string
15+
var err error
16+
17+
fmt.Println("Select account type SAVING OR CURRENT:\n")
18+
fmt.Scanf("%s", &accountType)
19+
20+
if s.atm.account, err = AccountFactory(AccountType(accountType)); err != nil {
21+
return err
22+
}
23+
24+
s.atm.SetState(s.atm.dispenserAmount)
25+
26+
return nil
27+
28+
}
29+
30+
func (s *SelectAccount) StateName() string {
31+
return "SelectAccount"
32+
}

0 commit comments

Comments
 (0)