0

Working on a bank program. I need to make sure that users (person objects) have their own bank accounts that they can create with different balances in each account that they can interact with. They should be able to deposit, withdraw, transfer money, etc. I'm having trouble outputting correctly.

As you will see, when it says how much money each person has in their account which is being called, it's displaying the total from all accounts created by that user. Any ideas on how I can fix this?

Also, I need a little guidance on the if statement in my transfer method. I can't get it to display correctly for each scenario.

class Bank

  attr_accessor :balance, :withdrawal, :deposit, :transfer, :users
  @@accounts = {}
  #@@balance = {}
  def initialize(bname)
    @bname = bname
    @users = {}
    @@accounts[users] = bname
    #@@balance[users] = bname
    puts "#{@bname} bank was just created."
  end

  def open_account(name, bname, balance = 0)
    if @users.include?(name)
      bname.each do |users|
        @@accounts.push('users')
      end
    end
    @balance = balance
    puts "#{name}, thanks for opening an account at #{@bname} with an initial $#{balance} deposit!"
  end

  def user
    @users
  end

  def withdrawal(name, amount)
    @balance -= amount
    puts "#{name} withdrew $#{amount} from #{@bname}.  #{name} has #{@balance}.  #{name}'s account has #{@balance}."
  end

  def deposit(name, amount)
    @balance += amount
    puts "#{name} deposited $#{amount} to #{@bname}.  #{name} has #{@balance}.  #{name}'s account has #{@balance}."
  end

  def transfer(name, account2, amount)
    if name == name
      @balance -= amount
      @transfer = amount
      puts "#{name} transfered $#{amount} from #{@bname} account to #{account2} account.  The #{@bname} account has $#{amount} and the #{account2} account has $#{@balance}."
    else
      puts "That account doesn't exist."
    end
  end
end

class Person

  attr_accessor :name, :cash
  def initialize(name, cash = 100)
    @name = name
    @cash = cash
    puts "Hi, #{name}.  You have $#{cash} on hand!"
  end
end

chase = Bank.new("JP Morgan Chase")
wells_fargo = Bank.new("Wells Fargo")
randy = Person.new("Randy", 1000)
kristen = Person.new("Kristen", 5000)
justin = Person.new("Justin", 1500)
chase.open_account('Randy', "JP Morgan Chase", 200)
chase.open_account('Kristen', "JP Morgan Chase", 300)
chase.open_account('Justin', "JP Morgan Chase", 400)
wells_fargo.open_account('Randy', "Wells Fargo", 200)
wells_fargo.open_account('Kristen', "Wells Fargo", 300)
chase.deposit("Randy", 200)
chase.deposit("Kristen", 350)
chase.withdrawal("Kristen", 500)
chase.transfer("Randy", "Wells fargo", 100)
chase.deposit("Randy", 150)

The current output for this code is:

JP Morgan Chase bank was just created.
Wells Fargo bank was just created.
Hi, Randy.  You have $1000 on hand!
Hi, Kristen.  You have $5000 on hand!
Hi, Justin.  You have $1500 on hand!
Randy, thanks for opening an account at JP Morgan Chase with an initial $200 deposit!
Kristen, thanks for opening an account at JP Morgan Chase with an initial $300 deposit!
Justin, thanks for opening an account at JP Morgan Chase with an initial $400 deposit!
Randy, thanks for opening an account at Wells Fargo with an initial $200 deposit!
Kristen, thanks for opening an account at Wells Fargo with an initial $300 deposit!
Randy deposited $200 to JP Morgan Chase.  Randy has 600.  Randy's account has 600.
Kristen deposited $350 to JP Morgan Chase.  Kristen has 950.  Kristen's account has 950.
Kristen withdrew $500 from JP Morgan Chase.  Kristen has 450.  Kristen's account has 450.
Randy transfered $100 from JP Morgan Chase account to Wells fargo account.  The JP Morgan Chase account has $100 and the Wells fargo account has $350.
Randy deposited $150 to JP Morgan Chase.  Randy has 500.  Randy's account has 500.
Randy D
  • 13
  • 5

1 Answers1

0

I think some of the issues may be down to a misunderstanding of Object Oriented Design, rather than specifically of Ruby. So, let's specify the Bank:

Bank

  • open(name, balance) Open an account with the name and balance given. Return the account to deposit, withdraw and transfer from.
  • deposit(amount) credits the balance of the bank.
  • withdraw(amount) debits the balance of the bank (if they have enough money).

