• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • paul wheaton
  • Liutauras Vilda
  • Ron McLeod
Sheriffs:
  • Jeanne Boyarsky
  • Devaka Cooray
  • Paul Clapham
Saloon Keepers:
  • Scott Selikoff
  • Tim Holloway
  • Piet Souris
  • Mikalai Zaikin
  • Frits Walraven
Bartenders:
  • Stephan van Hulst
  • Carey Brown

Bank Account Problem

 
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
A popular choice for examples of Java/OOP coding is a bank account. This topic has been discussed here before. I'd like to address the question of how (or if) one should break out the operations one conducts on a bank account into multiple classes.

I found case study online about it. The author suggests three classes: BankAccount (to hold, get, and set the current balance, with locking); TellerAccountAccessor (to pay bills, withdraw money, and make deposts); and BankAccountAccessor (to deduct bank fees and add accrued interest). In his example, however, the only "state" maintained by the latter two classes is a reference to the BankAcount object, which has me wondering if those two classes shouldn't be inner classes of the BankAccount and (here it comes) why they ought to be distinct classes at all. They really just seem to be functions operating on an account, to me.

The author entertained a number of comments, incorporating some of them as suggestions, until his code looked like this:


This is an example of where I am having a hard time understanding why objects help. The author emphasizes the importance of thread-safety. Fair enough, but after he posted the above version, some commenters began suggesting he had rather overcomplicated things. No one posted this, but the comments revolved around the idea that he was really doing little more than this:


My procedural-programmer's mind has me thinking that last bit supports as much functionality as the rest, and is quite a bit simpler. The author, however, defends his OOP approach with the usual points (modularity, isolation of function, and so on). In those cases where the other classes besides BankAccount retain some state, I might see his point. But, my question is, what advantages accrue to the use of additional classes that retain, as their only internal state, a reference to an object of a different class when those additional classes will only perform their method's operations in ways that affect that object? How/why is that any better than each of those methods simply being methods of that object in the first place?

Thanks in advance.
 
Marshal
Posts: 80123
416
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
A teller can access many accounts, so I think it does merit a separate existence as a class in its own right.
 
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:My procedural-programmer's mind has me thinking that last bit supports as much functionality as the rest, and is quite a bit simpler. The author, however, defends his OOP approach with the usual points (modularity, isolation of function, and so on). In those cases where the other classes besides BankAccount retain some state, I might see his point. But, my question is, what advantages accrue to the use of additional classes that retain, as their only internal state, a reference to an object of a different class when those additional classes will only perform their method's operations in ways that affect that object? How/why is that any better than each of those methods simply being methods of that object in the first place?


Well first off, I would say that a simple class doesn't support any notion of subtyping of accounts (eg, Savings, Investment, Current etc.); and secondly IMO your class supports the model, rather than the actuality, of an Account, which of necessity involves not only transactions, but their persistence (models are generally only interested in current state, not history).

I do reckon that he's missed an important transaction though: a transfer. Now you could argue that it's simply a withdrawal from one account accompanied by a deposit to another, but it must be atomic, and it may also have associated charges or rules, so I'd certainly have one.

My "first cut".

Winston
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:A teller can access many accounts, so I think it does merit a separate existence as a class in its own right.


Without wanting to create a PP/OOP argument, please let me pursue that a bit. If a Teller object retained a reference to an account, I suppose that object could be passed to methods expecting a Teller, without those methods having to know (or have access to) the account referenced by that Teller. But wouldn't that be done just as well by creating a Teller interface to the BankAccount object?

If a Teller object might be used to access more than one account, seems that one would have to add a setAccount method to the Teller, which isn't all that different (is it?) from passing the account reference to the Teller's methods (now, possibly static, since the Teller has no other state), in which case I am still thinking the Teller's methods could as sensibly be methods of the BankAccount.

Now, if an object held a reference to more than one BankAccount, or maintained some other state, I can see why it might be useful to isolate it and its methods from the BankAccount. For example, if a Teller object were associated with, say, a specific person, and that person's object retained the total cashflows in and out of that Teller's account, we might want to be able to examine those totals (maybe a Teller with low daily cashflows is goofing off, while one with high cashflows is doing something calling for examination).

Thus, in the specific example I cited, I am wondering if an object that maintains, as its only state, a reference to another object wouldn't better be implemented as an interface to that referenced object, while, at the same time, wondering if the "signal" that a new object is called for is the need for state that is not part of the (in this case) BankAccount. At the same time, I am cognizant of the many advisors who warn against objects that maintain too much state, which, of course, is the point at which my head begins to revolve at a substantial rpm.

