Skip to content

Instantly share code, notes, and snippets.

@christianromney
Forked from rajakolluru/NotMyKindaModel.java
Created August 28, 2012 18:15
Show Gist options
  • Select an option

  • Save christianromney/3501625 to your computer and use it in GitHub Desktop.

Select an option

Save christianromney/3501625 to your computer and use it in GitHub Desktop.
/* This is my interface, my API, my contract. */
interface IReservation {
IList<IGuest> getGuests();
Duration getLength();
Amount getBalance();
Amount getTotal();
bool cancel();
// By the way pay() could have been in IPaymentMethod as well but you know what?
// I called first and so I win!
// Or both of us would end up implementing pay() in which case we would smartly delegate
// the job of implementing pay() to a common helper class
// Any similarity between the helper class and the IReservationManager is purely coincidental
// [CR] Hmm, I think this difference boils down to design. I don't think pay() belongs on PaymentMethod,
// because it is being <<used>> to make a payment, not making the payment itself. But we're bordering on
// diminishing returns with this toy example. Incidentally, a method I do think the PaymentMethod should
// support is validate().
PaymentStatus pay(IPaymentMethod method, Amount amount);
PaymentStatus payInFull(IPaymentMethod method);
// ... and much more ...
}
/*
Hi, I'm Reservation. If you'd like to pay, let me
know. Like a good waiter, I'll take your money to
the cashier and come back with your doggie bag.
But there are caveats.. So hold on to your seat and look at
what I got to say below for the pay methods
*/
@serializable
class Reservation implements IReservation {
// Dependency injection for the cashier...
// Yes.. don't even think about instantiating me if you dont have the
// ITransactionGateway. I need one dead or alive. It would be dead if
// it is in a non service layer and alive if it is in the service layer.
// And yes, you need to use the DI to obtain an instance of me since that is the
// guy who knows how to instantiate any object including domain objects. So for every
// request you need to ask the DI framework to create me even if it is slow.
// [CR] Donald Knuth on performance optimization:
// "Premature optimization is the root of all evil."
// ;)
// And no I don't need to think about instantiating you with or without
// an ITransactionGateway since the DI container handles the details.
public Reservation(ITransactionGateway backend) {...}
// Assume these do something useful
public IList<IGuest> getGuests() {...}
public Duration getLength() {...}
public Amount getBalance() {...}
public Amount getTotal() {...}
public bool cancel() {...}
/*
You want to pay in full? No problem, I know
how much you owe. Your credit card, please...
I might tempt you to call me from the presentation
layer but unless the service layer is co-resident in the same JVM I
am not going to work!
[CR] Okay so I'm beginning to see where you're going with this, but
I'm not falling for this "temptation" you speak of. We're *not* calling
pay() from the presentation layer since it's for...presentation. If
you're looking to enforce that rule, there are patterns for this pathological case, too.
You can introduce the ReservationPresenter if you *really* feel you need to—though
I would hope it does something more interesting than no-op "dangerous" or inappropriate
interface methods. Perhaps it can transform some data into a more convenient format
for the front end or something equally useful. We want no slacker classes here-in fact
the less software we write, the better. In this scenario, we each have a different "extra"
class. But I'm relying on more on convention to avoid these scenarios, where you're
relying on configuration.
*/
public PaymentStatus payInFull(IPaymentMethod method) {
return pay(method, getBalance());
}
/*
The "cashier" (backend) handles the actual transaction;
that's not my responsibility. I just tell the cashier
what you want to purchase ("this") and bring you back
the answer.
Same comments as above.
*/
public PaymentStatus pay(IPaymentMethod method, Amount amount) {
return backend.applyPayment(this, method, amount);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment