On 10/8/07, Trans [email protected] wrote:
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.
If they are in a module they are not limited by that constraint. I
haven’t lost anything at all by putting all behavior in modules, I
have only gained. The approach is a superset.
You’ve lost maintainability and clarity. You’ve lost any sense of good
design.
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.
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.)
I mostly do it as classes. I don’t screw around with modularizing
things that simply aren’t modules. Just because you CAN extract all
of the purpose out of a class and put it into a module just to
include it right back in doesn’t mean you SHOULD.
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.
You’ve created an artificial separation between forms of code
encapsulation because, you reason to yourself, one is for handling
state and one if for generic capabilities.
It’s not artificial. In Ruby, modules do not contain state. Period.
Only objects, which are instances of classes, contain state.
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.
The canonical examples of modules (Enumerable, Comparable) don’t deal
with state directly, but only through a defined interface that DOES
deal with state in an object. Transaction::Simple is a module because
it adds functions to objects that allow those objects to manipulate
state in different ways.
Transaction::Simple has generic applicability. Text::Format does not.
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.
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.)
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.
Classes defined by composing modules is bad practice. There are times
for doing things like this, but they are few and far between and
people who do it should be prepared to defend their reasons for doing
so because it smells like bad code.
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.
On 10/8/07, Trans [email protected] wrote:
library reference object. However when I want to use it for creating
distributable packages I mixin Distribution and Dependency modules.
Personally? I’d prefer the former. If I was dumb enough to design
something that required 50 attributes. Let’s pick on your example:
[snip layered redesign]
are improved because now my Package class doesn’t HAVE fifty attributes;
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