To try to focus here, do you think either or both these notions makes any kind of sense?

1. Objects whose only state is a reference to another object should be interfaces to that object.
2. The need to preserve state that is unnecessary to the existence of a particular object signals the need to create a new object.

Or am I just talking trash again?
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Winston Gutkowski wrote:Well first off, I would say that a simple class doesn't support any notion of subtyping of accounts (eg, Savings, Investment, Current etc.); and secondly IMO your class supports the model, rather than the actuality, of an Account, which of necessity involves not only transactions, but their persistence (models are generally only interested in current state, not history).


Agreed, and I think his expanded version begins to consider this. That's a helpful observation since it suggests that what the author calls a "case study" kind of misses its own point. That is, by being oversimplified, the real justifications for creating separate Teller (and other) objects are lost. Yes, a transaction history (perhaps on a per-Teller basis, for reasons I outlined in my previous comment here), might be useful. A BankAccount object that merely holds, and allows for changes in, a balance, doesn't show the need for that. One could, however, imagine a Transaction object that would be a collection of cashflows. Those might be created on-the-fly, from a master collection, meeting certain criteria (for example, all the cashflows for a single year). That's another case where methods expecting access to transaction histories might be satisfied with an interface on the BankAccount object, but it seems kind of silly to recompute a collection for each access to one cashflow. That is, if a method needed cashflows and the caller wanted to supply only those for the year 2012, or only those over US$10,000 (yeah, my bank account has a lot of those), the sensible way would be to create that collection once and then pass it to the method that needs its. Couldn't really do that with an interface alone (worse, from one access to the next, the contents of the collection might change, since--and I am sure you all have this problem--US$10,000 cashflows are pretty common).

I do reckon that he's missed an important transaction though: a transfer. Now you could argue that it's simply a withdrawal from one account accompanied by a deposit to another, but it must be atomic, and it may also have associated charges or rules, so I'd certainly have one.


