Strategy pattern / interface design arrangement

(Daniel Waite) #1

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

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-forum.com/.

(snacktime) #2

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

路路路

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

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

(Robert K.) #3

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

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

路路路

On 21.08.2006 07:55, Daniel Waite wrote:

(Ilan Berci) #4

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-forum.com/.

(Daniel Waite) #5

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

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! :slight_smile:

路路路

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

(Kenosis) #6

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

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! :slight_smile:

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

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

(Rick DeNatale) #7

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

路路路

On 8/21/06, Daniel Waite <rabbitblue@gmail.com> wrote:
--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

(Daniel Waite) #8

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

路路路

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

(Oliver Bandel) #9

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-typing-story

), 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

(Daniel Waite) #10

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

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

路路路

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

(Gavin Kistner) #11

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

(Robert K.) #12

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?


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.

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

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.

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

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

路路路

On 22.08.2006 02:00, Daniel Waite wrote:

(William Crawford) #13

Oliver Bandel wrote:

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.

路路路

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

(Daniel Waite) #14

Phrogz wrote:

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! :slight_smile: :slight_smile: :slight_smile:

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!

路路路

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

(Kirk Haines) #15

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

路路路

On Tue, 22 Aug 2006, Phrogz wrote:

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.

(Oliver Bandel) #16

Phrogz wrote:

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-typing-story

[...]

This kind of texts has a lot of words but it makes the things not really
understandable. Maybe because I don't see the problems as problems,
because I look different on these things.

So I like to hear that short 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.

OK, if that's correct, then duck-typing is different from FPL's
approach.

When you have the same types in a functional setting, e.g. with OCaml, then you can put together the functions, even if they have different names. As long as the type is the same, you can use them. The name can be the same but also be different.

In OO there muste be the same names also.
But what if the name is the same but not the type?
It can bring a lot of chaos....

Ciao,
聽聽聽聽Oliver

(Rick DeNatale) #17

Oliver Bandel wrote:
> 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.

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.

路路路

On 8/22/06, William Crawford <wccrawford@gmail.com> wrote:

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

(Eric Mahurin) #18

So I like to hear that short 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.
>

OK, if that's correct, then duck-typing is different from FPL's
approach.

Strangely enough, C++ has something just like duck-typing. They call a
duck-type a "concept":

http://www.sgi.com/tech/stl/stl_introduction.html
Concepts and Modeling

One very important question to ask about any template function, not just
about STL algorithms, is what the set of types is that may correctly be
substituted for the formal template parameters. Clearly, for example, int*or
double* may be substituted for find's formal template parameter
InputIterator. Equally clearly, int or double may not: find uses the
expression *first, and the dereference operator makes no sense for an object
of type int or of type double. The basic answer, then, is that
findimplicitly defines a set of requirements on types, and that it may
be
instantiated with any type that satisfies those requirements. Whatever type
is substituted for InputIterator must provide certain operations: it must be
possible to compare two objects of that type for equality, it must be
possible to increment an object of that type, it must be possible to
dereference an object of that type to obtain the object that it points to,
and so on.

Find isn't the only STL algorithm that has such a set of requirements; the
arguments to for_each <http://www.sgi.com/tech/stl/for_each.html> and
count<http://www.sgi.com/tech/stl/count.html>,
and other algorithms, must satisfy the same requirements. 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.

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. Nevertheless, concepts are an extremely important part of the STL.
Using concepts makes it possible to write programs that cleanly separate
interface from implementation: the author of find only has to consider the
interface specified by the concept *Input Iterator*, rather than the
implementation of every possible type that conforms to that concept.
Similarly, if you want to use find, you need only to ensure that the
arguments you pass to it are models of *Input Iterator. *This is the reason
why find and reverse can be used with lists, vectors, C arrays, and many
other types: programming in terms of concepts, rather than in terms of
specific types, makes it possible to reuse software components and to
combine components together.

(Matthew Johnson) #19

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