Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ The sixth project in this repository is a Meeting Scheduler, a system that simul
- Room calendar management, tracking when a room is booked or available.
- Concurrency control, preventing scheduling conflicts in real time.

## ATM

The seventh project in this repository is a ATM machine, a system that simulates ATM. It demonstrates the following concepts:

- State design pattern for different state of ATM.
- Chain of responsibility principle for withdrawing cash.
- Template pattern for handling error of each place in one place.
- Concurrency control, preventing atm to conflict.

### Features

- Book and cancel meetings seamlessly.
Expand Down
42 changes: 42 additions & 0 deletions atm-machine/atm/Account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package atm

import "errors"

type Account interface {
GetBalance() float64
}

type AccountType string

const (
Saving AccountType = "SAVING"
Current AccountType = "CURRENT"
)

func AccountFactory(accType AccountType) (Account, error) {
switch accType {
case Current:
return &CurrentAccount{}, nil
case Saving:
return &SavingAccount{}, nil
default:
return nil, errors.New("Not a valid account type")
}
}

type BaseAccount struct {
acctNo string
balance float64
}

func (b *BaseAccount) GetBalance() float64 {
return b.balance
}

type SavingAccount struct {
BaseAccount
}

type CurrentAccount struct {
BaseAccount
}
134 changes: 134 additions & 0 deletions atm-machine/atm/atm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package atm

import (
"atm-machine/model"
"fmt"
"sync"
)

type WithdrawNote struct {
FiveHundred int
Hundred int
Left int
}

type ATM struct {
countOfNotes map[string]int
card model.Card
account Account
uiOption []string
WithdrawAs *WithdrawNote
insertPin ATMState
insertCard ATMState
readCard ATMState
selectAccount ATMState
dispenserAmount ATMState
currentState ATMState
mu sync.RWMutex
}

func (a *ATM) SetState(s ATMState) {
a.currentState = s
}

func (a *ATM) CleanPreviousAtmTransaction() {
a.mu.Lock()
defer a.mu.Unlock()
a.WithdrawAs = &WithdrawNote{}
a.card = model.Card{}
a.account = nil
}

func (a *ATM) PrintMoney() {
a.mu.Lock()
defer a.mu.Unlock()
fmt.Printf("\n500 note present:%d, 100 not present:%d", a.countOfNotes["500"], a.countOfNotes["100"])
}

func (a *ATM) StateName() string {
a.mu.Lock()
defer a.mu.Unlock()
return a.currentState.StateName()
}

func (a *ATM) InsertCard() error {
a.mu.Lock()
defer a.mu.Unlock()
return a.currentState.InsertCard()
}

func (a *ATM) GetCardDetail() error {
a.mu.Lock()
defer a.mu.Unlock()
return a.currentState.GetCardDetail()
}

func (a *ATM) InsertPin() error {
a.mu.Lock()
defer a.mu.Unlock()
return a.currentState.InsertPin()
}

func (a *ATM) DispenserAmount() error {
a.mu.Lock()
if err := a.currentState.DispenserAmount(); err != nil {
return err
}

a.mu.Unlock()

a.CleanPreviousAtmTransaction()

return nil
}

func (a *ATM) SelectAccount() error {
a.mu.Lock()
defer a.mu.Unlock()
return a.currentState.SelectAccount()
}

func (a *ATM) Execute(operation func() error) {
err := operation()
if err != nil {
a.CleanPreviousAtmTransaction()
a.SetState(a.insertCard)
fmt.Println("All operation will be nil operation:")
fmt.Println("Error while operation:", err.Error())
}
}