Ah, nice! Transfers are between two different accounts. Question: is it better to have a transfer implemented as a class, or as a method of BankAccount (as in myAccount.transferTo(yourAccount)? How does one decide?


My "first cut".

Winston


And a good one. I do appreciate my idiot C/C++ programmer's questions being taken seriously here. Thanks.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:One could, however, imagine a Transaction object that would be a collection of cashflows...


Yes, but to my mind that would probably be a subtype. In the real world, you may have other transactions like change of name/address that have their own foibles.
For me, a 'bank transaction' is very similar to a database transaction - ie, a "unit of work" - but geared to the sorts of things a bank needs to do; and probably also governed by all sorts of rules, including imposed ones, like how long you have to keep records for.

It's also worth remembering that, in ledger terms, all 'value' transactions are actually transfers; however, that's about the extent of my knowledge when it comes to DEB systems.

Winston
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Winston Gutkowski wrote:

Stevens Miller wrote:One could, however, imagine a Transaction object that would be a collection of cashflows...


Yes, but to my mind that would probably be a subtype. In the real world, you may have other transactions like change of name/address that have their own foibles.
For me, a 'bank transaction' is very similar to a database transaction - ie, a "unit of work" - but geared to the sorts of things a bank needs to do; and probably also governed by all sorts of rules, including imposed ones, like how long you have to keep records for.

It's also worth remembering that, in ledger terms, all 'value' transactions are actually transfers; however, that's about the extent of my knowledge when it comes to DEB systems.

Winston


All fair and valid points (at least, to the limits of my own knowledge of DEB, which was mostly acquired by learning how to use QuickBooks). But, let's drill down to an OOP question: when it comes to moving money from one account to another, how would one choose between, say, an object called Transferor, and a method called transferTo? For example, ignoring(!) synchronization for a moment, a Transferor object might look like this:


Is there any advantage to that over putting the transfer code directly into the account object?



Returning to the relevance of state, it seems to me that no state must pre-exist for the transfer, and none must be retained after it is complete. Is that part of the key to knowing when one needs a new object?
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:Is there any advantage to that over putting the transfer code directly into the account object?


I think so, because a Transaction is something that has a life of its own outside the Account.

To me, an Account is a glorified sack of money: you can put money in; you can take money out (if you have it). There may be a few extra bells and whistles like overdrrafts, charges and interest; but at the end of the day it's a sack of money.

A Transaction, on the other hand, is much closer to a business process; and is likely to have rules (charges, history retention, etc.) that have to do with what's being done rather than relating to a specific Account. That doesn't mean to say that an Account doesn't have modifiers though, just like your example above, and these would almost certainly be used by a Transaction.

I'd also say that a Transaction (particularly a 'value' transaction) has to be atomic and reversible. It might even, like a db transaction, need commit() and rollback() options.

Winston
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Winston Gutkowski wrote:A Transaction, on the other hand, is much closer to a business process; and is likely to have rules (charges, history retention, etc.) that have to do with what's being done rather than relating to a specific Account.



Ah, brilliant! When you create a Transaction (or a Transferor), some of its state might be unique to that transaction, and nothing that a BankAccount would know or need to retain. For example, there might be a limit to the size of a transfer, there might be a particular signal it has to send to some other object or process (InlandRevenue or InternalRevenueService). This reminds me of a discussion we had in another thread here, recently, about sorting. One might create a variety of Sorter objects, each of which works different ways, even though what it does is "sort" things. Its methods wouldn't be passed anything much other than items to compare, but you might create it such that its comparisons are done in a particular way, and, as long as that object lives, you want that to be the same way everytime it is used. Similarly, you might have an object that can transfer money between accounts, but (depending upon how it is initialized or has its state set) might apply various limits and/or generate various side-effects each time it is asked to move funds.

I was talking to my wife about this (she codes, but is not a life-time professional, which actually gives her a refreshingly uncomplicated view of things, at times). She noted that I seem to name my objects with nouns, my interfaces with adjectives, but that this topic (objects that do things to other objects, more than they are for the purpose of storing data themselves), seem to get names that are agent nouns. That is, a BankAccount is a sack of money. Someone must take the intiative to move money from one BankAccount to another. If the BankAccount is capable of moving its own money, it might implement a Transferable interface. But, if not, someone (or something) must move that money instead. Hence, if an object will tranfer (a verb) money, that object might be called a Transferor (an agent noun).

Perhaps that's overanalyzing things, but I feel like there's some insight in that observation.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:...but that this topic (objects that do things to other objects, more than they are for the purpose of storing data themselves), seem to get names that are agent nouns. That is, a BankAccount is a sack of money. Someone must take the intiative to move money from one BankAccount to another. If the BankAccount is capable of moving its own money, it might implement a Transferable interface. But, if not, someone (or something) must move that money instead. Hence, if an object will tranfer (a verb) money, that object might be called a Transferor (an agent noun).


Or, perhaps, a Teller?

Winston
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Sure, but as that class was first implemented in the online example, it added only complexity with no functionality. The only advantages to breaking out the functionality of withdrawal/deposit from the BankAccount class to the Teller class seemed pretty theoretical (it was more modular; it kept the objects smaller), but (as the critics there pointed out) it actually added some unnecessary complexity by requiring Teller objects to hold a reference to BankAccount objects, thus adding the need for some state maintenance that wasn't necessary for the overall functionality of the application.

Here, you are pointing out some potential practical gains from that distribution. For example, we've considered different Transferor objects imposing different limits on tranfers (I like the Transferor notion much more than the Teller notion, because it ineherently implicates two BankAccount objects, which gets us away from situations where a single BankAccount object and its methods would appear to be able to cover all of our needs). I am thinking that might truly gain from isolating the transfer methods into their own class, because that class could centralize the synchronization I've ignored (until now), and allow subclasses to handle the business of imposing limits. Again, let's treat the BankAccount object just as you suggest: a sack of money. It could be this simple:


Now, let's say we want to be able to have synchronized transfers between two accounts. We could start with this:



Now, by itself, this doesn't seem to be an improvement over a method internal to BankAccount that could do transfers ("fromAcount.transfer(toAccount)" seems pretty obvious, though you'd have to pay extra attention to synchronizing the toAccount's receipt since it might be getting money from more than one account in more than one thread). But(!), suppose we want to allow for limitations and we want those to be flexible. For example, the Transferor above does nothing to prevent overdrafts (or overdraughts, depending on your location). Let's say we were disallow those. The Transferor class could now become this:


Now we have imposed some "business logic" without embedding it in the "sack of money" BankAccount. Still... so what? We could put this into the BankAccount object, and again save ourselves some complexity. But, that's only because the transfer method imposes a fixed restriction (it doesn't allow negative balances in BankAccount objects). That's not always going to be what we want. Some accounts will allow for negative balances, while some won't. Let's add that as an external check, by querying the BankAccount object before the transfer:


