Hello! I have a simple bank program where I have to have an exception
prompt on screen when someone tries to make a negative withdrawal and
also when there are insufficient funds in the account to be withdrawn.
This is what I have so far. Its in 2 separate files on my comp. One is
the "driver" for the program whereas the other contains all the classes.
DRIVER PROGRAM CODE
$:.unshift File.dirname( __FILE__ )
require( "BankAccount" )
accounts = []
choice = -1
until choice == 5
puts "1. Open Account"
puts "2. View Accounts"
puts "3. Make Deposit"
puts "4. Make Withdraw"
puts "5. Exit"
print "Choose: "
choice = gets.to_i
case choice
when 1 ;
if accounts.size < 6
print "Under what name? "
name = gets.chomp
accounts.push( BankAccount.new name )
puts "Account created"
else
puts "You already have the maximum number of accounts"
end
when 2 ;
accounts.each_with_index {|a, i| puts "#{i}. #{a}"}
when 3 ;
begin
print "How much to deposit? "
amount = gets.to_i
accounts.each_with_index {|a, i| puts "#{i}. #{a}"}
print "Deposit to which account? "
accounts[ gets.to_i ].deposit( amount )
rescue NegativeAmountError => e
puts e
end
when 4 ;
begin
print "How much to withdraw? "
amount = gets.to_i
accounts.each_with_index {|a, i| puts "#{i}. #{a}"}
print "Withdraw from which account? "
accounts[ gets.to_i ].withdraw( amount )
rescue InsufficientFundsError => e
puts e
end
end
end
CLASSES PROGRAM CODE
class BankAccount
@@id = 0
def initialize( name )
@id = @@id = (@@id + 1)
@name = name
@balance = 0
end
def deposit( amount )
@balance += amount if amount > 0
end
def withdraw( amount )
@balance -= amount if amount > 0 and amount <= @balance
end
def to_s
"Account #@id - Balance: #@balance (#@name)"
end
end
class InsufficientFundsError
def initialize( id, balance, amount )
end
def to_s
"Account #{@id} has insufficient funds ($#{@balance}) to allow the
withdrawal of $#{amount}"
end
end
class NegativeAmountError
def initialize( action, id )
end
def to_s
"You cannot [action] a negative amount for account [id]"
end
end
Like I said above, the two operations that I want this program to do
are:
1) When making a withdrawal, it cant withdraw if there are insufficient
funds thus prompting the InsufficientFundsError
2) You cant make a negative withdrawal thus prompting the
NegativeAmountError
Any words of wisdom would be greatly appreciated!
on 2012-11-15 22:33
on 2012-11-15 22:39
Justin Gamble wrote in post #1084633: > Like I said above, the two operations that I want this program to do > are: > 1) When making a withdrawal, it cant withdraw if there are insufficient > funds thus prompting the InsufficientFundsError To raise an exception when the condition is met: raise InsufficientFundsError.new(1234,50,100) However you will need to change your class so that it is a subclass of an existing exception, e.g. class InsufficientFundsError < RuntimeError ... end and you will need to make the other corrections to these classes which should become obvious when you run and debug the program.
on 2012-11-15 22:45
What is the reason of doing the .new(...)in raise InsufficientFundsError.new (1234, 50, 100) ?
on 2012-11-16 03:23
Justin Gamble wrote in post #1084635: > What is the reason of doing the .new(...)in > > raise InsufficientFundsError.new (1234, 50, 100) > > ? class MyException < Exception def exception return ZeroDivisionError.new end end begin raise MyException rescue MyException => e p e end begin raise MyException.new rescue ZeroDivisionError => e p e end --output:-- #<MyException: MyException> #<ZeroDivisionError: ZeroDivisionError>
on 2012-11-16 08:43
Justin Gamble wrote in post #1084635: > What is the reason of doing the .new(...)in > > raise InsufficientFundsError.new (1234, 50, 100) > > ? If the exception doesn't take any arguments, you can just do raise InsufficientFundsError If the exception takes only one argument you can do raise InsufficientFundsError, 123 But if the initialize method takes multiple arguments, as far as I know you must create an instance of the exception class explicitly. (Tested with ruby 1.8)
on 2012-11-16 09:27
On Fri, Nov 16, 2012 at 1:43 AM, Brian Candler <lists@ruby-forum.com> wrote: > > Is this a good way to use exceptions? Having an insufficent balance might be something the user finds exceptional, but I'd think it would be a standard sort of handling for a bank applicaion...
on 2012-11-16 13:28
On Fri, Nov 16, 2012 at 9:27 AM, tamouse mailing lists <tamouse.lists@gmail.com> wrote: > Is this a good way to use exceptions? Having an insufficent balance > might be something the user finds exceptional, but I'd think it would > be a standard sort of handling for a bank applicaion... Depends on how you implement it: if you have the option to query the current balance and define that a withraw operation is only allowed with sufficient funds then you throw an exception. If OTOH you define that withdraw will return success then you have a boolean return value. Both approaches are legal and feasible. Kind regards robert
on 2012-11-16 19:16
Robert Klemme wrote in post #1084722: > On Fri, Nov 16, 2012 at 9:27 AM, tamouse mailing lists > <tamouse.lists@gmail.com> wrote: > >> Is this a good way to use exceptions? Having an insufficent balance >> might be something the user finds exceptional, but I'd think it would >> be a standard sort of handling for a bank applicaion... > > Depends on how you implement it: if you have the option to query the > current balance and define that a withraw operation is only allowed > with sufficient funds then you throw an exception. If OTOH you define > that withdraw will return success then you have a boolean return > value. Both approaches are legal and feasible. But I'd suggest *not* the current implementation: def withdraw( amount ) @balance -= amount if amount > 0 and amount <= @balance end There is no indication whether the withdrawal took place or not!
on 2012-11-17 04:47
On Fri, Nov 16, 2012 at 6:27 AM, Robert Klemme <shortcutter@googlemail.com> wrote: > On Fri, Nov 16, 2012 at 9:27 AM, tamouse mailing lists > <tamouse.lists@gmail.com> wrote: > >> Is this a good way to use exceptions? Having an insufficent balance >> might be something the user finds exceptional, but I'd think it would >> be a standard sort of handling for a bank applicaion... > > Depends on how you implement it: I know the right answer to every question is "It depends" :) > if you have the option to query the > current balance and define that a withraw operation is only allowed > with sufficient funds then you throw an exception. If OTOH you define > that withdraw will return success then you have a boolean return > value. Both approaches are legal and feasible. I guess I'm looking for some kind of guidance to generalize this. My understanding is that one only uses exceptions for things which the app finds exceptional, meaning things which it cannot or should not be dealing with, which in some cases may depend on the business practices and policies one is implementing. Recognizing this was a class assignment for the OP, the situation is highly contrived, and the lesson might be how to use and handle exceptions, so great. Do you (or anyone else who'd like to chime in!) have a ... set of heuristics, maybe? .. that help you know when it's appropriate and when it's not? The reason I ask is not merely academic. I see in code many places where it is raising and exception, and then the rescue code does something like this: rescue Exception # blah blah end In other words, they know an exception might be raised, but they trap evey possible exception, rather than a specific one. I'm worried (?) (thinking) that use of raising exceptions might be a little too easily decided upon as the answer, and if I should maybe push back on such implementations... > Kind regards > > robert > > -- > remember.guy do |as, often| as.you_can - without end > http://blog.rubybestpractices.com/ > Thanks, so much, Robert. :) And I am going to check out your web site right now.
on 2012-11-17 08:41
Subject: Re: Question on exceptions Date: Sat 17 Nov 12 12:47:06PM +0900 Quoting tamouse mailing lists (tamouse.lists@gmail.com): > # blah blah > > end > > In other words, they know an exception might be raised, but they trap > evey possible exception, rather than a specific one. I'm worried (?) > (thinking) that use of raising exceptions might be a little too easily > decided upon as the answer, and if I should maybe push back on such > implementations... I will try to describe in words the fuzzy logic I follow. I do not often use exceptions. In more than seven years of using Ruby, I have probably only subclassed Exception once or twice. I generally capture an exception (thrown by my code or by other libraries) like this: rescue => err print_a_message_on_the_log(err) maybe_print_backtrace(err.backtrace) maybe_correct_this_or_that_value maybe_break_out_of_a_loop_or_return end The Ruby interpreter's usage of exceptions is mostly limited to grave events. I try to mimic this usage in my raising practices. Basically, exceptions should only indicate situations that have to be debugged out: an exception should never indicate a special, but acceptable, case. I come from C (I still use C). Practice in C tends to be that your procedures return non-zero in case of errors - the non-zero value indicates the type of error. This can be cascaded: you check the return value, and return the same if negative, and so on. But it can certainly escalate to awkwardness, especially if at some level your function has to return other data. For me, the advantage of the exception mechanism is that it has its own, separate distribution channel. If you do not catch an exception, the program terminates, with VERY useful debugging data: the message of the exception, and the backtrace. When I encounter an exception, the catching option is not the preferred one. If at all possible, I try to modify the code so that the special case either does not happen, or is specifically handled, in a programmatic way. If I resort to a rescue clause like the one above, it means that I come to the (maybe temporary) conclusion that the special case can happen, but it is something to be monitored. My programs tend to have meaningful log files: disk space is cheap. Patterns of repetition of warning messages may give precious hints to solving complex bugs or refactoring non-optimized algorithms. If I catch an exception, I can do so at any of the steps of the backtrace. Often it is useful to make use of the exception-raising logic to get out of some of the levels of the call tree. This iterative process is part and parcel of the testing phase that all new code has to undergo. Every situation is different. If you really care about the healthy operation of your code, every choice should be tuned to the specific case. The process cannot be made automatic. Automatic coding/debugging results in mediocre programs. In all cases, I would not use the exception mechanism in the example that the original poster makes. Rather, the BankAccount#withdraw method would return true (or possibly the new balance of the account) if the withdraw method was successful, and false otherwise. Ruby has a neat syntax for catching false/nil values: unless(account.withdraw(sum)) ... The condition of insufficient funds is maybe undesirable from the point of view of the account holder, but it is a perfectly legitimate situation; all mechanisms that handle withdrawals should foresee and properly manage it. Withdrawals *can* fail. It should not be an exception-al event from the point of view of the code. This is just an effort to put into words my craft. Every other craftsman/woman will most probably follow different ways. Carlo
on 2012-11-17 15:12
On Sat, Nov 17, 2012 at 1:40 AM, Carlo E. Prelz <fluido@fluido.as> wrote: > In all cases, I would not use the exception mechanism in the example > that the original poster makes. Rather, the BankAccount#withdraw > method would return true (or possibly the new balance of the account) > if the withdraw method was successful, and false otherwise. Ruby has a > neat syntax for catching false/nil values: One of the things I *really* like about Ruby is that false is *not* the same as nil or zero. Thus returning false from a method is something the calling code can actually get a handle on, unlike in other languages (such as C, perl, etc) where false is overloading zero, which might be a legitimate return value. > This is just an effort to put into words my craft. Every other > craftsman/woman will most probably follow different ways. > Carlo Thanks, Carlo, that was an excellent desciription, and mirrors my thoughts as well.
on 2012-11-17 15:20
On Sat, Nov 17, 2012 at 4:47 AM, tamouse mailing lists <tamouse.lists@gmail.com> wrote: > > I know the right answer to every question is "It depends" :) Ah, I see you learned the most important lesson! ;-) >> if you have the option to query the >> current balance and define that a withraw operation is only allowed >> with sufficient funds then you throw an exception. If OTOH you define >> that withdraw will return success then you have a boolean return >> value. Both approaches are legal and feasible. > > I guess I'm looking for some kind of guidance to generalize this. > > My understanding is that one only uses exceptions for things which the > app finds exceptional, ... which is a kind of tautological statement. :-) > meaning things which it cannot or should not be > dealing with, which in some cases may depend on the business practices > and policies one is implementing. Yes. And this also depends on context and design philosophy. For example, in Java you will get an exception when accessing an array or an ArrayList with an illegal index. In Ruby you just get nil from Array if the index was out of range. This makes perfectly sense given that the normal iteration method in Ruby is via #each which ensures you only get to see elements in the Array and in Java you have to externally iterate with an index or Iterator instance. OTOH you will get an exception in Ruby if the type is not an integral type or the numeric value is too large. > Do you (or anyone else who'd like to chime in!) have a ... set of > heuristics, maybe? .. that help you know when it's appropriate and > when it's not? I can see these categories of errors where an exception seems appropriate to me: 1. programmer errors - invoking methods which are not there - invalid arguments passed to methods (type errors, range errors) - invalid arguments passed which would invalidate the class invariant - calling methods at the wrong time i.e. when the instance is not in the proper state for the method call (e.g. you must invoke #open on something before you can invoke #write, you need to obtain some form of lock before you are allowed to work with an instance) 2. exceptional conditions outside the program - technical IO errors (disk and network) - memory exhausted There are probably more but this is what comes to mind off the top of my head. > In other words, they know an exception might be raised, but they trap > evey possible exception, rather than a specific one. I'm worried (?) > (thinking) that use of raising exceptions might be a little too easily > decided upon as the answer, and if I should maybe push back on such > implementations... That's really hard to tell without more context. :-) > Thanks, so much, Robert. :) And I am going to check out your web site > right now. You're welcome! Kind regards robert
on 2012-11-17 16:00
On Sat, Nov 17, 2012 at 8:19 AM, Robert Klemme <shortcutter@googlemail.com> wrote: > Ah, I see you learned the most important lesson! ;-) I'm quick like that. :) >> My understanding is that one only uses exceptions for things which the >> app finds exceptional, > > ... which is a kind of tautological statement. :-) That was intended. :) But, seriously, it does bring up the question "why did (this developer) think (this situation) was exceptional to (this application)?" I don't think there can ever be a general answer to that question, which is why I asked for heuristics. > I can see these categories of errors where an exception seems appropriate to me: Great list. Thanks. > That's really hard to tell without more context. :-) Oh, yes, I know;; this is something I think I have to work out for this specific situation.
on 2012-11-17 16:35
On Sat, Nov 17, 2012 at 8:40 AM, Carlo E. Prelz <fluido@fluido.as> wrote: > Subject: Re: Question on exceptions > Date: Sat 17 Nov 12 12:47:06PM +0900 > > Quoting tamouse mailing lists (tamouse.lists@gmail.com): > >> Do you (or anyone else who'd like to chime in!) have a ... set of >> heuristics, maybe? .. that help you know when it's appropriate and >> when it's not? > I will try to describe in words the fuzzy logic I follow. > > I do not often use exceptions. In more than seven years of using Ruby, > I have probably only subclassed Exception once or twice. Well, there are other uses of exceptions than defining your own. :-) One could even go as far as to say that you use exceptions if you use code that throws exceptions - even if there is no trace of catching exceptions in your code. That makes sense from a certain perspective because you rely on the fact that the code you use will raise exceptions if errors surface. > The Ruby interpreter's usage of exceptions is mostly limited to grave > events. What exactly is a "grave event"? > I try to mimic this usage in my raising practices. Basically, > exceptions should only indicate situations that have to be debugged > out: an exception should never indicate a special, but acceptable, > case. I beg to disagree. An IO error which surfaces is usually not something you will be tackling by debugging. You might be the invoker of a script who learns that way the the file name you passed on the command line was wrong. There may also be an network error because your WLAN lost connection spuriously etc. > I come from C (I still use C). Practice in C tends to be that your > procedures return non-zero in case of errors - the non-zero value > indicates the type of error. This can be cascaded: you check the > return value, and return the same if negative, and so on. But it can > certainly escalate to awkwardness, especially if at some level your > function has to return other data. What you describe neatly shows the major disadvantages of error handling via return type checking: - It is cumbersome (you need to check a lot return values). - It has to be done manually (i.e. you can forget it). - Propagation to invokers is tedious. - The error handling code is intermixed with regular code which makes the code harder to read. Of course, in C you do not have much choices since there are no exceptions in the language. > For me, the advantage of the exception mechanism is that it has its > own, separate distribution channel. If you do not catch an exception, > the program terminates, with VERY useful debugging data: the message > of the exception, and the backtrace. Absolutely. > When I encounter an exception, the catching option is not the > preferred one. If at all possible, I try to modify the code so that > the special case either does not happen, or is specifically handled, > in a programmatic way. Often you cannot do that. For example in the IO error case mentioned above the only thing you could do would be check the file for existence and readability before opening it. But since other processes have access to the file system as well that still does not give you 100% safety that the file is still there when you are going to open it. So just opening it and handling the exception (or just propagating it up the call stack) is the simpler option because the exception may come anyway. You end up with less code and a clearer structure. And in a way you avoid redundancy (duplicate checks for existence and accessibility). > If I catch an exception, I can do so at any of the steps of the > backtrace. Often it is useful to make use of the exception-raising > logic to get out of some of the levels of the call tree. Or put it differently: often it makes sense to handle an exception remote from to where it originated. > This iterative process is part and parcel of the testing phase that > all new code has to undergo. Every situation is different. If you > really care about the healthy operation of your code, every choice > should be tuned to the specific case. The process cannot be made > automatic. Automatic coding/debugging results in mediocre programs. Right, and if it was possible at all, we would be without jobs. :-) > In all cases, I would not use the exception mechanism in the example > that the original poster makes. Rather, the BankAccount#withdraw > method would return true (or possibly the new balance of the account) > if the withdraw method was successful, and false otherwise. Ruby has a > neat syntax for catching false/nil values: > > unless(account.withdraw(sum)) > ... # alternative account.withdraw(sum) or do_something_else > The condition of insufficient funds is maybe undesirable from the > point of view of the account holder, but it is a perfectly legitimate > situation; all mechanisms that handle withdrawals should foresee and > properly manage it. Withdrawals *can* fail. It should not be an > exception-al event from the point of view of the code. Unless for example the program cannot verify itself that funds are sufficient. It is not a too uncommon scenario to rely on database functionality (constraints) to ensure a negative balance is not reached. In this case the RDBMS driver's exception is necessary to learn of the violation because even if you do the check beforehand there can still be a transaction modifying the balance between the check and the withdraw (depending on concurrency model of the RDBMS of course, in Oracle and Postgres, where reads are not blocked by writes, you would either need explicit locking or work with the exception). > This is just an effort to put into words my craft. Every other > craftsman/woman will most probably follow different ways. I think your stress of context is very important. The downside is that it takes some time and experience to feel comfortable in all situations to decide on the proper way. Kind regards robert
on 2012-11-17 16:49
Subject: Re: Question on exceptions
Date: Sun 18 Nov 12 12:35:06AM +0900
Thanks for your many words!
Quoting Robert Klemme (shortcutter@googlemail.com):
> I beg to disagree.
You are hereby granted a universal, perennial, transferrable,
irrevocable concession to do so 8-)
Carlo
on 2012-11-17 17:15
On Sat, Nov 17, 2012 at 4:48 PM, Carlo E. Prelz <fluido@fluido.as> wrote: > Subject: Re: Question on exceptions > Date: Sun 18 Nov 12 12:35:06AM +0900 > > Thanks for your many words! You're welcome! > Quoting Robert Klemme (shortcutter@googlemail.com): > >> I beg to disagree. > > You are hereby granted a universal, perennial, transferrable, > irrevocable concession to do so 8-) Wow! And: LOL Have a nice weekend! robert
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.