Fair enough, although that's only 50% wrong
No, it's 100% wrong. You said "the only reuse [is] via a subclass." Just
using the instance as an object is reuse (composition).
[snip]
modules are more versatile than classes. Thus by using all modules for
behavior (and classes as only compositions of modules) maximizes
reusability. And for this simple fact: classes cannot be mixed-in.
Wrong. Look VERY CAREFULLY at your code, Trans. How many of your modules
are used in more than one place?
If the answer is few, then you're doing it wrong and you're introducing
negative consequences in terms of maintainability, while not actually
increasing reuse. Most methods are specific behaviour for a given state.
Pretending that by extracting that into modules that youre increasing
reuse is nonsense. Unless you're actually reusing the code, you're not
increasing reuse.
You've lost maintainability and clarity. You've lost any sense of good
design.
Huh? It's a mess to divide these things into modules for mixin, but
its ok to divide them as classes and delegate? The point is to make
less mess by dividing things up into more manageable pieces. Those
pieces may prove nicely reusable, too, if well thought out, btw.
Yeah, it's much cleaner to think in terms of objects. It's much
preferable to compose than inherit. Delegation is only necessary when
you need to Act As something else.
But my example of extraction of the *whole* damned class was to make a
point about the limitations of class vs module reuse. For instance,
here's an example I didn't mention before. Did you know that if you
use all modules then AOP coding is very simple? It's easy to prepend a
module relative to other modules in the inheritance chain. But not so
for classes. Dynamic AOP for classes takes some mind-numbing meta-
coding for sure.
That's part of the problem, I guess: I don't think that AOP is
worthwhile in Ruby. AOP doesn't add any readability to the code, in the
end, than doing it a bit smarter. AOP only sort-of makes sense in Java
because Java's (otherwise) a static language. But even there, it's not
all that useful.
Of course I've seen some of your code, Austin. And you're quite a
capable coder. But your Ruby code has a very classic feel to it. Like
you were programming in C. You're very conservative and don't much
venture into the meta-coding deep. I'm not insinuating anything
negative in that, btw. I think it's good that there are all types of
coders. But you seem almost religious about your way of doing things.
That's because you're tone-deaf. I'm saying that from experience with
other people actually having to maintain code or having to maintain
other peoples' code, the way that you're doing things is nonsense and
reduces overall maintainability.
If you think my code looks like C, you'd be quite wrong; I simply don't
do metaprogramming where it isn't appropriate. (I have an experimental
branch of PDF::Writer that used quite a bit of metaprogramming to
encapsulate a number of rules that PDFs impose on objcts; if I ever pick
up PDF::Writer again, then I will probably be going from that
direction.) Most of the problems that I've solved haven't needed meta-
programming and I'm not going to inflict it on others just to show how
kewl I am.
There's also code that I have written that I can't show (it's
work-related) that does stuff within a particular domain that I would
never advocate in any other way. (I have one, in particular, that
overrides const_missing to return a string value representing the name
of the const just tried. The code where you'd use this expects that.)
Of course not. And of course I don't. But I wish Ruby didn't make me
have to choice one over the other. It reduces the flexibility of it's
otherwise extremely elegant inheritance model; and likewise makes
delegation the better choice in more cases than otherwise would be
necessary.
"Necessary" is a matter of opinion. To pick up on a different message
(to which I'll be replying to here, as well), I consider an object with
50 attributes an abomination. I don't care whether you've gotten those
attributes by mixins or manually writing them; it means you haven't
thought about your class design well enough. You seem to prefer flat
classes; I think that flat classes are a disaster in waiting.
[snip]
Ah yea, and "If God meant for men to fly, he would have given them
wings."
You're arguing that I have the problem, not Ruby. But I'm not arguing
Ruby has a problem, I'm arguing that maybe it could be even better.
Nor do I have a problem. I finished the program yesterday. I didn' run
into a limitation per-se, I ran in to a "why am I having to make an
ultimately pointless distinction?"
Yeah. You do have a problem and you *have* been arguing that Ruby has a
problem. If one is suggesting an improvement to something, it's because
one has hit a pain point. Sometimes those pain points are legitimate;
sometimes they're like the old joke:
Man: Doctor, it hurts when I put my arm behind my back.
Doctor: Well, don't put your arm behind your back.
Your example is precisely one of those situations. The distinction, by
the way, MAY be pointless. Inasmuch as it would encourage the sort of
design decisions you're making and have demonstrated in this discussion?
I'm more than happy to have it there, because if you're doing it, far
less capable programmers than you would make even dumber decisions from
it.
Now you're knit picking words. Of course only object instances have
state. But classes, and modules!, define objects.
I'm not nit-picking at all. It's the fundamental distinction between the
two right now. Classes can be turned into things that have state;
modules can't. Modules can only add features to objects.
Yep. A well defined interface is an important aspect of code reuse --
that's true for all forms of composition.
Not even related to what I said. Modules have some generic
applicability.
But that is putting a hard line in a soft world. There really is no
need to make that hard distinction. If a coder, like yourself, wants
to do that you can do so just as easily with a single form of
encapsulation....
[snip the rest of this nonsensical paragraph and the entire next
paragraph that has nothing to do with what I said]
Do NOT try to put words in my mouth.
What words? You always do this Austin. You makes some admonishing
remark and then never explain what your talking about. It's a very
deceptive conversational tactic. And I wish you'd cut it out. Explain
yourself. What words do you mean? What I have I said that is
nonsensical? And why do you think it's all in reference to what you
said. What about the point *I* trying to make?
"If a coder, like yourself, wants to ... with a single form of
encapsulation." You rambled on for two paragraphs as if I had said
something about a single form for development. Which I didn't. Never
have, never will. (In fact, it's pretty much *you* that's talked about
only using one form for development. I know you don't, but you're still
talking nonsense here.)
No. For years I have explored the language. Pushed up against it's
boundaries. Investigate what it can and can't do. I bring up ideas and
make suggestions to explore those further, not because I'm stuck, but
because I'm curious.
Really? If that's the case, then you really need to work on your
presentation skills, because you've yet to present something in a way
that says "I'm exploring this..." and instead nearly always say "Ruby
should do this..." while presenting examples that in every single case
could be coded easier and cleaner if you simply stepped back and thought
a little more. The example you opened this discussion with
(LinuxVideoPlayerApplication) is such BAD design that you SHOULD have
been able to see it.
Like I said, Ruby isn't perfect and there are things that can improve
about it -- and yes, you've pointed out a few of those areas. But when
it "hurts" to do something one way in Ruby, it usually means that you're
doing something suboptimally.
I'm forever learning. I didn't come from a C background and just
started writing my C program's in Ruby. I learned Ruby as Ruby. And I
try to use Ruby for what it is, and enjoy considering what more it
could be.
Nice misdirection and assumption of the martyr position.
Does this exploration lead me down rough roads sometimes? Damn right
it does. But in the end I'm a better coder for it. Am I the best coder
around here? Of course not, I don't pretend to be. But you sure do.
Again, nice misdirection. I don't pretend to be the best coder. I am,
however, a professional software developer that has to deliver a lot of
code on a regular basis. There are things that I know I wouldn't put up
with from people who worked with me if I had to maintain their code.
There are things that I don't care about.
Good gracious. You have so utterly missed the whole point I'm not sure
why I continue to type. What was my point Austin? Please, I want you
to put words in my mouth so I can see if there's actually any
communication going on here.
I haven't missed the point. You believe that modules are more flexible
than classes and that things would be better if you could mix classes in
the same way you can mix modules in, or at least if you could "properly"
instantiate modules. You may believe that, but in Ruby at least, you're
wrong.
[snip layered redesign]
There we go. It's now a properly composed object that doesn't mix in
multiple pieces of functionality into a flat access hierarchy. I now
have a Package::Version *class* to reuse elsewhere if I want, and it's a
full-on black box. I don't have any worries that Package::Version the
class will have any instance variables that conflict with another item
because I used a module.
It took me all of thirty seconds to fix your design and make it
class-oriented rather than module-oriented. My objects are now more
usable as a whole and the overall interface substitution capabilities
are improved because now my Package class doesn't HAVE fifty attributes;
it's got FIVE. The Distributable package is properly specialized, too.
It might be possible to use a module to extend a package object into a
distributable package object rather than a subclass, but I'm not sure
that the design for that is a good one.
Because each of my classes is smaller, my code is likely to be
significantly more robust, and I have functionality that I can test
independently of the whole Package class without trying to mix it
into something different.
That's what I mean, Trans: your design approach for mixing in 50
attributes or writing 50 attributes is bad. If you need that many,
your object is PROBABLY way too big. (Yes, there are times when such
things are necessary; they're extremely uncommon, though.)
Fantastic jobs Austin. Now initialize the class from a YAML file for
me. Did I mention the file spec looks like this:
[...]
It has some 35+ possible entires and some of those have aliases
besides (because I like giving my users some flexibility). Plus it
should be extensible because other utilities (like the distributable
packaging tool) will need additional information.
Come on, show the dumb-dumb how it's *properly* done.
class Package
def self.from_yaml(yaml)
pkg = Package.new
YAML.load(yaml).each do |key, value|
case key
when 'package' then # ...
when 'version' then # ...
when 'created' then # ...
when 'homepage' then # ...
when 'devsite' then # ...
when 'authors' then # ...
when 'description' then # ...
when 'libpaths' then # ...
else
# save the unknown pairs somewhere so that subclasses can deal
# with them, too.
end
end
end
end
Just because your storage representation is flat doesn't mean your
in-memory object has to be. (And you can define your own #to_yaml on the
Package class that flattens things for saving.)
This is no different than what I wrote for Ruwiki four years ago (I
eschewed YAML because Syck was broken in a couple of different releases
of Ruby.)
-austin