
The Gang of Four book is wrong about delegation (2012) - Tomte
http://saturnflyer.com/blog/jim/2012/07/06/the-gang-of-four-is-wrong-and-you-dont-understand-delegation/
======
steego
I'd hate to be the Gang of Four, especially since they wrote their book 20
years ago. Since then numerous people (including myself) have discussed how to
evolve some of their ideas given today's landscape.

My position is many ideas are still valid in their book, but they are less
novel today and are often implemented with simpler tools. I'm sure the Gang
would concur.

Other ideas may be less valid over time and I'd be surprised if the team
didn't grow in their perspective.

~~~
eropple
I forget who said it, but I ascribe to the notion that most design patterns
exist to work around insufficiencies in languages or ecosystems. To that end,
a lot of them totally still exist--they've just been reduced in difficulty and
ceremony, as you mention. The command pattern, for example, is literally just
a closure.

~~~
Denzel
You're probably thinking of when Peter Norvig said: "Design patterns are bug
reports against your programming language."

------
humanrebar
> And C++ can't do delegation

Sure it can. The syntax is a little different, though. And there are a few
ways to do it. Here's one:

    
    
        #include <iostream>
        #include <functional>
    
        struct Container {
            template <typename Self>
            void announce(Self const & self) const
            {
                std::cout << "these are my things: "
                          << self.things << '\n';
            }
        };
    
        struct Bucket {        
            std::string things;
            std::function <void()> announce;
    
            template <typename Delegate>
            explicit Bucket(
                Delegate const & delegate,
                std::string const& aThings) :
                    things(aThings),
                    announce([&](){ delegate.announce(*this); } )
            { }
        };
    
        int main()
        {
            // Container works with anything with a 'things'
            // attribute
            Container container;
            // Bucket takes anything with an announce method
            // that accepts a Bucket-like object
            Bucket bucket(
                container,
                "planes, trains, and automobiles"
            );
            bucket.announce();
            return 0;
        }

------
favorited
> If the message is delegated further, all questions about the values of
> variables or requests to reply to messages are all inferred to the object
> that delegated the message in the first place.

I'm not sure how the author jumps from that quote to:

> When that object delegates to another, then any reference to "self" always
> refers to the original message recipient. Always.

The high-order bit is that the _delegating_ object can be interrogated for
more information or context (and a reference to that object can be forwarded
to further delegates), no? Why does that need to be implemented by re-binding
whatever keyword or name an object calls itself to a different object?

~~~
galaxyLogic
That is probably because what he and we are talking about here is the
technical definition of "delegation".

If you just call a method of another object and then that object perhaps but
not necessarily sends some messages back to the original sender, to get
further information, that could conceptually be seen as "delegation". But
technically that's just normal (bi-directional) message-exchange.

"Delegation" needs to refer to something more technically specific to warrant
its existence as a technical concept.

~~~
josteink
For languages which didn't have function-pointers as a first-class construct
(like Java), you had to create another way to delegate functions between
different objects. A way to encapsulate them, if you like.

It may not seem novel now, but it was not obvious to everyone at that time
that this was a possible design-pattern.

------
grandalf
I think the example gets bogged down in language features which obscures the
conceptual idea.

Hence I think the example would best be illustrated using pseudocode, which
would perhaps motivate a meaningful discussion of how language features make
certain patterns more or less useful, and perhaps, zooming out, certain
designs more or less easy to reason about.

------
galaxyLogic
Perhaps a better way to explain "delegation" would be to say that it means
"borrowing a method from some other object then executing it in the context of
the borrower.

In O-O the "context" in particular refers to the "message recipient" available
as the value of the pseudo-variable 'this' (or 'self' in Smalltalk).

To delegate = To borrow a method. Delegator = Borrower. Delegate = Lender.

------
norswap
The Gang of Four book is terrible. The explanation are very unclear (often
because of excessive abstraction, even in the description and discussion).

It's been a long time so my memory is hazy on its particular sins, but I've
vowed never to touch it again. I never understood what anyone saw in that book
(except that it was one of the first book on the topic of design patterns).

~~~
simon83
I always thought I was too dumb to understand the Gang of Four book. I thought
everyone else must be so much smarter than me, because everyone praised this
book. I really had trouble getting a clear picture in my head of the ideas,
concepts and how it all fits together, due to the, as you say, excessive
abstraction.

