views:

168

answers:

11

Hi,

I am trying to write a program in object-oriented style. I have some confusions when coding the interaction between two objects.

Scenario: Person (John) gives Person (Betty) $ 5.

Possible solutions (pseudo code):

A) John.pays(Betty, 5);
B) Betty.receives(John, 5);
C) Bank.transfer(John, Betty, 5);
D)
begin transaction:
John.decrease(5);
Betty.increase(5);
end transaction:
E) Service.transferMoney(John, Betty, 5); // Service is a generic service object

Please tell me which one is a more appropriate way of coding in OOP way, and the reason behind it. I am looking for some guidelines like "Tell, Don't Ask" rule.

Thanks.

A: 

My vote: C. Where C does what D does (e.g. doesn't lose money, etc.).

In this small example, "the bank" is a perfectly valid entity which knows how much money John and Betty have. Neither John nor Betty should be able to lie to the bank.

Don't be afraid to invert (or not) the logic in an "OO" program as required for the situation.

pst
A: 

You should model according to your domain. Option C looks best choice as it will separate the transaction logic into the Bank\Service class.

Unmesh Kondolikar
A: 

This is a question I often struggle with myself as a novice programmer. I agree that "C" seems like the best choice. In something like this, I think it's best to use a "neutral" entity such as the "bank". This actually models most real life transactions of importance since most transactions of import utilize checks and/or credit (a neutral 3rd party).

Pete
+2  A: 

I'd vote for none of the above :)

Why is John paying Betty? That's an important question, as it explains where the entry point is. Let's say John owes Betty money, and it's payday.

public class John
{
    public void onPayday()
    {
        Betty.Receive(5.0f);
    }
}

This is if you want to go with a pure object-interaction style approach, of course.

The difference here is that we don't have an outside routine coordinating the interactions between John and Betty. Instead, we have John responding to external events, and choosing when to interact with Betty. This style also leads to very easy descriptions of desired functionality - eg "on payday, John should pay Betty."

This is a pretty good example of what Inversion of Control means - the objects are interacting with each other, rather than being manipulated by some external routine. It's also an exmaple of Tell, Don't Ask, as the objects are telling each other things (John was told it's payday, John tells Betty to accept 5 dollars).

kyoryu
_@kyoryu_: This sounds all very nice, but where does the actual money transfer *happen*, and what would that code look like? As I understand your answer, `Betty.Receive(5.0f)` doesn't actually transfer the money, but instead only triggers an event ("someone asks betty to receive 5$"). Otherwise (if that method call indeed does the money transfer), then surely you are voting for the OP's option B?
stakx
@stakx: It's a simplified example to show object interaction. The primary difference between this option and Option B are that in Option B, there is still a higher level procedure manipulating John and Betty, and acting as an intermediary. In this option, John and Betty are *directly* interacting, without needing an intermediary at all. Real code for this would also include John deducting his $5, and either committing or rolling back when Betty successfully received it - something like the case described here: http://www.eaipatterns.com/ramblings/18_starbucks.html
kyoryu
+2  A: 

There are a number of alternate solutions here. For instance,

Betty.Receieves(John.Gives(5))

This assumes that the Gives function returns the amount given.

tx = CashTransaction(John, Betty);
tx.Transfer(5);

This assumes the first prameter is the Payor, and the second is the Payee, then you can perform multiple transactions without creating new objects.

Things can be modeled in a number of ways. You should choose the one that most closely resembles what you are trying to model.

Mystere Man
This would make Gives() method highly coupled on Receives() method().
janetsmith
No it doesn't. Gives only returns a value, John doesn't care who he's giving it to. Betty, likewise only receives a number.. and doesn't care who are what gives it to her.
Mystere Man
Actually, the Gives/Receives method is so uncoupled, that it's a bit leaky. You can pass any number to it, and numbers can get lost if they're not "recieved". In real life you would fix this through abstract classes, polymorphism, or interfaces. However, in such simple constructs one has to assume security and robustness are not issues.
Mystere Man
+1  A: 

If you really want to get OOPy, try the following

Person       Betty,John;
CashTransfer PocketMoney;
PocketMoney.from   = John;
PocketMoney.to     = Betty;
PocketMoney.amount = 20.00;
PocketMoney.transfer();

The point of OOP isn't to make code more like written language, but to have objects with different methods and parameters to make code more readable.

So from the above code, you can see that John is giving Betty $20 in pocket money. The code is meaningful, allowing for easier code readability, as well as understandability.

Andrew Dunn
This isn't OOPy. This is obfuscatory. In particular having an object in an unusable undead state post-construction is, frankly put, evil. Objects should be born in usable condition, not in a state of undeath.
JUST MY correct OPINION
But then people could have methods such as Person::AddFunds() etc the example is extremely primitive.By having a separate object for transferral of cash, you give the transferral meaning, as well as giving the transferral the ability to be used a countless number of times, PocketMoney.transfer() will from then on make John give Betty $20.
Andrew Dunn
You are missing my point. You are instantiating CashTransfer (PocketMoney). That instantiated class is undead. You have to fill it with the brains of John and Betty and some money before it's a fully-living class that you can actually invoke the `transfer()` method on. John and Betty should be in the constructor, IMO, and the amount should be in the `transfer` call.
JUST MY correct OPINION
@Just your incorrect OPINION, No, he's not missing your point. You're missing the point that this is not real world code, but rather illustrating a single concept. The solutions listed here are missing lots of real world concepts, like having a transaction with full rollback support, exception handling, auditing, etc... Yes, most people will agree with you that objects should be constructed correctly (there are exceptions, and sometimes it's not possible given the constraints) but this is not a real world problem or solution.
Mystere Man
+9  A: 

One thing I've noticed is that people that are new to OOP get caught up in trying to map the physical world into the code they are writing. Do you really care that John and Betty are people or are you actually wanting to depict a bank account? I think your choice of objects in the example actually make it harder to figure out the solution to the problem.

The important parts of this are 1) Where to put the logic of how to move the money. 2) Where to store the data of how much money each person has.

Both of these questions depend on if you are talking about real people or a customer of a bank. I'm guessing you are talking about a customer because I can't think of a reason to write an app for the other. Also, a Bank is a pretty generic term, is it the big brick building with people inside of it or is it the online website with several different pages that do different things. A bank account object can have a method (possibly static depending on how you decide to store your data and what all you are going to use your object for) that knows how to transfer from one account to another. The logic of how to transfer does not belong to Betty or John or a bank, it belongs to a bankAccount which can have special logic based on the type of account if there are fee's involved or the like. If you gave that logic to the bank you would end up with a giant bank class with methods for everything from greating a customer to dealing with money in very specific account types.

Based on just the need to transfer money there is no need to create a bunch of other objects. Based on the known requirements and presumed future requirements the below would be a good choice. CheckingAccount.Transfer(johnsAccountNo, bettysAccountNo)

BitOff
+1, since there can be a company behind a bank account instead of a person.
chelmertz
+3  A: 

Can I ask a question now? Who controls the money? Does John decide the transaction amount, does Betty, or some unspecified 3rd party?

The reason I am asking is because there is no real right or wrong answer here, just one that might be more flexible, or robust than the others. If this is a real life situation then I would model the transaction as something that both parties have to agree on before it proceeds, and the person spending the money (John) initiating it. Something like answer C and @Mysterie Man

tx transaction_request = John.WantsToBuyFor(5);    //check if John can
if( Betty.AgreesWith( transaction_request ) )      //check if Betty wants
{
   transaction_request.FinalizeWith(Betty);        //Do it with Betty
}

and the FinalizeWith function does the math

void FinalizeWith(Person party)
{
    requestor.cash -= amount;
    party.cash += amount;
{

Of course you might want to add some description of what item is John buying.

jalexiou
+1, not for the concrete code example, but because of the line of thinking (a controller and two parties that both have to agree before the controller will do its work). And for the transaction that "do[es] it with Betty" :)
stakx
+3  A: 

The answer to this question is a long and complicated one that you'll get in bits and pieces from a large number of people. Why only in bits and pieces? Because the correct answer depends almost entirely upon what your system's requirements are.

One trap you will have to make sure you don't fall into, however, is this one. Read the answers you get here. You'll get a lot of good advice. (Pay the most attention to the advice that's been voted up a lot.) Once you've read and understood those, read Steve Yegge's rant (and understand it!) as well. It will save you sanity in the long run.

JUST MY correct OPINION
A: 

Being new to OOP and finally using some OOP, I'd say that it should be A and B.

We are focusing on persons and it's up to each person to handle his money. We don't know if he's going to use the bank or if he's just getting cash directly from Betty.

You created a Person class and you add methods to the class with two methods: send and recieve. It also must have a public var named balance to keep track of their balances.

You create two Person objects: Betty and John. Use methods accordingly. Like John.sends(Betty, 5). That should create Betty and update Betty's balance as well.

What if they want to use the bank? Add another method, say... Transfer(acct) whatever it is.

That's what I would think.

netrox
+2  A: 

There is one property of pure OOP that can help with the example which easily passes under the radar, but the object-capability model makes explicit and centers on. The linked document ("From Objects to Capabilities" (FOtC)) goes into the topic in detail, but (in short) the point of capabilities is that the ability of an object to affect its world is limited to objects it has references to. That may not seem significant at first, but is very important when it comes to protecting access and affects what methods of a class are available in methods of other classes.

Option A) gives account John access to account Betty, while option B) gives Betty access to account John; neither is desirable. With option C), account access is mediated by a Bank, so only Banks could steal or counterfeit money. Option D) is different than the other three: the others show a message being sent but not the implementation, while D) is a method implementation that doesn't show what message it handles, nor what class it handles it for. D) could easily be the implementation for any of the first three options.