func NewATM() *ATM {
atm := &ATM{
countOfNotes: map[string]int{
"500": 1000,
"200": 2000,
"100": 1000,
},
WithdrawAs: &WithdrawNote{},
}

atm.insertCard = &InsertCard{
atm: atm,
}

atm.readCard = &ReadCard{
atm: atm,
}

atm.insertPin = &InsertPin{
atm: atm,
}

atm.selectAccount = &SelectAccount{
atm: atm,
}

atm.dispenserAmount = &DispenserAmount{
atm: atm,
}

atm.SetState(atm.insertCard)

return atm
}
42 changes: 42 additions & 0 deletions atm-machine/atm/atm_state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package atm

type ATMState interface {
StateName() string
InsertCard() error
InsertPin() error
SelectAccount() error
GetCardDetail() error
DispenserAmount() error
}

type ATMAbstract struct{}

func (s *ATMAbstract) InsertCard() error {
return nil
}

func (s *ATMAbstract) InsertPin() error {
return nil
}

func (s *ATMAbstract) AuthticateCard() error {
return nil
}

func (s *ATMAbstract) DispenserAmount() error {
return nil
}

func (s *ATMAbstract) SelectAccount() error {
return nil

}

func (s *ATMAbstract) GetCardDetail() error {
return nil

}

func (s *ATMAbstract) StateName() string {
return "ATMAbstract"
}
34 changes: 34 additions & 0 deletions atm-machine/atm/card_reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package atm

import "atm-machine/model"

const (
InvalidCard = "invalid"
)

// This will be actuall implementation of card reader // i am returning a card from here
type CardReader struct {
}

func NewCardReader() CardReader {
return CardReader{}
}

// for now it will return some dummy card depend of input for acting as multiple car
func (c CardReader) ReadCard(cardType string) model.Card {
switch cardType {
case InvalidCard:
return model.Card{
Status: model.NoCard,
}
default:
return model.Card{
BankName: "hdfc",
CardNo: "34567-sd55498459",
AccountNo: "bshy4859-sdhhj66",
Status: model.Active,
UserName: "Himanshu sharma"}

}

}
15 changes: 15 additions & 0 deletions atm-machine/atm/insert_card.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package atm

type InsertCard struct {
atm *ATM
ATMAbstract
}

func (d *InsertCard) InsertCard() error {
d.atm.SetState(d.atm.readCard)
return nil
}

func (d *InsertCard) StateName() string {
return "InsertCard"
}
31 changes: 31 additions & 0 deletions atm-machine/atm/insert_pin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package atm

import (
"errors"
"fmt"
)

type InsertPin struct {
atm *ATM
ATMAbstract
}

func (s *InsertPin) InsertPin() error {
// logic to valid pin, we can call api client inside it, i will return sucess and error depend on input
fmt.Println("Enter 4 letter pin for card:\n")

var pin string

fmt.Scanf("%s", &pin)

if pin == "1111" {
s.atm.SetState(s.atm.selectAccount)
return nil
} else {
return errors.New("Pin is not valid")
}
}

func (d *InsertPin) StateName() string {
return "InsetPin"
}
20 changes: 20 additions & 0 deletions atm-machine/atm/read_card.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package atm

type ReadCard struct {
atm *ATM
ATMAbstract
}

func (d *ReadCard) GetCardDetail() error {
card := NewCardReader().ReadCard("valid")
if card.CardNo == "" {
d.atm.SetState(d.atm.insertCard)
}
d.atm.card = card
d.atm.SetState(d.atm.insertPin)
return nil
}

func (d *ReadCard) StateName() string {
return "ReadCard"
}
32 changes: 32 additions & 0 deletions atm-machine/atm/select_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package atm

import (
"fmt"
)

type SelectAccount struct {
atm *ATM
ATMAbstract
}

func (s *SelectAccount) SelectAccount() error {

var accountType string
var err error

fmt.Println("Select account type SAVING OR CURRENT:\n")
fmt.Scanf("%s", &accountType)

if s.atm.account, err = AccountFactory(AccountType(accountType)); err != nil {
return err
}

s.atm.SetState(s.atm.dispenserAmount)

return nil

}

func (s *SelectAccount) StateName() string {
return "SelectAccount"
}
Loading