Ok, jeg syntes det minibank-programmet var litt interessant. Så jeg satte av et par timer til implementering og testing. Det er svært mange måter å løse dette på, men skreiv det i håp om at noen kanskje kunne finne dette interessant og/eller lærerikt.
Målet med dette er at minibanken skal være dynamisk og enkel å kunne bygge ut videre for all mulig funksjonalitet man skulle kunne ønske seg. Det skal også være mulig å koble til minibanken over internett (typisk med putty i raw mode).
Blir kun å dra med enkelte eksempler fra kildekoden i denne posten, det blir for mye å gi alt her, dere kan laste ned hele sourcen fra adressen. Den er også skrevet i C#, ikke php slik kodeeksemplene sier.
Jeg anbefaler dessuten at dersom dere vil leke med dette, så bruker dere PuTTY, satt i Raw mode
Fullstendig kode kan lastes ned fra
http://80.65.51.238/~kenwi/minibank/MiniBank.rar
http://91.205.187.69/~kenwi/minibank/putty.png
Jeg kan også nevne at jeg har satt opp en server som står på 62.92.122.25 port 2471 som dere kan teste på.
Programmet tar ikke høyde for brukerimput som ikke er skrevet inn korrekt, så dere trenger sikkert ikke være så altfor kreative med akkurat dét.
Planlegging og implementasjon
Jeg har en hovedklasse jeg kaller Server, denne inneholder all statisk data i systemet. Denne inneholder for eksempel en liste over alle kontoer som eksisterer i systemet, og alle kontoenes verdier og eiere.
Det skal være en dynamisk meny, jeg skal kunne skreddersy nye menyer og sette opp hvilken funksjonalitet de enkelte menyene tilbyr. Dette betyr at jeg er nødt til å lage en klasse som beskriver en meny, og jeg er også nødt til å lage klasser som beskriver funksjonaliteten til de enkelte kommandoene i menyen.
Jeg tenker i utgangspunktet to menyer, men jeg har muligheten til kjapt og enkelt lage så mange jeg har lyst til, men så mange undernivå som jeg overhodet ønsker.
- Login
- Vis penger på konto
- Sett inn penger på konto
- Overfør penger fra konto til en annen konto
- Vis kontoer
- Quit
- Lag konto
- Vis kontoer
- Quit
Hver av disse tingene representerer en kommando, kommandoen er en klasse som inneholder en konstruktør og en funksjon som heter Execute(). Dette er likt for alle kommandoer. Dette betyr at jeg kan iterere igjennom en liste med kommandoer og utføre samme funksjonen på alle objektene. Høres flott ut? Greit.
Jeg starter med å lage klassen som heter Server, denne tar imot tilkoblinger fra klienter og aksepterer disse. Lager et nytt objekt fra klassen som heter MainMenu, denne er den første menyen som blir presentert til en bruker. Konstruktøren til MainMenu tar med en Socket som parameter, slik at jeg kan få en referanse til klienten som har koblet til. MainMenu tar også med seg en referanse til listen over kontoer som eksisterer. Dette er fordi jeg vil ønske å gjøre endringer på kontoene videre i menyene.
En tråd opprettes og utfører funksjonen Process() mot den klienten som akkurat koblet til. Dette betyr at jeg har mange brukere som kan koble til, og alle vil i bunn og grunn ha tilgang til å endre på listen over kontoer.
Kode
while( true )
{
// Aksepter tilkoblingen til en klient
Socket socket = listener.AcceptSocket();
if( socket.Connected )
{
Debug.WriteLine( "Client Connected" );
MainMenu menu = new MainMenu( socket, accounts );
Thread thread = new Thread( new ThreadStart( menu.Process ) );
thread.Start();
}
}
Slik Process() funksjonen i MainMenu fungerer er følgende:
1: Send meny til brukeren
2: Ta imot input, utfør funksjonen Parse().
Parse() Går igjennom listen over kommandoer som er tilknyttet MainMenu og kjører funksjonen Execute() på kommandoen som er skrevet inn.
Kode
public string Parse( string command )
{
// Fjern newline fra kommandoen og hvilken kommando brukeren har kjørt
command = command.Replace( Environment.NewLine, "" );
int num = 0;
foreach( IMenuItem item in children )
{
num++;
if( command.Equals( num.ToString() ) )
{
// Kjør kommandoen
item.Execute();
// Ingen flere kommandoer skal avfyres
break;
}
}
// Returner menyen tilbake til klienten
return this.ToString();
}
Jeg viser dere en liten del av konstruktøren til MainMenu, og her er kommandoene som er puttet inn i den.
Kode
public MainMenu( Socket socket, List<Account> accounts )
{
// ...
// Slenger med accounts som referanse, vi ønsker å dra med Accounts
children.Add( new MenuItemLogin( socket, accounts ) );
children.Add( new MenuItemCreateAccount( socket, accounts ) );
children.Add( new MenuItemDisplayAccounts( socket, accounts ) );
children.Add( new MenuItemQuit( socket ) );
}
Et lite eksempel her er kommandoen Quit som er med i MainMenu. Som dere kan se så har den fått med seg en referanse fra MainMenu (som igjen har fått referansen sin fra Server-klassen). Dette betyr at jeg har en link mellom klienten og Quit-kommandoen, og jeg kan dermed utføre funksjonen Close() på socketen og stenge ned tilgangen til nøyaktig denne brukeren. Artig?
Kode
// Klassen arver fra MenuItem og implementerer Interfacet IMenuItem
class MenuItemQuit : MenuItem, IMenuItem
{
private Socket client;
// Referanse til klienten så man kan sende data tilbake,
// ta også med hvilken tekst itemet skal ha, send tilbake til base-klassen
public MenuItemQuit( Socket client )
: base( "Quit" )
{
this.client = client;
}
// All logikk som skal inn i kommandoen
public void Execute( )
{
client.Close();
}
}
Så for å vise at jeg kan lage et ny menyklasse (med arv fra MainMenu, fordi klassene skal ha samme type funksjonalitet) veldig enkelt drar jeg med menyen som blir presentert til brukeren når han har logget inn. Legg også merke til at jeg har med kommandoen MenuItemQuit slik jeg hadde i toppmenyen. Eksemplet viser at jeg kjapt og enkelt kan skreddersy alle menyene mine, og lage så mange og avanserte jeg kan tenke meg fram til. Dette betyr at det selvfølgelig blir mye mye mer kode enn om jeg hadde laget systemet rett fram, men også at systemet blir veldig mye mer dynamisk!
Kode
// Denne klassen arver fra MainMenu og vi kan derfor skreddersy denne
// menyen med å supplere de forskjellige kommandoene som den skal vise
class LoggedinMenu : MainMenu
{
// Brukeren som er logget inn
private Account account;
public LoggedinMenu( Socket socket, List<Account> accounts, Account account )
{
this.socket = socket;
this.account = account;
this.accounts = accounts;
children.Add( new MenuItemViewBalance( socket, accounts, account ) );
children.Add( new MenuItemDeposit( socket, account ) );
children.Add( new MenuItemWireTransfer( socket, accounts, account ) );
children.Add( new MenuItemDisplayAccounts( socket, accounts ) );
children.Add( new MenuItemQuit( socket ) );
}
}
Så, la oss begynne å leke litt med serveren. Jeg starter med å koble opp mot den med PuTTY ( i RAW-mode), jeg lager meg en konto som heter m0b, og logger inn med denne. Deretter setter jeg inn 1337 på kontoen og ser etterpå hvor mye penger som er inne på kontoen.
Kode
1 Login
2 Create Account
3 List Accounts
4 Quit
Input: 2
Please specify username: m0b
1 Login
2 Create Account
3 List Accounts
4 Quit
Input: 1
Please specify username: m0b
You are now logged in as m0b
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input: 2
Value: 1337
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input: 1
Your account balance: 1337
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input:
Ser dermed at å legge inn penger på en konto funker helt utmerket. Det neste jeg gjør er å koble meg opp med en til klient med PuTTY og lager enda en ny konto jeg kaller idlemob. Ser på hvilke kontoer som eksisterer. Som forventet så ser jeg nå to kontoer, idlemob og m0b. Sjekker kontobalansen på idlemob også. Som forventet er det ingen penger der.
Kode
1 Login
2 Create Account
3 List Accounts
4 Quit
Input: 2
Please specify username: idlemob
1 Login
2 Create Account
3 List Accounts
4 Quit
Input: 1
Please specify username: idlemob
You are now logged in as idlemob
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input: 4
--- Accounts ---
m0b
idlemob
----------------
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input: 1
Your account balance: 0
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input:
Det er nå det kanskje kan bli litt moro. Jeg går tilbake til m0b-kontoen min med 1337 spenn og overfører 539 spenn til idlemob.
Kode
Your account balance: 1337
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input: 3
Please specify destination account: idlemob
Please specify amount to wire: 539
You successfully wired 539 to idlemob
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input:
Såååå... Tilbake til sessionen jeg har gående med brukeren idlemob, sjekker saldoen.
Kode
Your account balance: 0
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input: 1
Your account balance: 539
1 View balance
2 Deposit
3 Wire transfer
4 List Accounts
5 Quit
Input:
Kjempegreier!
Det som er artig her er jo selvfølgelig at dette blir værende på serveren til jeg slår den av. Jeg kan altså koble meg til med putty, gjøre ting, koble av, og koble til igjen å resume ved å logge inn på brukeren.
http://91.205.187.69/~kenwi/minibank/putty2.png
Sist endret av m0b; 14. mai 2009 kl. 20:04.