Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
43 changes: 43 additions & 0 deletions atm-machine/atm/Account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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 SavingAccount struct {
acctNo string
balance float64
}

func (s *SavingAccount) GetBalance() float64 {
return s.balance
}

type CurrentAccount struct {
acctNo string
balance float64
}

func (c *CurrentAccount) GetBalance() float64 {
return c.balance
}
120 changes: 120 additions & 0 deletions atm-machine/atm/atm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package atm

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

type WithdrawNote struct {
FiveHundread int
Hundread int
Left int
}

type ATM struct {
countOFNotes map[string]int
card model.Card
account Account
uiOption []string
WithdrawAs *WithdrawNote
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) ResetAtm() {
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) DispenserAmount() error {
a.mu.Lock()
defer a.mu.Unlock()
return a.currentState.DispenserAmount()
}

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.ResetAtm()
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.selectAccount = &SelectAccount{
atm: atm,
}

atm.readCard = &ReadCard{
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"
}
30 changes: 30 additions & 0 deletions atm-machine/atm/card_reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package atm

import "atm-machine/model"

// 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 "invalid":
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"
}
30 changes: 30 additions & 0 deletions atm-machine/atm/insert_pin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package atm

import (
"errors"
"fmt"
)

type InsetPin struct {
atm *ATM
ATMAbstract
}

func (s *InsetPin) 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" {
return nil
} else {
return errors.New("Pin is not valid")
}
}

func (d *InsetPin) 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.selectAccount)
return nil
}

func (d *ReadCard) StateName() string {
return "ReadCard"
}
30 changes: 30 additions & 0 deletions atm-machine/atm/select_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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)

s.atm.account, err = AccountFactory(AccountType(accountType))

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

return err

}

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

type Withdraw interface {
ProcessAmount(atm *ATM, atmamount float64)
}

func NewWithDrawPipeline() Withdraw {
return fiveHundreadWithdraw{
next: hundreadHundreadWithdraw{},
}
}

type fiveHundreadWithdraw struct {
next Withdraw
}

func (f fiveHundreadWithdraw) ProcessAmount(atm *ATM, atmamount float64) {
div := atmamount / 500
rem := int(atmamount) % 500
atm.WithdrawAs.FiveHundread = int(div)
f.next.ProcessAmount(atm, float64(rem))
}

type hundreadHundreadWithdraw struct {
next Withdraw
}

func (h hundreadHundreadWithdraw) ProcessAmount(atm *ATM, atmamount float64) {
div := atmamount / 100
rem := int(atmamount) % 100
atm.WithdrawAs.Hundread = int(div)
atm.WithdrawAs.Left = rem

}
Loading