Strategy pattern / interface design arrangement

D

Daniel Waite

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
 
S

snacktime

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.
 
R

Robert Klemme

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
 
I

Ilan Berci

Daniel said:
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..
 
D

Daniel Waite

Robert said:
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 said:
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.
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! :)
 
K

Kenosis

Daniel said:
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.


That's the hallmark of the Strategy pattern.


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.


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? :(



What's GOF?


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.


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

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

Daniel Waite

Kenosis said:
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. :)
 
R

Rick DeNatale

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.
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.
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.
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.

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?HistoryOfPatterns

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.denhaven2.com/articles/2006/07/26/my-favorite-duck-typing-story
), and folks might look on both the GOF patterns, and languages like
Ruby and Smalltalk in a different light.
 
P

Phrogz

Oliver said:
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-typing-story


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.
 
D

Daniel Waite

Rick said:

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. :)
 
D

Daniel Waite

Phrogz said:
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.

Thank you! That's what I needed to hear! :) :) :)

I figured as much. From my experience with PHP 4, with weak OO support,
you could still code in a manner similar to C++ or Java without all the
rules _enforcing_ a particular paradigm.

I think what you said can be summed up in: Code to an interface, not an
implementation. Granted, that mentions one of the things you said you
don't need, but here's my codified understanding of it:

Implementation:

@authorize_net = AuthorizeNet.new
@authorize_net.process_card(card)

Interface:

@payment_processor = AuthorizeNet.new
@payment_processor.process_card(card)

That may be a weak example, but I think it illustrates the point. When
the "physical" variable I'm using tells me more about the object that it
should. E.g. "this is an AuthorizeNet object," versus "this is some kind
of credit card processor."

Thanks all!
 
K

khaines

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.

An example in real life.

IOWA apps receive an Iowa::Request object that encapsulates all of the
details about the request to be handled. Because different source
environments for the request, such as mod_ruby, FCGI, Webrick, Mongrel,
etc... themselves make the request information available in different
ways, the Iowa::Request needs to know how to initialize itself if it is
given an Apache::Request object from mod_ruby or a Mongrel request object.

It's very ugly to have all of that platform specific code piled up in
Iowa::Request, though.

So, the solution that I settled on is to duck type.

Iowa::Request has a method, new_request(). I waffled for a while about
just overriding new(), but in the end, decided against it. Anyway, an
instance of Iowa::Request is now never created. It simply serves as a
superclass to define a common set of accessors and make some common code
available to its subclasses, and to act as the factory to create the real
request objects.

Each of the subclasses implements the same API, but the
Iowa::Requests::Apache class takes an Apache::Request to initialize
itself, while the Iowa::Requests::WEBrick takes a webrick request object.
Each subclass knows how to take care of it's own specific details.

The Iowa::Request.new_request() method just provides a call to get a new
request. No code except for the code in new_request() cares about the
identity of the actual Request objects, and all it does is identify which
request class is needed, require the code if it has not already been
required, and then create an object of the class, which it returns.
Nothing else cares what it is, so long as it has the expected methods.

This seems like it might be a reasonable pattern for your credit card
processors, and it is easy to implement.

You have some method that you call to access or retrieve a processor.
Each of the real credit card processor classes implement the same API, so
none of the rest of your code has to care at all what they are. They are
just a thingy that you feed card and purchase info to; some lights flash,
there's some beeping and maybe a gear clack or two, and it spits back a
response indicating what happened during the processing.

cc_processor = CreditCardProcessor.new_processor(company)
buyers.each {|b| b.purchases.each {|p| cc_processor.process(b,p)}}


Kirk Haines
 
M

Matthew Johnson

These requirements are sufficiently important that we give them a
name: we call such a set of type requirements a *concept*, and we
call this particular concept *Input
Iterator <http://www.sgi.com/tech/stl/InputIterator.html>*. We say
that a
type *conforms to a concept*, or that it *is a model of a concept*,
if it
satisfies all of those requirements. We say that int* is a model of
*Input
Iterator* because int* provides all of the operations that are
specified by
the *Input Iterator* requirements.

There is a lot of power in identifying a cohesive set of type
requirements and giving them a name as it allows communication and
thought to move up a level so we can think of the requirements as a
single entity rather than thinking repsonds_to? :this and
responds_to? :that. This is much the same as using iterators rather
than explicit loops in our code.

A group of type requirements that is cohesive and is given a name
also increases the likelihood that code from various sources will be
written that both conforms to and works with types conforming to the
requirements. A nice example of this is the large number of types in
Ruby which work with Enumerable by implementing each and possibly
having members that implement <=>.

