I want to write a java program that would keep track of a bank account
right now I have the following simple program:
public class account
{
private double balance;
private String owner;
public account(double x, String s) { balance=x; owner=s; }
public String owner() { return owner; }
public void withdraw(double a) { balance -= a; }
public void deposit(double a) { balance += a; }
public void printbalance() { System.out.println(balance); }
// main for testing:
public static void main(String[] argv)
{
account a1 = new account(2000,"you boss");
account a2 = new account(1000,"me nerd");
a1.deposit(400);
a2.withdraw(300000); // not enough money!
a2.withdraw(-500000); // trying to cheat!
a1.printbalance();
a2.printbalance();
}//main
} // account
And I want to add to this program using aspectj the following:
1- I want to prevent the account from withdraw a greater amount of the current balance and withdraw a negative numbers.
2- also I want it to prevent deposit a negative numbers.
3- I need to add a graphical interface , (buttons )
4- add secret pin or password that needs to be entered before a customer can make transaction.
5- keep track of all the transactions (withdraws and deposits) made on an account, and print out a report when asked for.
I would appreciate your help. Thank you.
privileged aspect newAccount
{
//withdraw (prevent withdraw negative numbers and number greater than the //current balance)
void around(account a, double x) : execution(void account.withdraw(double)) && target(a) && args(x){
if(x > a.balance){
System.out.println("not enough money!");
return;
}else if(x < 0){
System.out.println("trying to cheat!");
return;
}
proceed(a, x);
}
//Deposit: prevent deposit negative number
void around(double x) : execution(void account.deposit(double)) && args(x){
if(x < 0){
System.out.println("trying to deposit negtive money!");
return;
}
proceed(x);
}
after() : execution(public static void *.main(String[])){
account.a3 = new account(3000,"he nerd");
a3.deposit(-100);
a3.printbalance();
}
//To Do: pin secret password
//To Do: Transaction Record
}
I can see that you are still learning Java because you don't know the basic programming conventions such as that
class names should start with an upper case letter,
variables, parameters and fields should have understandable names not not single letters.
You are also using direct field access from a privileged aspect instead of just creating public getter methods for your class's fields and using those. A toString method is also helpful because then you can easily print the object without accessing getters and fabricating your own output.
Besides, the advice running after the main method is a nice experiment but does not make make much sense. Because the account owner has the same name as one of the account owners in your application, it looks as if you want to hack into that account. I commented the code there so as to explain why it cannot work like that.
I also refactored both your application class and the aspect to now look like this without changing the functionality:
package de.scrum_master.app;
public class Account {
private String owner;
private double balance;
public Account(String owner, double balance) {
this.owner = owner;
this.balance = balance;
}
public void withdraw(double amount) {
balance -= amount;
}
public void deposit(double amount) {
balance += amount;
}
public String getOwner() {
return owner;
}
public double getBalance() {
return balance;
}
#Override
public String toString() {
return "Account[owner=" + owner + ", balance=" + balance + "]";
}
public static void main(String[] argv) {
Account bossAccount = new Account("Boss", 2000);
Account nerdAccount = new Account("Nerd", 1000);
bossAccount.deposit(400);
nerdAccount.withdraw(200);
bossAccount.withdraw(300000); // Cannot withdraw more than account balance
nerdAccount.withdraw(-500000); // Cannot withdraw a negative amount
bossAccount.deposit(-123456); // Cannot deposit a negative amount
System.out.println(bossAccount);
System.out.println(nerdAccount);
}
}
package de.scrum_master.aspect;
import de.scrum_master.app.Account;
public aspect AccountAspect {
// Withdrawal
void around(Account account, double amount) :
execution(void Account.withdraw(double)) &&
target(account) &&
args(amount)
{
if (amount > account.getBalance()) {
System.out.println("Cannot withdraw more than account balance");
return;
}
if (amount < 0) {
System.out.println("Cannot withdraw a negative amount");
return;
}
proceed(account, amount);
}
// Deposit
void around(double amount) :
execution(void Account.deposit(double)) &&
args(amount)
{
if (amount < 0) {
System.out.println("Cannot deposit a negative amount");
return;
}
proceed(amount);
}
// This does not make any sense because
// 1. it happens after the application ends (after leaving main method)
// 2. Even though the account owner is the same as in the main method,
// it does not mean that by creating a new object with the same name
// the "Nerd" can manipulate the original account balance. You have to
// intercept the original Account object and manipulate it directly.
after() : execution(public static void *.main(String[])) {
System.out.println("--- after end of main program ---");
Account account = new Account("Nerd", 3000);
account.deposit(-100);
System.out.println(account);
}
// TODO: PIN secret password
// TODO: transaction record
}
The console log will be:
Cannot withdraw more than account balance
Cannot withdraw a negative amount
Cannot deposit a negative amount
Account[owner=Boss, balance=2400.0]
Account[owner=Nerd, balance=800.0]
--- after end of main program ---
Cannot deposit a negative amount
Account[owner=Nerd, balance=3000.0]
I will not do your homework assignment for you, but give you some hints:
PIN (secret password): The Account class needs a field pin which could be set in a constructor and should not have a public getter method in order to avoid that anyone can access the PIN. If the assignment requires you not to edit the base class but solve the problem via AOP, you can use inter-type definition (ITD) in order to add a private field and a public setter, maybe even an additional constructor to the class. Next you would add an advice which would ask the user to enter a PIN on the console if he tries to access any transactional methods such as deposit and withdraw of a certain account for the first time. After entering the PIN correctly he would be able to continue, otherwise there would be an error message and the transaction would be forbidden. The aspect itself could keep a cache (temporary storage) of all Account objects - probably you want to use a Set<Account> - which have been successfully authenticated during the running session, so as to avoid that the user has to enter the PIN for the same account again.
Transaction record per account: Again, you can use ITD in order to add something like a List<TransactionRecord> as a field to the Account, initialise it with an empty list and then add a transaction record for each deposit or withdrawal. You can also keep it simple for your proof of concept, not creating a TransactionRecord helper class but just using a List<Double> for the transactions, recording positive amounts for deposits and negative ones for withdrawals. A List<String> with elements like "deposit 123.45" or "withdrawal 67.89" is also a viable alternative. The important thing is that your teacher can see the correct aspect logic.
Related
I have 3 classes, Account, CappedAccount, UserAccount,
CappedAccount, and UserAccount both extend Account.
Account contains the following:
abstract class Account {
...
/**
* Attempts to add money to account.
*/
public void add(double amount) {
balance += amount;
}
}
CappedAccount overrides this behavior:
public class CappedAccount extends Account {
...
#Override
public void add(double amount) {
if (balance + amount > cap) { // New Precondition
return;
}
balance += amount;
}
}
UserAccount doesn't override any methods from Account, so it doesn't need to be stated.
My question is, does CappedAccount#add violate LSP, and if it does, how can I design it to comply with LSP.
For example, does add() in CappedAccount count as "strengthening preconditions"?
It's important to remember the LSP covers both syntax and semantics. It covers both what the method is coded to do, and what the method is documented to do. This means vague documentation can make it difficult to apply the LSP.
How do you interpret this?
Attempts to add money to account.
It's clear the add() method is not guaranteed to add money to the account; so the fact that CappedAccount.add() may not actually add money seems acceptable. But there is no documentation of what should be expected when an attempt to add money fails. Since that use case is undocumented, "do nothing" seems like an acceptable behavior, and therefore we have no LSP violation.
To be on the safe side, I would amend the documentation to define expected behavior for a failed add() i.e. explicitly define the post-condition. Since the LSP covers both syntax and semantics, you can fix a violation by modifying either one.
TLDR;
if (balance + amount > cap) {
return;
}
is not a precondition but an invariant, hence not a violation (on his own) of the Liskov Substition Principle.
Now, the actual answer.
A real precondition would be (pseudo code):
[requires] balance + amount <= cap
You should be able to enforce this precondition, that is check the condtion and raise an error if it is not met. If you do enforce the precondition, you'll see that the LSP is violated:
Account a = new Account(); // suppose it is not abstract
a.add(1000); // ok
Account a = new CappedAccount(100); // balance = 0, cap = 100
a.add(1000); // raise an error !
The subtype should behave like its supertype (see below).
The only way to "strengthen" the precondition is to strenghten the invariant. Because the invariant should be true before and after each method call. The LSP is not violated (on his own) by a strengthened invariant, because the invariant is given for free before the method call: it was true at the initialisation, hence true before the first method call. Because it's an invariant, it is true after the first method call. And step by step, is always true before the next method call (this is a mathematicual induction...).
class CappedAccount extends Account {
[invariant] balance <= cap
}
The invariant should be true before and after the method call:
#Override
public void add(double amount) {
assert balance <= cap;
// code
assert balance <= cap;
}
How would you implement that in the add method? You have some options. This one is ok:
#Override
public void add(double amount) {
assert balance <= cap;
if (balance + amount <= cap) {
balance += cap;
}
assert balance <= cap;
}
Hey, but that's exactly what you did! (There is a slight difference: this one has one exit to check the invariant.)
This one too, but the semantic is different:
#Override
public void add(double amount) {
assert balance <= cap;
if (balance + amount > cap) {
balance = cap;
} else {
balance += cap;
}
assert balance <= cap;
}
This one too but the semantic is absurd (or a closed account?):
#Override
public void add(double amount) {
assert balance <= cap;
// do nothing
assert balance <= cap;
}
Okay, you added an invariant, not a precondition, and that's why the LSP is not violated. End of the answer.
But... this is not satisfying: add "attempts to add money to account". I would like to know if it was a success!! Let's try this in the base class:
/**
* Attempts to add money to account.
* #param amount the amount of money
* #return True if the money was added.
*/
public boolean add(double amount) {
[requires] amount >= 0
[ensures] balance = (result && balance == old balance + amount) || (!result && balance == old balance)
}
And the implementation, with the invariant:
/**
* Attempts to add money to account.
* #param amount the amount of money
* #return True is the money was added.
*/
public boolean add(double amount) {
assert balance <= cap;
assert amount >= 0;
double old_balance = balance; // snapshot of the initial state
bool result;
if (balance + amount <= cap) {
balance += cap;
result = true;
} else {
result = false;
}
assert (result && balance == old balance + amount) || (!result && balance == old balance)
assert balance <= cap;
return result;
}
Of course, nobody writes code like that, unless you use Eiffel (that might be a good idea), but you see the idea. Here's a version without all the conditions:
public boolean add(double amount) {
if (balance + amount <= cap) {
balance += cap;
return true;
} else {
return false;
}
Please note the the LSP in its original version ("If for each object o_1 of type S there is an object o_2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o_1 is substituted for o_2, then S is a subtype of T") is violated. You have to define o_2 that works for each program. Choose a cap, let's say 1000. I'll write the following program:
Account a = ...
if (a.add(1001)) {
// if a = o_2, you're here
} else {
// else you might be here.
}
That's not a problem because, of course, everyone uses a weaken version of the LSP: we don't want the beahvior to be unchanged (subtype would have a limited interest, performance for instance, think of array list vs linked list)), we want to keep all "the desirable properties of that program" (see this question).
I'm new to Java and I have created a class which is based on the question from this exercise.
I've tried my best to follow it and I think the reason why my variables are 0 or null is that I didn't write anything in the constructor. The question didn't say anything about what to write in the constructor.
I'm printing everything out because I want to see the result, but all I get from getCardNumber is null, getBalance is 0, coffee is 0. redeemFreeCoffee and isFreeCoffeeAvailable does work, simply because there are no variables that override them.
Here's the full question:
a. Each loyalty card stores the card number, current balance (the number of points) and the number of coffees on the card. Implement a
constructor with the card number (of type String) as its argument and
method getCardNumber() and getBalance().
b. Implement a method collectRewards(double amount, int coffees) that takes the amount spent (in pounds) and the number of coffees
bought and increases the balance (by one point for every pound spent)
as well as the number of coffees on the card.
c. Implement a method isFreeCoffeeAvailable() that checks whether a free coffee is available, that is, whether the number of coffees on
the card is greater than or equal to 9.
d. Implement a method redeemFreeCoffee() that first checks whether a free coffee is available. If this is the case then it reduces the
number of coffees by 9 and returns true, otherwise false.
I've tried changing the variables from private to public but I still get the same result.
I've even tried putting my main in a different class but the result is still the same.
public String cardNumber;
public int balance;
public int coffee;
public double amount;
public String getCardNumber () {
return cardNumber;
}
public int getBalance () {
return balance;
}
public double collectRewards(double amount, int coffees) {
if (amount > 0) {
coffee++;
balance++;
}
return amount;
}
public int isFreeCoffeeAvailable(){
if (coffee >= 9) {
return coffee;
}
return coffee;
}
public boolean redeemFreeCoffee() {
if (coffee > 9) {
coffee-=9;
return true;
}
else {
return false;
}
}
public LoyaltyCard (String cardNumber){
}
public static void main (String[] args) {
String cardNumber = "0987654321";
LoyaltyCard LoyaltyCardOne = new LoyaltyCard(cardNumber);
System.out.printf("%s%n%s%n%s%n%s%n%s",LoyaltyCardOne.getCardNumber(),LoyaltyCardOne.getBalance(),LoyaltyCardOne.collectRewards(6.0,5),LoyaltyCardOne.redeemFreeCoffee(),LoyaltyCardOne.isFreeCoffeeAvailable());
}
I'd like to see the result for getCardNumber(), getBalance() and the amount of coffee.
all I get from getCardNumber is null
You never initialized it
public LoyaltyCard (String cardNumber){
this.cardNumber = cardNumber;
}
because there are no variables that override them.
I think you might be confused about what "override" means, but that isn't the problem.
getBalance is 0, coffee is 0
You're calling those before you ever "collect rewards"
You will need to collect before printing the invidiual values, and read the logic again - increase by one point for every pound spent. So, focus on changing this block to fix that.
if (amount > 0) {
coffee++;
balance++;
}
Note that the instructions don't say the collectRewards returns anything. Also coffee should be increased by the input parameter, maybe than just 1.
Otherwise, you would need to call collectRewards at least 9 times before the redeem and isAvailable methods would work.
And once those are, you could do this, rather than rewrite coffee > 9
if (this.isFreeCoffeeAvailable()) {
} else {
}
Note: isFreeCoffeeAvailable should probably return coffee > 9; rather than return the amount
In Java all non-local variables are initialized to 0, or null. So far in the code you don't set variables to your desired values. You can either create a constructor which takes values, e,g:
LoyaltyCard(int balance, int coffee, double amount) {
this.balance = balance;
this.coffee = coffee;
// ... and other fields
or create setters for each field:
public setBalance(int balance) {
this.balance = balance;
}
I understand the title is quite confusing so I'll clarify here.
The following is an extremely simple bank class I slapped together that takes the users name and balance and has deposit and withdraw classes.
public class Bank {
String user;
int balance = 0;
int pin;
public void setup (String n,int p)
{
user = n;
pin = p;
}
public void withdraw (int amount)
{
balance -= amount;
}
public void deposit (int amount)
{
balance += amount;
}
public String toString ()
{
return String.format("User: %s\nBalance: %s",user,balance);
}
}
Now. Here is my qualm. The following is another simple class I made to make use of the Bank class. I've tried HashMaps, Arrays and I'm sure I'm just missing a basic principle. This isn't for a class I would just like to know how to do the following:
I want to be able to create different users (I'm sure I'd have to use (like I have) a HashMap or ArrayList) and have EACH user have separate access to the balance variable, so when one person, lets say person X with pin 1234 withdraws funds, ONLY their funds are withdrawn.
I know I can just use a HashMap and set it up like
HashMap <String, Integer> HM = new HashMap <String, Integer> ();
HM.put("User",0)
where user is the name and 0 is the balance.
I can easily update different users balances, but this seems counterintuitive.
import java.lang.reflect.Array;
import java.util.*;
public class BankDriver {
public static void main (String [] args)
{
boolean quit = false;
Bank b = new Bank ();
Scanner sc = new Scanner (System.in);
do {
System.out.println("1: Create account\2: Quit");
int input = sc.nextInt();
switch (input)
{
case 1:
b.setup("Name",1234);
System.out.println(b.toString());
break;
case 2:
quit = true;
break;
}
}while(!quit);
System.out.println("Exiting.");
}
}
How would YOU accomplish this?
AGAIN. I know I can do it in different ways, but I was curious if I could accomplish it this way.
The object oriented design that you are using is not very good for what you are trying to achieve. If you want to have access for each client of the bank, you should create a Client class (or Account) with its getters and setters like this:
Also, I changed your integers to doubles
public class Client{
public int pin;
public String name;
public double amount;
public Client(String name, double amount, int pin){
this.name = name;
this.amount = amount;
this.pin = pin;
}
public String getName(){
return name;
}
public int getPin(){
return pin;
}
public void changePin(int newPin){
this.pin = newPin;
}
public void withdraw (double amount)
{
this.amount -= amount;
}
public void deposit (double amount)
{
this.amount += _amount;
}
#Override
public String toString ()
{
return String.format("User: %s\nBalance: %s",this.name,this.amount);
}
}
Now, from your void() you can create all the Clients that you want in any specific data structure. I'll give you an example using a HashMap
HashMap <String, Client> HM = new HashMap <String, Client>();
HM.put("Uri D. Charles", new Client("Uri D. Charles", 100.00, 1234));
HM.put("Luis Lavieri", new Client("Luis Lavieri", 2000.00, 1234));
After that you can manipulate the data structure as you prefer to access that data. I hope this is what you are looking for
First of all create a getter methods for name and pin.
You can create an ArrayList of Bank object and add the bank object to the arraylist each time a user created an account.
Then when the user want to withdraw you then ask them there name and pin and then loop the arraylist while comparing the pin and name of the bank object of the arraylist and the person name and pin that just entered.
If the object was found you can then ask the user how much money to withdraw and call the widraw method.
I am working on a practice assignment to improve my understanding of driver and resource class, I have created a resource class in which holds my code for any possible Bank Account being created. I then need to output that account and it's information, here are the instructions:
A. BankAccount.java and TestBankAccount.java
a. Assignment – text book (Project 5-5, p.197). Complete a UML summary for the resource
class. Write 2 constructors and all accessor and mutator methods for the field variables. Write
a toString() method.
b. Output – should appear exactly as the output shown below. The balance statement will come
from the toString() method. Format the toString() method with String.format(). All other
outputs are string literals (in quotes).
Open account Mickey has a current balance of $0.00
Deposit $500.00 Mickey has a current balance of $500.00
Withdraw $125.25 Mickey has a current balance of $374.75
Open account Minnie has a current balance of $1,000.00
Withdraw $73.21 Minnie has a current balance of $926.79
Open account Goofy has a current balance of $10,000.00
Close account Goofy has a current balance of $0.00
My code for the resource class:
public class BankAccount
{
private String ownerName;
private double balance;
public BankAccount()
{
ownerName = "";
balance = 0.00;
}
public BankAccount(String name, double bal)
{
ownerName = name;
balance = bal;
}
public String toString()
{
return ownerName + " has a current balance of " + String.format("%1$,.2f", balance);
}
public String getOwnerName()
{
return ownerName;
}
public void setOwnerName(String name)
{
ownerName = name;
}
public void deposit(double d)
{
balance+=d;
}
public void withdrawl(double w)
{
balance-=w;
}
}
My code for the Driver class:
public class BankAccountDriver
{
public static void main(String[] args)
{
BankAccount micky = new BankAccount("Micky", 0.00);
System.out.println("Open Account " + micky);
System.out.println("Deposit $500.00 ");
}
}
I am stuck on doing the math for depositing, withdrawing and deleting the account. I believe I have the methods ready to perform such tasks but how do I output it?
You do indeed have all you need. When you want to withdraw $10 from micky, you just need micky.withdrawal(10.0), and the corresponding thing with deposits.
You have everything you need you just need to call toString() on the instansiated object.
public class BankAccountDriver {
public static void main(String[] args) {
BankAccount micky = new BankAccount("Micky", 0.00);
System.out.println("Open Account " + micky.toString());
micky.deposit(500.0);
System.out.println("Deposit $500.00 " + micky.toString());
}
}
A few things:
For your String.format, try String.Format("${0}", balance);
As mentioned, you need to just call your toString() method to have the "has a current balance.." part
You have the code to deposit and withdraw, but you don't call it. So you need to call it, like micky.deposit(500);. Then you can repeat your toString() call to print it out.
If your having a problem with the actual displaying, try Console.WriteLine() instead of System.out.println()
public class Account {
//===============Properties===================
protected double Balance;
protected String Owner;
protected double AcctNo;
//================Behaviors===============
public void setBalance(double bal) {Balance = bal;}
public double getBalance() {return Balance;}
public void setOwner(String own) {Owner = own;}
public String getOwner() {return Owner;}
public void setAcctNo(double an) {AcctNo = an;}
public double getAcctNo() {return AcctNo;}
//==============Constructors==============
public Account() {
super();
Balance=0;
Owner="";
AcctNo=0;
}
public Account(double bal, String own, double an) {
super();
Balance=bal;
Owner=own;
AcctNo=an;
}
public void deposit() {
0=deposit+Balance;
}
public void withdraw() {
0=withdraw-Balance;
}
public void display() {
System.out.println("Owner = " + getOwner());
System.out.println("Balance = " + getBalance());
System.out.println("Account Number = " + getAcctNo());
}
public static void main (String args []) {
Account a1;
a1 = new Account();
a1.setOwner("Frank");
a1.setBalance(1000);
a1.setAcctNo(2222);
a1.deposit(100.00);
a1.display();
}
}
The withdraw and deposit has became tricky for me, I thought that, that would be the answer to solving the problem, but it was not. I believe everything else is finished except for the those 2 problems
Can I be guided towards the way to understanding the Deposit and Withdrawal concept please? Thanks in advance!
Your problems are here:
0=deposit+Balance;
and here:
0=withdraw-Balance;
When assigning values to variables in Java, the right hand side is evaluated and the result is stored in the left hand side, e.g.
result = things+to+add;
But your code attempts to calculate something and store the result in 0, a literal number, this cannot work, it needs to be a variable.
Also, you functions to withdraw and deposit - how much are you withdrawing or depositing? The deposit function needs to know how much to deposit, and the withdraw function needs to know how much to withdraw. You need to add an argument to each to provide this information:
Here's a possible deposit function:
public void deposit(double amount) {
balance = balance+amount;
}
Heres some things you should all the time with your code
Use consistent formatting and make sure things are indented to the correct location. This means that if you have code inside brackets ({}) that they are are indented 1 and only 1 tab in (tab or 4 space w/e...).
in Java classes are always started with an uppercase and non static final variables are started with lower case variables, the variables Balance, Owner and AcctNo would be more suitable to start with lower case characters.
If you follow those formatting steps it will be much easier to debug and read your code.
Now your problem is your withdraw and deposit methods for 1 dont have a variable in the arguments. You need to add a variable to the method signature.
public void deposit(double ammount) {....
Then you have the statement 0=deposit+Balance; which I dont know what your intending to do there but it is invalid Java. It probably should read: Balance=Balance+deposit.