On 2009-11-17, Marnen Laibow-Koser [email protected] wrote:
I didn’t say the same object – I said the same thing.
a = Stat.new(15, -2)
b = Stat.new(15, -2)
It doesn’t matter in the slightest whether a.object_id == b.object_id.
Regardless, a and b are the same thing to all intents and purposes –
and you can tell that, in part, precisely because the object_id is
irrelevant.
Perhaps, but (15, -2) and (14, -1) are also equal, even though they’re
obviously different things.
Two fives equal a ten. When I am talking about money, all I care about
is the net value, not the internal representation (coins or bills).
Unless
I have to deal with a vending machine.
But 99% of the time, 10==10 is the correct interpretation of a
comparison
between two fives and a ten.
call. You’re confusing the method with its return value. The
difference is subtle but important.
Hmm.
Oh, heyyyyy.
Okay, so one option would be:
def str
@str.to_i
end
In short, just return the value rather than the object I’m using to
model
it.
The problem is, I sort of want to be able to do things like:
john.str.add_modifier(“drunk”, -3)
Because really, the fact that str is a stat is part of the intended
published interface.
It’s just that, if you aren’t doing something uniquely stat-like to a
stat, you just want its integer value.
English: John’s strength, in the abstract, isn’t the same thing as
Mary’s strength in the abstract.
Ruby: john.method(:strength) != mary.method(:strength)
English: The current value of John’s strength happens to be equal to the
current value of Mary’s strength.
Ruby: john.strength == mary.strength
Does this make sense?
Yup.
Why? There’s no point to doing so as far as I can see. Even if it were
a Fixnum, it would already be yielding different objects every time it
changed. The client should make no assumptions about
john.strength.object_id.
Not as a specified interface that people should rely on, but rather, as
an
implementation choice – it seems crazy to me to regenerate whole
object-trees
frequently.
This sounds extremely expensive for an extremely commonplace thing. In
an ordinary turn of a game, I might have thirty or forty modifiers which
change, meaning that I’d be duplicating-but-modifying non-trivial object
trees thirty or forty times.
It may sound expensive, but it really is the best way of handling value
objects. This is how classes like Fixnum, Date, and BigDecimal work.
(Actually, Fixnum uses the Flyweight pattern under the hood; the others
might also.)
None of those classes are complicated trees containing five or ten or
thirty other objects, though, are they?
History of previous values (in some cases), and the modifiers aren’t
fixnums; they’re name/value pairs
Really? You’re not just doing
@modifiers = {:racial => 2, :class => 1, :armor => -1}?
Modifier is a class too, which can handle things like counting down its
duration, etcetera.
And a stat has a handful of them.
probably plus other traits such as
a duration (some expire, some don’t).
Does that really belong in the values itself? I’d tend to think not;
rather, that probably belongs in your event system.
It seems to me that the knowledge that a modifier expires is best
handled
by embedding it in the modifier.
But if you want history, then the StatValue object or whatever you wind
up using will probably have to be immutable anyway, so you can be sure
your history is accurate!
Ahh, but I don’t necessarily care whether it’s accurate, just whether I
have a record.
For a concrete example: A lot of games would have, say, a strength
stat,
and things that can lower your strength. Then you find a “potion of
restore strength”, which restores your strength to its highest previous
value.
To my mind, this is the kind of thing that should be handled entirely
by the stat object. It’s not john’s job to know what his highest score
was for a given stat; it’s the stat’s job to know what its highest score
was.
Then refactor your design at that time. Design for what you have now,
rather than predicting the future. Remember YAGNI.
In this case, though, I really do know that I need a fair bit of this.
The ability to damage and restore stats is pretty much essential to a
roguelike.
Since you don’t know yet, don’t design yet for them (your future plans
could easily change). Design a model that works for what you actually
have, and be ready to change it as necessary.
Yeah, but changing this into an immutable object is a lot MORE work. So
I’m not sure I should put extra work into making the object less
flexible
on the off chance that I won’t later not need to have done that work.
Largely correct, although I can think of exceptions. You’ll probably
want to break this down into a composed object containing one or more
value objects.
I think so.
No need. The former is a lot friendlier. You can always redefine
Character#strength= .
True.
That strikes me as silly. There’s no reason that the client class
should care whether your strength= method really does an assignment
operation.
True.
Hmm.
So yeah, I think at this point, I really do want a complicated composite
object, which happens to delegate to a value object which has traits
which
are useful for my purposes – in this case, almost certainly a fixnum.
-s