[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Strategy pattern / interface design arrangement

Daniel Waite

8/21/2006 5:55:00 AM

Hi all. I've got a design question.

Imagine you have a module, CreditCardProcessor. Inside
CreditCardProcessor is another module called AuthorizeNet.
CreditCardProcessor acts as an interface that AuthorizeNet must
implement. (This is, from what I understand, a reasonably accurate
implementation of the Strategy pattern (behavioral implementation behind
a common interface). However, if you feel I could be more accurate,
don't hesitate to say so. :)

Now imagine we have a class named Acme that extends class Company.
Company includes CreditCardProcessor and has an instance variable called
@credit_card_processor.

Now Acme has all the tools it needs to process cards. However, do we
say...

@company.process_credit_card(card) # A pass-through (convenience) method

... or...

@company.credit_card_processor.process_card(card)

If you were designing (or have designed) such an arrangement, which is
your preferred method and why? (Or if you have an entirely different
suggestion I'm all ears.)

Thanks, much!

- Daniel

--
Posted via http://www.ruby-....

17 Answers

snacktime

8/21/2006 6:23:00 AM

0

On 8/20/06, Daniel Waite <rabbitblue@gmail.com> wrote:
> Hi all. I've got a design question.
>
> Imagine you have a module, CreditCardProcessor. Inside
> CreditCardProcessor is another module called AuthorizeNet.
> CreditCardProcessor acts as an interface that AuthorizeNet must
> implement. (This is, from what I understand, a reasonably accurate
> implementation of the Strategy pattern (behavioral implementation behind
> a common interface). However, if you feel I could be more accurate,
> don't hesitate to say so. :)
>
> Now imagine we have a class named Acme that extends class Company.
> Company includes CreditCardProcessor and has an instance variable called
> @credit_card_processor.

I would probably not put CreditCardProcessor inside Company. I would
leave it as a stand alone module and call it when needed.

Robert Klemme

8/21/2006 8:24:00 AM

0

On 21.08.2006 07:55, Daniel Waite wrote:
> Hi all. I've got a design question.
>
> Imagine you have a module, CreditCardProcessor. Inside
> CreditCardProcessor is another module called AuthorizeNet.
> CreditCardProcessor acts as an interface that AuthorizeNet must
> implement. (This is, from what I understand, a reasonably accurate
> implementation of the Strategy pattern (behavioral implementation behind
> a common interface). However, if you feel I could be more accurate,
> don't hesitate to say so. :)

I find it odd that AuthorizeNet is a module and that it's contained in
CreditCardProcessor which you said defines the interface. First, you
don't have interfaces in Ruby (and you don't need them). Second,
nesting the "interface" and classes that implement it seems weird to me.
If we assume for the moment that we are using a language with
interfaces then the main point of them is, that you can have arbitrary
classes implement them. And these classes can reside in whatever
package or namespace.

> Now imagine we have a class named Acme that extends class Company.
> Company includes CreditCardProcessor and has an instance variable called
> @credit_card_processor.

Why does Company include CreditCardProcessor if it's just an interface?

> Now Acme has all the tools it needs to process cards. However, do we
> say...
>
> @company.process_credit_card(card) # A pass-through (convenience) method
>
> .. or...
>
> @company.credit_card_processor.process_card(card)
>
> If you were designing (or have designed) such an arrangement, which is
> your preferred method and why? (Or if you have an entirely different
> suggestion I'm all ears.)

It always depends... I'd probably go with the second approach because
that does less cluttering of class Company's public interface / signature.

Kind regards

robert

Ilan Berci

8/21/2006 3:28:00 PM

0

Daniel Waite wrote:
> Hi all. I've got a design question.

>
> Now imagine we have a class named Acme that extends class Company.
> Company includes CreditCardProcessor and has an instance variable called
> @credit_card_processor.
>

Some of the GOF patterns don't translate to a dynamically typed language
such as Ruby. With duck typing, there is no need to explicitly set the
interface that the internal strategy component will adhere to and your
implementation is free to swap strategy components just as you would in
a statically typed language but with the added flexibility of not having
to explicitly set the interface before hand..




--
Posted via http://www.ruby-....

Daniel Waite

8/21/2006 7:21:00 PM

0

Robert Klemme wrote:
> I find it odd that AuthorizeNet is a module and that it's contained in
> CreditCardProcessor which you said defines the interface. First, you
> don't have interfaces in Ruby (and you don't need them).

I realize Ruby doesn't explicitly support the concept of an interface
via a keyword (convention over configuration anyway, right?). However,
that doesn't mean I can't program inline with the _concept_ of an
interface. The _concept_ of design by contract. It's those concepts I'm
trying to utilize because I believe they're helpful.

> Second, nesting the "interface" and classes that implement
> it seems weird to me.

That's the hallmark of the Strategy pattern.

> If we assume for the moment that we are using a language with
> interfaces then the main point of them is, that you can have arbitrary
> classes implement them.

Yes and no. Mostly no in my situation. The point of using an interface
_in this case_ is to encapsulate a defined set of behaviors into many
possible implementations. We may well add another payment processor to
our arsenal later down the road. Provided the interface is strong enough
I should be able to code to that interface and not be able to tell which
implementation I'm using.

> Why does Company include CreditCardProcessor if it's just an interface?

To this end I'm not certain. Looking at my examples again I suppose it
doesn't make much sense to include it into Company.

CreditCardProcessor.process_credit_card(@company.credit_card, order)

Would make more sense. Or does it? :(

Ilan Berci wrote:
> Some of the GOF patterns don't translate to a dynamically typed language
> such as Ruby.

What's GOF?

> With duck typing, there is no need to explicitly set the
> interface that the internal strategy component will adhere to and your
> implementation is free to swap strategy components just as you would in
> a statically typed language but with the added flexibility of not having
> to explicitly set the interface before hand..

Not only is there no need, there's no way to explicitly set the
interface in Ruby. But I want to act as though it's explicit, that it
must be adhered to.

snacktime wrote:
> I would probably not put CreditCardProcessor inside Company. I would
> leave it as a stand alone module and call it when needed.

Aye, I think I'm with you on this. Thanks! :)

--
Posted via http://www.ruby-....

Kenosis

8/21/2006 7:30:00 PM

0


Daniel Waite wrote:
> Robert Klemme wrote:
> > I find it odd that AuthorizeNet is a module and that it's contained in
> > CreditCardProcessor which you said defines the interface. First, you
> > don't have interfaces in Ruby (and you don't need them).
>
> I realize Ruby doesn't explicitly support the concept of an interface
> via a keyword (convention over configuration anyway, right?). However,
> that doesn't mean I can't program inline with the _concept_ of an
> interface. The _concept_ of design by contract. It's those concepts I'm
> trying to utilize because I believe they're helpful.
>
> > Second, nesting the "interface" and classes that implement
> > it seems weird to me.
>
> That's the hallmark of the Strategy pattern.
>
> > If we assume for the moment that we are using a language with
> > interfaces then the main point of them is, that you can have arbitrary
> > classes implement them.
>
> Yes and no. Mostly no in my situation. The point of using an interface
> _in this case_ is to encapsulate a defined set of behaviors into many
> possible implementations. We may well add another payment processor to
> our arsenal later down the road. Provided the interface is strong enough
> I should be able to code to that interface and not be able to tell which
> implementation I'm using.
>
> > Why does Company include CreditCardProcessor if it's just an interface?
>
> To this end I'm not certain. Looking at my examples again I suppose it
> doesn't make much sense to include it into Company.
>
> CreditCardProcessor.process_credit_card(@company.credit_card, order)
>
> Would make more sense. Or does it? :(
>
> Ilan Berci wrote:
> > Some of the GOF patterns don't translate to a dynamically typed language
> > such as Ruby.
>
> What's GOF?
>
> > With duck typing, there is no need to explicitly set the
> > interface that the internal strategy component will adhere to and your
> > implementation is free to swap strategy components just as you would in
> > a statically typed language but with the added flexibility of not having
> > to explicitly set the interface before hand..
>
> Not only is there no need, there's no way to explicitly set the
> interface in Ruby. But I want to act as though it's explicit, that it
> must be adhered to.
>
> snacktime wrote:
> > I would probably not put CreditCardProcessor inside Company. I would
> > leave it as a stand alone module and call it when needed.
>
> Aye, I think I'm with you on this. Thanks! :)
>
> --
> Posted via http://www.ruby-....

GOF = Gang of Four (the authors of the "original" Design Patterns"
text.)

Daniel Waite

8/21/2006 8:02:00 PM

0

Kenosis wrote:
> GOF = Gang of Four (the authors of the "original" Design Patterns"
> text.)

Ah... I'm currently reading Kathy Sierra's Head First Design Patterns.
Kinda sucks because it's written in and for Java programmers, so my
translations are sometimes rough.

Thanks for the heads up. :)


--
Posted via http://www.ruby-....

Rick DeNatale

8/21/2006 9:07:00 PM

0

On 8/21/06, Daniel Waite <rabbitblue@gmail.com> wrote:
> Robert Klemme wrote:
> > I find it odd that AuthorizeNet is a module and that it's contained in
> > CreditCardProcessor which you said defines the interface. First, you
> > don't have interfaces in Ruby (and you don't need them).
>
> I realize Ruby doesn't explicitly support the concept of an interface
> via a keyword (convention over configuration anyway, right?). However,
> that doesn't mean I can't program inline with the _concept_ of an
> interface. The _concept_ of design by contract. It's those concepts I'm
> trying to utilize because I believe they're helpful.

Yes, but Modules in Ruby provide implementation, so they don't really
make good reifications of the concept.

> > Second, nesting the "interface" and classes that implement
> > it seems weird to me.
>
> That's the hallmark of the Strategy pattern.

Here I have to politely disagree. The hallmark of the Strategy
pattern is the provision of a configurable service object (which the
GOF calls a Context) which clients can configure by giving it
different ConcreteStrategy objects, and the separation of
implementation of the Context and the various ConcreteStrategies,
through the use of an interface definition called a Strategy.

So one way of looking at the strategy in the pattern is that it
identifies the species of duck that the context is looking for when
the client configures it.

But this can be done as documentation or commentary.

> > If we assume for the moment that we are using a language with
> > interfaces then the main point of them is, that you can have arbitrary
> > classes implement them.
>
> Yes and no. Mostly no in my situation. The point of using an interface
> _in this case_ is to encapsulate a defined set of behaviors into many
> possible implementations. We may well add another payment processor to
> our arsenal later down the road. Provided the interface is strong enough
> I should be able to code to that interface and not be able to tell which
> implementation I'm using.

But again, there's no real need or advantage to reify it in a Module.
And if you populate the Module with 'unimplemented' methods, and
require the ConcreteStrategies to include it, it actually obscures
whether or not a given ConcreteStrategy really implements the
Strategy.

> > Why does Company include CreditCardProcessor if it's just an interface?
>
> To this end I'm not certain. Looking at my examples again I suppose it
> doesn't make much sense to include it into Company.

It's not a feature of the Strategy pattern, which seems to be about
allowing a client to configure an internal implementation choice.
Having the Context export the strategy interface to the client seems
to be more related to Facade or Proxy, but not quite.


> Ilan Berci wrote:
> > Some of the GOF patterns don't translate to a dynamically typed language
> > such as Ruby.
>
> What's GOF?

GOF = [Erich_Gamma, Richard_Helm, Ralph_Johnson, John_Vlissides]

These were the authors of the book "Design Patterns" which was first
to really popularize the idea of patterns which was "discovered" by
Ward Cunningham and Kent Beck in the writings of Architect (houses
and towns) Christopher Alexander, and developed into a burgeoning
movement including a cadre called the Hillside group which the book
brought out into the open. http://c2.com/cgi/wiki?History...

GOF.map { | author | author.native_language } => [[Swiss_German, C++],
[Strine, C++], [American, Smalltalk], [American, C++]]

I used to hang out fairly regularly with all four of them, and worked
with all but Ralph at IBM, OTI or both. I used to kid GOF[2] that he
didn't hold up his end of the stick as the lonely dynamic language
guy. <G> Sadly I recently discovered that GOF[3] passed away about a
year ago.

With the 3-1 composition of the GOF as guys with an abstract-data type
background, IMHO most of the patterns put too much emphasis on C++ish
implementation artifacts such as type-implementation confusion, and
templates (In fact in the implementation section on the Strategy
pattern they suggest cases where you might use C++ templates to
configure the Context rather than defining a Strategy interface).

Had Ralph pushed a bit harder, the book might well have abstracted
some of those concepts to duck types (Smalltalk had them, but I can't
recall that we used THAT name, it was most often roles or something
else http://talklikeaduck.denh...articles/2006/07/26/my-favorite-duck-ty...
), and folks might look on both the GOF patterns, and languages like
Ruby and Smalltalk in a different light.
--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denh...

Oliver Bandel

8/21/2006 11:01:00 PM

0

Rick DeNatale wrote:
[...]
>
> Had Ralph pushed a bit harder, the book might well have abstracted
> some of those concepts to duck types (Smalltalk had them, but I can't
> recall that we used THAT name, it was most often roles or something
> else
> http://talklikeaduck.denhaven2.com/articles/2006/07/26/my-favorite-duck-ty...
>
> ), and folks might look on both the GOF patterns, and languages like
> Ruby and Smalltalk in a different light.


A while ago I heard about duck typing.
I didn't have used Ruby at that time.... I started programming Ruby
about three days ago... and now look at this group....
....and again find this "duck type".

The things I read about it ("duck typing") when I first
had contact to this item seem to be very similar to
type polymorphism of functional languages.... look at
OCaml or haskell for example.

But maybe this analogy is not quite right...
....so if someone is here who can explain it better...
....if not, after jumping into Ruby more detailed,
I may do it later.

Some words about the Patterns: People often think
it must be necessary to use them in a language and then
all is ok. But the patterns are like a cooking recipe,
for meals that - sometimes - are ordered by the hungry people.

But even if theses meals maybe are ordered in some situations
very often, not all people want to eat them always.

They can be used if they are necessray, but why to insist on
using them?!

Well, someone here who can copmpare duck typing of OO-languages/Ruby
and type polymorphism of FPLs? They seem to be similar or the same?!


Ciao,
Oliver

Gavin Kistner

8/21/2006 11:37:00 PM

0

Oliver Bandel wrote:
> But maybe this analogy is not quite right...
> ...so if someone is here who can explain it better...
> ...if not, after jumping into Ruby more detailed,
> I may do it later.

Perhaps read the article that you cite:
http://talklikeaduck.denhaven2.com/articles/2006/07/26/my-favorite-duck-ty...


The short of it is: two different classes of objects are
interchangeable if they both support the same (named) methods that code
using those objects invoke. You don't need static-typing or
compile-type checking. You don't need interfaces. You don't need
contracts. You just need to have the methods available that you are
going to call.

Daniel Waite

8/22/2006 12:01:00 AM

0

Rick Denatale wrote:
> [A lot.]

Wow. Okay, your description of the Strategy pattern doesn't mean a lot
to me without an example. (Sorry if that sounds harsh; it's not my
intention.) Could you provide an example? Are such patterns even worth
pursuing in Ruby?

> ... Modules in Ruby provide implementation, so they don't really
> make good reifications of the concept.

We're using a module now, actually. In the event another payment
processor comes along do you feel it should simply be another module?
(That's interesting, that a module provides implementation. I've not
heard that said anywhere else, but maybe because it was considered
obvious?)

> Here I have to politely disagree. The hallmark of the Strategy
> pattern is the provision of a configurable service object (which the
> GOF calls a Context) which clients can configure by giving it
> different ConcreteStrategy objects, and the separation of
> implementation of the Context and the various ConcreteStrategies,
> through the use of an interface definition called a Strategy.

You're saying one class is given another, configurable class, which is
configurable by giving a ConcreteStrategy object? Example? :(

Confused, much! I suppose if I fall back on my earliest Ruby teachings,
modules implement behavior (e.g. Enumerable) and classes mix-in that
behavior. Gah... I'm reading three books and the information presented
is frying my brain. (Head First Design Patterns, XP Explained and Object
Thinking.)

Anywho, thanks for all the help thus far. :)

--
Posted via http://www.ruby-....