I guess it was mostly due to my inexperience in software design in general. I
haven't tried to read it again since then, but I'm sure by now I'd at least
understand some of the words in there :) So I wouldn't recommend it for
beginners, but I think even experienced developers could still learn something
new.

~~~
mcv
I had the same thing. Fortunately there are other books that explain design
patterns in a clearer way, but I still didn't quite see the point at the time.
Clearly design patterns are important, and I should master them, right?

But then I switched from Java to Ruby, and half of the design patterns were
completely irrelevant. And I had other problems for which I didn't have
appropriate design patterns.

I think the Gang of Four book's target audience is far narrower than it's
often been interpreted. It's meant for intermediate C++ programmers. People
who have already run into a number of these problems that C++ doesn't really
handle well, and now here's a book that helps you around those limitations.

I guess it also works well for older Java versions, but it's not universal.
Many patterns are completely irrelevant for some languages, because those
languages have easier ways to do that. Many languages have other shortcomings
that are not handled in that book.

Maybe there's another target audience for that book: language designers.
Design your language so nobody needs these patterns. Have built-in constructs
that handle this stuff in an easy way for the programmer.

------
watwut
I guess that the meaning of the word delegation changed between 1986 and now,
definitely in the context of languages that are not prototypal.

I have heard the word delegation as in "this object methid here just returns
value somebody else calculated" so many times, that I am pretty confident
everybody would be just confused if I started to use different terms.

------
vendiddy
From the article, I understand what delegation _is_ but I don't get why it is
useful.

Anyone understand the concept?

~~~
mcv
My interpretation is: it's just prototype inheritance. Delegation is a way to
do inheritance. That's it.

------
machinedgod
Whole OOP is "wrong". It was "right" up to about early 90ies, given the
average application size and hardware. The moment CPU's started scaling with
cores and CPU started running hundreds of instructions per memory read, and
applications started being bigger than few tens of KB - cornerstones of OOP
(dynamic polymorphism, code attached to data, per-object access control)
started actively working against developers.

~~~
deorder
It is not "wrong", but it is being "overused". I had been fixing people their
broken OOP code for about 10 years (diamond problem, spaghetti code, too much
coupling etc.). People seem to abuse it in every way they can. I personally do
not like how code is often not separate from the data as well.

That is why i like C. I can have my data structure and just add functions that
can operate on those data structures outside of the data structure itself
without worrying about coupling code to the data. Extra functions that can
operate on the data may as well be inside a dynamically loaded plugin. Not
feeling forced to put everything that can operate on a single instance of the
data inside the same class. Not feeling forced to create a separate class to
add methods that can operate on multiple instances of the data and hardcoding
multicore support inside of it.

The new C++ (and Nim) have the notion of concepts which can mostly replace the
many ways OOP was being abused. It is similar to type classes in Haskell. I
advice you to read Alexander Stepanov's books.

You can have a data structure in a class, add certain concepts that the data
structure supports and then create separate functions which can operate on
these concepts outside of the class / object.

~~~
machinedgod
I'm not sure I'd agree with initial statement (and by this I literally mean -
"I am not sure", its not a figure of speech).

I agree with everything else you said, but the way I see it - when a paradigm
cornerstone itself starts getting in the way of code organization, then that's
a clear sign that paradigm itself is 'wrong', or more precisely, wrong when
applied to this set of problems.

Oooooooh, look at that, I understand now what you meant :-D

Regarding your second paragraph - this is, looking from efficiency standpoint,
the best approach. Additional gain is in access control: it becomes flat (ie.
module-controlled), rather than having a class hierarchy in between, and then
using messy constructs such as interfaces/mixins to achieve both code and type
inheritance. Raise hands if you ever ended up in situation where you have to
convert from one type to another, while the actual _data_ they carry are
precisely the same. Raise hands if you ever had to pollute an interface or a
parent class with extra data, because a subclass somewhere contains exact data
needed at the other part of the chain. This is friction - and it works against
the developer/team. The larger the software, the worse it gets; it doesn't
have to be that way.

Anyways, regarding last two points - yes, I'm very familiar with parametric
polymorphism (it is a sole reason I use C++ for work, over C), and I have than
half a decade of Haskell experience behind me. Trying to shift into Rust
lately - its easier to find jobs.