FOtC has the beginning of a solution that includes a few other classes:

  • sealers & unsealers,
  • purses, which are a little like accounts in that they contain money but don't necessarily have an owner.
  • mints, which are the only things that can create purses with positive balances

A mint has a sealer/unsealer pair, which it endows to a purse whenever the mint creates one. Purses oversee balance changes; they use the sealer when decreasing a balance, and the unsealer to transfer from one purse to another. Purses can spawn empty purses. Because of the use of sealers & unsealers, a purse only works with other purses created by the same mint. Someone can't write their own purse to counterfeit money; only an object with access to a mint can create money. Counterfeiting is prevented by limiting access to mints.

Anyone with access to a purse can initiate a transaction by spawning an empty purse and transferring money from the first purse into it. The temporary purse can then be sent to a recipient, which can transfer money from the temporary purse to some other purse that it owns. Theft is prevented by limiting access to purses. For example, a bank holds purses on behalf of clients in accounts. Since a bank has access only to the purses of its clients' accounts and temporary purses, only a client's bank can steal from the client (though note that in a transfer between bank accounts, there are two clients that can be victimized, hence two potential thieves).

This system is missing some important details, such as monetary authorities (which hold references to one or more mints) to create money.

All in all, monetary transactions are tricky to implement securely, and thus may not be the best examples to learn the basics of OOP.

outis
+1 great explanation of the four options in the 2nd paragraph. The rest of your answer maybe focuses on security more than necessary, but this is a very interesting read nevertheless.
stakx