This really is more complicated and adds no functionality, but I think there is a great benefit in this approach: the Transferor class can be subclassed in a way that imposes its own restrictions by overriding the authorzedToWithdraw method:


This would allow any kind of restriction (or not) on withdrawals, without the Transferor having to include that code (nor the BankAccount), yet callers to Transferor object methods would really be calling the overridden authorizedToWithdraw method (actually, they'd be calling the original Transferor object's transfer method, and that would be calling the overridden authorizeToWithdraw method).

Now, this example has a lot of problems, I can see. It doesn't enforce positive transfer amounts (nor, alternatively, test for withdrawals when the amount is negative). The override approach imposes restrictions on BankAccount objects in pairs, which probably isn't how real banking works. But, it does preserve the simplicity of the BankAccount class as a "sack of money," while allowing subclassing of the Transferor class to impose limitations without leaking the BankAccount API (I haven't paid much attention to access keywords here, but I am assuming appropriate usages of those would provide public access to the Transferor object without granting public access to methods of the BankAccount class that could change its state). It also synchronizes transfer requests so nothing inside the BankAccount objects can change without the transfer method knowing about it.

So, fellows, am I on the right track here? Does this look like OOP, or am I just bein' another Brooklyn rube and fakin' it? Campbell? Winston?
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:So, fellows, am I on the right track here? Does this look like OOP, or am I just bein' another Brooklyn rube and fakin' it? Campbell? Winston?


I think perhaps you're overthinking it and, as you well know, the best thing to do in those cases is to StopCoding.

It seems to me that you're trying to define these things in Java terms rather than really analysing what they do. Sure, an Account is a sack of money (or possibly a box is a better analogy; especially one with a key), and if there was no such thing as government, you could possibly imagine a bank as a bunch of strongboxes in a secure vault with owners who simply come with their key, sign in, and withdraw or deposit money from their box as they see fit.

Unfortunately, that's not the case. For a start, banks are required to keep records, and a Transaction is that record ('fraid I don't like Transferor too much - it seems like one of those 'made up' words that don't have much meaning outside your bank). Secondly, while you, as an 'owner', may own the money in your box, you don't own the box itself; the bank does. The bank also provides security for each owner; otherwise, why wouldn't they just keep their money under their mattress?

Finally - and this where the 'box' or 'sack' analogy breaks down a bit - a bank isn't simply a bunch of boxes or sacks (although, as a single account holder, it may seem that way to you): it's an enormous pool of money that is given to the bank on regulated terms to use any way they like (usually to make more money).

I'd say that the "actor", in this case, is actually the Teller, who is the Bank's proxy for the Account holder, or Owner. Their sole job is to see that procedures are followed, and that the real actor (the Owner) is actually the one who requested a specific Transaction to take place.
Before the days of ATMs (and especially back in the bad old days before interstate banking in the US), these procedures could involve promissory notes, telephone calls, or even bonds to make sure that transactions were valid and secure - especially if the Owner couldn't go to their own branch - but, at the end of the day, the Transaction is simply the record - or if you prefer, the authorization - for a change to occur.

Now, in Banking terms that might mean that a Transaction has to have an actor, but not that it is the actor.

My 2 cents.

Winston
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Winston Gutkowski wrote:I think perhaps you're overthinking it...

Well, there are worse things to be accused of, I suppose.

and, as you well know, the best thing to do in those cases is to StopCoding.

Good reminder. I mulled this over for the last few days, avoiding the keyboard, sketching stuff out on paper instead.