I believe it would be of benefit to the Ruby community to name more
of these cohesive sets of type requirements and give them informal
definitions as they are identified. STL provides examples of many
sets of type requirements, although STL concepts do not fit the Ruby
Way very well at all and of course would need to change quite
significantly in order to feel natural in Ruby. The collection
concepts and function object concepts in particular are very well
thought out and could be very applicable in more clearly defining
some common "duck types" in Ruby.
Concepts are not a part of the C++ language; there is no way to
declare a
concept in a program, or to declare that a particular type is a
model of a
concept.

Although interestingly they are likely to be a part of the language
in the future. There are a variety of Technical Reports published
that are being considered by the standards committee, thus
illustrating how the C++ approach is different from Ruby as there is
a strong desire in the C++ community to remove the freedom and find a
way to statically check duck types.

Matthew
 
R

Robert Klemme

Rick said:

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?
http://en.wikipedia.org/wiki/Strategy_pattern
http://c2.com/cgi/wiki?StrategyPattern

Are such patterns even worth
pursuing in Ruby?

Yes, but you have to adjust the implementation to the language at hand.
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?)

I really don't understand why you bring in modules here and seem to keep
insisting on them. Each strategy should be its own class IMHO. You
can, if you want to have some form of reuse or general behavior, give
them all a common superclass. But that's it.
You're saying one class is given another, configurable class, which is
configurable by giving a ConcreteStrategy object? Example? :(

See the Wikipedia page. "Context" in the UML diagram is your Company,
"Strategy" is your Processor base class (if you have it at all).
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.)

I guess *this* is your real problem: you need some more understanding -
once you get that all will be clear.

Kind regards

robert
 
W

William Crawford

Oliver said:
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.

I haven't seen anyone mention it, and I think it's important to know...
The name 'Duck Typing' comes from the saying "if it walks like a duck
and quacks like a duck, it must be a duck."
(http://peak.telecommunity.com/DevCenter/MonkeyTyping)

In short, if a type/class/whatever has all the methods of a duck (that
you intend to use), it's a duck and can be used as a duck would be used,
regardless of what's actually under the feathers.

In other words, TCPSocket doesn't just inherit from Socket, Object,
etc... It IS a Socket, it IS an Object, etc.
 
R

Rick DeNatale

I haven't seen anyone mention it, and I think it's important to know...
The name 'Duck Typing' comes from the saying "if it walks like a duck
and quacks like a duck, it must be a duck."
(http://peak.telecommunity.com/DevCenter/MonkeyTyping)

In short, if a type/class/whatever has all the methods of a duck (that
you intend to use), it's a duck and can be used as a duck would be used,
regardless of what's actually under the feathers.
Yes.

In other words, TCPSocket doesn't just inherit from Socket, Object,
etc... It IS a Socket, it IS an Object, etc.

While this is true, it's not a particularly good example to explain duck typing.

Duck typing is not about what an object "is", but what it can be used
for. Whether an object can be used for something depends on who is
using it.

Let's look at DT's example from the Pickaxe. Cutting the code down to
the bare essentials we've got:

def csv_from_row(op, row)
#code to compute res from row
op << res << CRLF
end

Now what kind of duck does op need to be? Simply an object which
accumulates objects via the << method.

His first use was:

result = ""
query.each_row {|row| csv_from_row(result, row)
http.write result

This works, Strings meet the requirements of the op parameter, but
they also have other characteristics. The important one here is the
subtle effect on GC. For a more detailed analysis of this see why's
article http://whytheluckystiff.net/articles/theFullyUpturnedBin.html

So let's try to find another object which we can put in the role of
the op parameter. Arrays also can be used. So we have

result = []
query.each_row {|row| csv_from_row(result, row)
http.write result

However we've now got a little problem in that although an array works
as the op parameter, http.write probably won't like it. But that's
easy to fix:

http.write result.join

Back when I was younger, and object[-oriented] programming was fairly
new, my friends and I spent a lot of time philosophizing about types
and objects. Although I don't remember talking about ducks, we did
talk alot about the theater. The most common word we used to use for
what we now call a duck type was a role.

So when we duck type, we're in a position similar to a director
casting for a role in a play. Sometimes, we find a great actor, but
we need to subtly adapt the script to fit his style. That's what's
happening in DT's pickaxe example.

Another way of looking at this, is that duck typing is akin to the way
the American President is chosen. An assessment is performed on the
candidates, and then the country and world adapts.

Contrast this to the way things are done in picking the next British
monarch, or the type systems of C++ or Java. In both cases one has to
be born into the role, having the right genes via inheritance. Of
course in the first case, who gets the job doesn't matter all that
much anymore.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top