And then from that we see that we also need an Account class, but, we'll be creating instances of it from inside Bank#open and we don't want people to be able to create instances any other way, so we'll hide this class inside Bank:

Bank::Account

  • deposit(amount) credits the balance of the account and the bank as well.
  • withdraw(amount) debits the balance of the account and the user (if they have enough money).
  • transfer(amount, account) transfers from one account to the other, if the required funds are available.

Now we notice that Bank and Bank::Account are sharing some methods, namely deposit, and withdraw, and additionally, some state, (the balance, and the name of the entity e.g. "JP Morgan Chase" for a bank or "Joe Bloggs" for a person). So let's abstract that out into its own class, (I'll call it BalanceHolder but it doesn't matter so much, as long as it makes sense).

Putting it together, we get this:

class BalanceHolder
  attr_reader :name, :balance
  def initialize(name, balance)
    @name    = name
    @balance = balance
  end

  def deposit(amount)
    @balance += amount
  end

  def withdraw(amount)
    raise ArgumentError, "#{@name} has insufficient funds." if @balance < amount
    @balance -= amount
  end
end

class Bank < BalanceHolder
  def open(name, balance)
    deposit(balance)
    Bank::Account.new(self, name, balance)
  end

  private
  class Account < BalanceHolder
    def initialize(bank, name, balance)
      @bank = bank
      super(name, balance)
    end

    def deposit(amount)
      super
      @bank.deposit(amount)
    end

    def withdraw(amount)
      super
      @bank.withdraw(amount)
    end

    def transfer(amount, account)
      withdraw(amount)
      account.deposit(amount)
    end
  end
end

chase       = Bank.new("JP Morgan Chase", 0)
wells_fargo = Bank.new("Wells Fargo", 0)

randy_chase = chase.open("Randy", 200)
randy_wells = wells_fargo.open("Randy", 200)

randy_chase.deposit(200)
randy_chase.transfer(100, randy_wells)

Perhaps the most important difference between what I've written and what you have written is something called Separation of Concerns (A similar idea is Single Responsibility Principle, or SRP). What this is referring to is that, in my interface, if you want to deposit money into an account, you call a method on the account, (rather than call a method on the bank, giving the account name as a parameter), and, to aid this, each account is aware of which bank it belongs to (because that is part of the state of a particular account).

Also worth noting is that there is nothing that a bank can do to an account after it is opened, so there is no point in the bank being aware of all the accounts it has associated with it.

One thing you have in your model which I have not incorporated into mine is the idea that one person can have multiple accounts at different banks. If I were to do this, there are several ways I could go about it. All of them involve creating yet another class called Person, which deals with the balance and name for a particular person.

  • One way is to make Person a subclass of BalanceHolder and have the Account update its associated person's balance with each transaction.
  • Alternatively, I could have the Person class keep track of all of its accounts, and when requesting a balance, it simply sums the balances of all of its accounts.

I would probably opt for the later because it reduces coupling, but this is a design decision to be made when the featureset of the program requires it.

amnn
  • 3,657
  • 17
  • 23
  • Thanks for your time asQuirrel! My directions for the task are kind of open ended and in the course we really haven't dealt with name spaces up to this point, although I have read about them and done a few exercises with them. You solution makes total sense and I'll rework my code to have a solid working program based on your suggestions. I figured mine looked a bit too "busy" and could definitely be cleaner, which yours is! I'll definitely need to be able to allow the person to make accounts at another bank so I'll start with your recommendations. Thanks! – Randy D Jun 01 '14 at 23:54
  • One more thing, in my instructions I was told to use 2 classes to create the program, which is why I tried to keep everything inside of bank and person classes. Keeping that in mind, is it possible to arrive at the same result given the solution you have provided or would you suggest I adjust my original code to try and behave more like what you supplied? – Randy D Jun 02 '14 at 00:22
  • Personally, I think it's silly to enforce a strict limit on the number of classes you can use. If this was the problem I was given, the solution I gave is the one I would use, and it contains 4 classes. If you're trying to follow instructions for a class however, then I would suggest you do what they say, using my answer as a basis. The key idea really, is that if you want to do something to the state of a Person, you have to do it by calling a method on Person, not on a Bank. – amnn Jun 02 '14 at 10:49