For a start, banks are required to keep records, and a Transaction is that record ('fraid I don't like Transferor too much - it seems like one of those 'made up' words that don't have much meaning outside your bank). Secondly, while you, as an 'owner', may own the money in your box, you don't own the box itself; the bank does. The bank also provides security for each owner; otherwise, why wouldn't they just keep their money under their mattress?

Heh. I'm a lawyer, so words like "Transferor" slip into my code sometimes. It's a legitimate word, but I agree it's a bit awkward. My purpose in using it was not so much to propose it as the ideal choice, but rather to try out my "agent noun" idea for classes that exist more to provide their methods than to store data in their instances.

I'd say that the "actor", in this case, is actually the Teller, who is the Bank's proxy for the Account holder, or Owner. Their sole job is to see that procedures are followed, and that the real actor (the Owner) is actually the one who requested a specific Transaction to take place.
Before the days of ATMs (and especially back in the bad old days before interstate banking in the US), these procedures could involve promissory notes, telephone calls, or even bonds to make sure that transactions were valid and secure - especially if the Owner couldn't go to their own branch - but, at the end of the day, the Transaction is simply the record - or if you prefer, the authorization - for a change to occur.

Now, in Banking terms that might mean that a Transaction has to have an actor, but not that it is the actor.

Okay, I think I am getting somewhere with this idea of a Transaction (which certainly includes data dinstinct from what would be in a BankAccount), and an actor. In my attempts above, my Transferor was the actor. So let me come full-circle to a more abstract question (and not write any code). What I am wondering is if the original case study made more sense than I thought it did, by locating certain methods in a class that only operated on another class. That is, the only instance variable in the author's TellerAccountAccessor (what maybe we would really just call a "Teller" here), was a reference to a BankAcount. My puzzlement is/was over why that's any better than just locating the TellerAccountAccessor's methods in the BankAccount class in the first place.

Now, holding a reference to another object just so you can call its methods appears to be an accepted practice. For example, Horstmann & Cornell explicitly suggest this as a way to put ActionListener implementations into their own classes (by passing them a reference to the object they must manage in their constructors; see p. 328, Core Java, Volume I, 8th ed). But they also say it is more convenient just to make such implementing classes into inner classes of those objects, and skip the part about passing them a reference.

Thus, I am kind of baffled about all this. Is passing a reference in a constructor call just a way to help move code out of the referenced object? If so, is that a commonly accepted practice? That is, is it good practice to create a class solely for the purpose of providing a tidy place to store methods, when those methods will not operate on any instance variables of that class, but, instead, operate on another object entirely? That seems like a dodge, to me, serving the same function that libraries of functions perform in procedural languages.

I know that's kind of a Java-specific question (and you advised me to back away from language issues for now), but that's where I'm at. That is, suppose I have an object that, say, stores an image (a raster full of pixels, I mean). Now suppose I am writing dozens, maybe hundreds, of short methods that process that picture in some way. Maybe one of them inverts it to a negative of itself. Another reflects it like in a mirror. One more does edge-detection, while another blurs it, and so on and on. None of those short methods needs any state of its own. All of them will need to access the pixel data to process the image. In my old procedural mind-set, I'd just create a big library full of functions each of which is passed a pointer to the pixel data. In OOP, is the right thing to do to create a class for each of those methods, and pass them a reference to the object with the image data? It almost seems like those methods all ought to be static methods of one big class, but I am given to understand that such a technique is the hallmark of the procedural programmer who is trying to resist, rather than embrace, OOP.

I'm really kind of stuck here, conceptually.

What's the right way to add methods that operate solely on an object, without retaining any state of their own, without that object's class becoming huge?

My 2 cents.

Winston

It's worth more than that to me. Thanks.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:That is, suppose I have an object that, say, stores an image (a raster full of pixels, I mean). Now suppose I am writing dozens, maybe hundreds, of short methods that process that picture in some way. Maybe one of them inverts it to a negative of itself. Another reflects it like in a mirror...


I think you're confusing the Image with Photoshop.
One is a piece if data, which could simply be viewed as a sequence of bytes (which is almost certainly what you'd do if you just wanted to write it to a File); the other is an application that works on an Image (or, more likely, on its Bitmap).
An Image is likely to have a compression protocol though (eg, JPEG), which allows you to convert it to and from a Bitmap; so an Image probably HAS a Protocol, but the Protocol is not the Image.

All the other stuff you mentioned strikes me as part of your Photoshop class (or classes); and considering the multitude of filters available for image manipulation (there's at least a dozen for resizing alone), yes, it's quite possible that you will have classes that contain piles of methods. Or, more likely, they'll be part of some image manipulation library - don't re-invent the wheel.

The same is true of your Bank Account: it's a box of money, and it may have basic methods to update its own contents; but something else has to initiate the modification, be it a Transaction, Owner or Teller; otherwise anyone could do anything they liked to anybody's Account - in which case it's mattress time for me.

This is one of the reasons why it's so important with OO to describe what you want to do (NOTE: not how you're going to do it). A good requirements description will often give you a first cut as to what classes you're likely to need. I wrote this page a while back to try to explain some of the reasons.

Winston
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Winston Gutkowski wrote:

Stevens Miller wrote:That is, suppose I have an object that, say, stores an image (a raster full of pixels, I mean). Now suppose I am writing dozens, maybe hundreds, of short methods that process that picture in some way. Maybe one of them inverts it to a negative of itself. Another reflects it like in a mirror...


I think you're confusing the Image with Photoshop.

No, no! That's my point: the Image is the thing to be processed, but the processor is a big set of methods. Whether one puts them in the Image object (which, I grant, would be distasteful) or the Photoshop object, it seems they do all belong together. I keep reading that, if one's class is over 200 (or 500, or 1000; take your pick) lines long, then one's class is too long. Granted, these are always qualified as "guidelines," but the people offering them tend to go on from there as though they'd never said that, and act like any class over 200/500/1000 lines has just got to be badly designed. I'm working on an image-processing application that just doesn't have that much in the way of state, but it does have tonnes of methods that all do something to a raster full of pixels (a "Bitmap," just as you said).

All the other stuff you mentioned strikes me as part of your Photoshop class (or classes); and considering the multitude of filters available for image manipulation (there's at least a dozen for resizing alone), yes, it's quite possible that you will have classes that contain piles of methods.


It's the difference between "class" and "classes" that has me going 'round in mental circles on this. Why would I put one method into one class, and another into another class, when both are of the form "public void processImageXXX(BuffferedImage image)"? Just because that keeps my classes smaller?

Or, more likely, they'll be part of some image manipulation library - don't re-invent the wheel.


Heh. At my age, you can be sure I don't want to be writing code that already exists. Not enough time left, dig?

Thanks for the counsel. I guess I may really just be in a context that justifies a lot of little routines that have no particular reason to be isolated from each other.

This particular inquiry arose, btw, when I started Googling for "good Java examples," and so on. Turns out that most of what people point to is either not a good example (people often say, "read the JDK source," but that violates nearly every "guideline" I've heard of, and also handles a lot more state than I've got to deal with), or relates to problems that naturally fall into categories of dinstinguishable responsibility that suggest natural classes for each one (and most of those seem to miimic normalized relational databases in their design, which I actually do know a lot about, but which is not a good paradigm for what I'm doing). If you know a good site or source for what you think of as exemplary code (in its design, in particular), please let me know.

Cheers,
Stevens
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:Thus, I am kind of baffled about all this. Is passing a reference in a constructor call just a way to help move code out of the referenced object? If so, is that a commonly accepted practice?


Sorry. Missed this question.
Yes. It's called a 'callback', and it's probably worth Googling. I suspect Wikpedia has a good page that describes them, and its quite common in Action/Listener constructs.

That is, is it good practice to create a class solely for the purpose of providing a tidy place to store methods, when those methods will not operate on any instance variables of that class, but, instead, operate on another object entirely? That seems like a dodge, to me, serving the same function that libraries of functions perform in procedural languages.


That might be the case if the method were simply generalized static methods; and there are a few examples of those: java.util.Arrays, java.util.Collections and java.lang.System being just three.
But in your case, a Transaction is a class that gives some meaning to a change. It might even encapsulate a bunch of changes to different Accounts, and it probably gives them much better names than
public boolean setBalance(int amount) { ...

HIH

Winston
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:It's the difference between "class" and "classes" that has me going 'round in mental circles on this. Why would I put one method into one class, and another into another class, when both are of the form "public void processImageXXX(BuffferedImage image)"? Just because that keeps my classes smaller?


No, it's to help define them. You might have a Sharpener class, or a Resizer class, or an Effects class (I'm just going by what I've seen in PS and Gimp you understand; I've really no idea how I would write - or group - them), and they could easily be interfaces rather than classes, so that designers are free to implement them any way they see fit.

And the fact that they act on an Image? Well, my presumption (although again, I'm no expert in this stuff) is that an Image might be in bitmap or vector-graphics form; and that the internals for each of those types would be drastically different, even though the facade they present to the world (ie, the "things" you can do to them) is the same.

Winston
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:I keep reading that, if one's class is over 200 (or 500, or 1000; take your pick) lines long, then one's class is too long. Granted, these are always qualified as "guidelines," but the people offering them tend to go on from there as though they'd never said that, and act like any class over 200/500/1000 lines has just got to be badly designed.


I think that's probably truer for methods than for classes and for them, my usual rule of thumb is 'no more than a screenful'.

But classes are quite different: String, for example, has over 3,000 lines, and I've written a few with more than that. I would say that, when you get to that sort of size, it's probably worth asking yourself whether all that code is justified but, if it is, the class should be a big as it needs to be.

Winston
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Winston Gutkowski wrote:

Stevens Miller wrote:It's the difference between "class" and "classes" that has me going 'round in mental circles on this. Why would I put one method into one class, and another into another class, when both are of the form "public void processImageXXX(BuffferedImage image)"? Just because that keeps my classes smaller?


No, it's to help define them. You might have a Sharpener class, or a Resizer class, or an Effects class (I'm just going by what I've seen in PS and Gimp you understand; I've really no idea how I would write - or group - them), and they could easily be interfaces rather than classes, so that designers are free to implement them any way they see fit.

And the fact that they act on an Image? Well, my presumption (although again, I'm no expert in this stuff) is that an Image might be in bitmap or vector-graphics form; and that the internals for each of those types would be drastically different, even though the facade they present to the world (ie, the "things" you can do to them) is the same.

Winston


Urp. I wrote a reply to this, but it vanished. Short version is that I am having a hard time seeing any difference between stateless static methods and a library of functions. However, your revelation about the String class was an eye-opener. I dug around in some of the other JDK source, and, lo!, quite a few of them are thousands of lines long! Some even include inner classes of considerable length. Then, of course, there are classes like Math that really are just big libraries of static functions.

Not that I'm saying OOP generally calls for something other than the tidy classes I keep seeing in the examples in books; I'm not nearly ready to comment on things like that (and, when I am, I doubt that's what I'll say). But it's comforting to see that some of the indicia I expect of contexts calling for large classes with many methods (most specifically, that they have no state) do appear in actual practice.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:Short version is that I am having a hard time seeing any difference between stateless static methods and a library of functions.


Short answer is: they don't. But in OO, truly stateless classes are quite rare - and usually created to mimic exactly what you describe - including, as you said, java.lang.Math. It might be worth noting though that Math.random() has been superceded by (and the method actually rewritten to use) java.util.Random, which is a purpose-built random number generator class, and IMO much more useful.

But it's comforting to see that some of the indicia I expect of contexts calling for large classes with many methods (most specifically, that they have no state) do appear in actual practice.


As I say, it does happen, but not as often as I suspect you think. A lot of the methods in String, for example, might look stateless; but that's because String is immutable. And as for your Photoshop methods, it seems far more flexible to me for its library to be made up of a bunch of interfaces which anyone who thinks they have "better mousetrap" can implement, rather than a bunch of static classes - ie, the same paradigm that's used for JDBC.

However, we seem to have strayed somewhat from the original brief...

Winston
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Winston Gutkowski wrote:

Stevens Miller wrote:Short version is that I am having a hard time seeing any difference between stateless static methods and a library of functions.


Short answer is: they don't. But in OO, truly stateless classes are quite rare - and usually created to mimic exactly what you describe - including, as you said, java.lang.Math. It might be worth noting though that Math.random() has been superceded by (and the method actually rewritten to use) java.util.Random, which is a purpose-built random number generator class, and IMO much more useful.


Oh, that's a beauty! I have no idea how the old version worked, but the javadoc makes it clear that the new Math.random actually does maintain state: it has its own Random object. Now, that's a case I can totally understand. The Random object (or the Math.random method) has an instance member variable that no other object needs to know about (nor should it), but which the Random object (or the Math.random method) relies upon from one call to the next. In a situation I like that, I can easily(!) see how an object is the right thing to have, as opposed to a method in the class that needs the random number, and also as opposed to a static method (the javadoc actually does a nice job of explaining why, because the Math.random method is synchronized, you would be better off with a lot of Random objects instead, to prevent contention). Amazingly, I think I actually get all that.

But it's comforting to see that some of the indicia I expect of contexts calling for large classes with many methods (most specifically, that they have no state) do appear in actual practice.


As I say, it does happen, but not as often as I suspect you think.


I am rapidly coming to that conclusion myself.

A lot of the methods in String, for example, might look stateless; but that's because String is immutable.


Sure, though that is really a property the String class has in common with any class that has no state, isn't it? That is, stateless objects are necessarily immutable, because there is nothing to mutate, correct?

And as for your Photoshop methods, it seems far more flexible to me for its library to be made up of a bunch of interfaces which anyone who thinks they have "better mousetrap" can implement, rather than a bunch of static classes - ie, the same paradigm that's used for JDBC.


Yeah, I think I can see that, too. For future development (and future developers), that could be quite valuable. For an application that isn't going to produce a library, I wonder if that's more a theoretical, rather than actual, benefit.

However, we seem to have strayed somewhat from the original brief...


Hah! That's because you fell into my trap!

See, I keep looking for some code that will help me get a conceptual grip on the question of when to create a new object. I went looking for "case studies" and found the BankAccount case we started with. My problem in seeing why it was a good example all comes from the fact that the new classes (the ones that operate on the example's BankAcount objects) only retain one member variable, which was a reference to a BankAccount object. As it turns out, I am finding that a rather unpersuasive example of the benefits of OOP (and, therefore, not a very helpful example of good design). What I really wanted was to provoke a discussion (which we have had, and thanks again, as always) about the more general question of being able to recognize a single class that really ought to be two or more classes. And, a little, teeny, weeny bit, I think I am getting some insight into it.

If I may, let me continue for a moment with the PhotoShop situation (since I understand that better and it's closer to what I'm doing). Suppose, for example, that I have a number of raster images I want to resize. In a procedural approach, I'd iterate over them all with something like this:

That's just some bullsh*t pseudo-code, so I have left out anything other than the function call, showing that the new dimensions are passed with each image to a resizing function.
Some of my puzzlement here arises from the fact that a "resizing" object doesn't really change this much:

Clearly, the rezise method can be stateless, if all it does is some simple process requiring only the image and dimensions to execute.

But...

There might be some kind of resizing that calls for initialization, say, of a table, or some other calculation that would be the same for a set of dimensions, and that does not need to be recomputed for each image. Using an interface, I could do this:



Then, for all my images, I could set up one Resizer object, and pass it to each image to use on itself



Uh... am I evenly remotely getting it here? (Please insert the emoticon that best represents the desperate hope of the man who is lost in the desert, as he thinks he sees an oasis in the distance, praying that it is not just another mirage this time.)

We've been at this quite a while. Feel free to e-mail me any American legal questions you might have, as I owe you some professional services in return for your tutoring by now.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:If I may, let me continue for a moment with the PhotoShop situation (since I understand that better and it's closer to what I'm doing). Suppose, for example, that I have a number of raster images I want to resize.


OK, but supposing it isn't a raster image (eg, a VG image). Then what do you do?

To me, an Image, like a bank Account, is something that knows how to update or present itself in various pre-defined ways. Your Resizer (more below) would be something that knows how to resize an image in general terms, and simply tells the Image what it should do - which brings up an interesting point that you might want to read about.

Uh... am I evenly remotely getting it here?


Yup, I think you're starting to get it. Another thing to consider, as I said before, is that there are all sorts of different ways of resizing (Lanczos and Mitchell are the ones I use most frequently, but there are tons of others), so having a Resizer interface allows a class to be implemented with whatever algorithm you want to use - indeed, you could even allow your users to choose which one they want.

And don't worry if you don't get it straight away. I was a COBOL hack for my first ten years in the biz, and the inclination to think procedurally is a natural one. I reckon I'm a reasonably intelligent bloke, but it took me 8 years of C++ and Java before I had my "moment of clarity" (and it truly was a 'Eureka' moment - the old light-bulb suddenly coming on - and those don't happen very often).

All I can say is: keep plugging away; and if you don't get it all yet, at least remember all the stuff you were taught about good procedural code (modularity, loose coupling etc.).
The other thing I would say - and I really can't stress this enough - is that while you're at the design stage, concentrate on WhatNotHow - that is: what you want to do, NOT how you're going to do it. It's an incredibly hard thing for most procedural bods to do, but the fact is that if you can't come up with a good English description of what you want to do, you're starting off at a big disadvantage.

Another possibility is to look for problems (eg, the Traffic Light problem that I've described in another thread; I'll post a link if I find it) that DON'T lend themselves to a procedural solution, and see if you can work out why.

Feel free to e-mail me any American legal questions you might have...


Don't get me started...truly. Adversarial vs. Inquisitorial (I live in Belgium) is just the tip of the iceberg.
I recently got the entire collection of Law & Order though, and I'm thoroughly enjoying going through all the back episodes I missed.

Winston

[Edit] PS: The link to the Traffic Light problem.
 
Today you are you, that is turer than true. There is no one alive who is youer than you! - Seuss. Tiny ad:
Smokeless wood heat with a rocket mass heater
https://woodheat.net
reply
    Bookmark Topic Watch Topic
  • New Topic