Why? Convince me, because as someone who has done my share of design
work, I don't see immmutability as a positive on most things. On
primary key references, yes (e.g., a customer's identification
number should never change or be changed in any way), immutability
is good. But as a general rule? Immutability isn't necessarily what
you want.
(As a point of note, in my Ruby, I don't think that I've *ever* used
#freeze. In the billing design work that I did, none of it depended
upon immutability of the objects -- the only immutable things in my
code and design were *constants*.)
The "need" for immutability is very, erm, mutable and can be worked
around with various design decisions.
I agree- good interface design effectively creates immutable classes
if desired. But that can be an issue because the object may only
desire immutability later in its lifecycle, or too certain clients.
Also, good design is an ideal case. In a large project, things can
become ugly and to be able to enforce a high-degree of control via
immutable objects can be beneficial, IMHO. Good design requires good
contracts to enforce it.
There are three things that make up an immutable object so far in our
discussion:
1. frozen objects
-this could also be done with good interface design for
certain cases. other alternatives to freeze would be useful to know-
wrap/delegate, undeffing, etc.
2. frozen attributes (assignmentFreeze)
-mostly appplies to inheritence, so private instance variables
would help here.
3. frozen value objects (referred to objects of a certain type)
-not really discussed yet, but seems important as without it,
freeze is only slushy.
And of course the question, why do you need it.
It's hard to describe the benefits of immutables in a short example,
but I think it's similar in nature to global variables- with
mutables, edits can be done in many places, leading to bugs as well as
maintenance issues.
That's why I mentioned singletons. They often raise the desire for
immutables since they facilitate uncontrolled access and program flow-
if you can't get to something, you can't change it. With singletons,
there is often too much coupling, which makes the issue of mutability
more pronounced. It's too easy for isolated pieces of code to break
with appropriate responsibility and assume too much knowledge of the
domain object.
For instance, in an asset manager:
asset=AssetManager.getAsset("id=1")
asset.value="10"
Being embedded in gui code.
With the singleton, this kind of code can be sprinkled throughout the
code. Bad design, sure. But this is often an issue where there is
gui-app logic bindings in different dialogs, tables, etc.
Of course, a better domain class can help.
class Asset
def initialize name, location, value
@name=name
@location=location
@value=value
end
attr_reader :name, :location, :value
end
This is essentially what I do with Java- creating interfaces that
present an immutable interface to the domain object. But that doesn't
work with duck typing. So in Ruby, it needs to be part of the class
interface, and the problem with that is that I may need to have more
write access to an object early in it's lifecycle. I suppose wrappers
or delegates are an option here, and that's not too hard to implement
in a nice way given Ruby's nature. Or even undefing methods. Any
suggestions welcome.
As a side note though, even with freezing, the above business object
is not immutable...
a=Asset.new "laptop", "home", 500
a.location << " is where the heart is" # regardless of a being frozen or not.
If an object should protect it's invariant nature, then it would be
nice for ruby to provide some capability for immutable value objects-
maybe something like:
value_reader :name, :location, :value # returns defensive copies
Some on the concept of value objects vs. reference objects:
http://c2.com/cgi/wiki?ValueObject
http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
You assert the value of immutability, but you haven't actually
demonstrated the value. I'm really not trying to be difficult here,
but what sort of immutability to you mean in Java -- and why do you
then need it in Ruby? I find that most of the time when people say
that they want particular features in Ruby they do so because
they're not *thinking* in Ruby, but in other languages that they
have to deal with on a daily basis to pay the bills. I know I do it,
from time to time, with C++ now that I'm making my money from that.
I'm still learning to "think" in Ruby for sure, but the essence of the
need comes from non-trivial systems without a perfect design.
Removing singletons and making certain domain objects immutable is
somewhat like putting aspects of the "Law of Demeter" into place.
http://c2.com/cgi/wiki?LawOfDemeter
Another way of thinking about is in terms of object lifecycle- if it
becomes immutable at some point, there is less to worry about. In a
large system, I would freeze and object just prior to releasing into
the "wild"- e.g. outside the boundaries of my subsystem.
Um. What do you mean by "attributes"? If you mean that which is
generated by attr_accessor, then:
class A
attr_accessor :foo
private :foo, :foo=
end # => A
class B < A; end # => nil
B.new.foo = :bar
# => NoMethodError: private method `foo=' called for
# #<B:0x2b52c38>
I meant the case where the instance variable
@foo is accessed in side
the class B.
If you mean instance variables, no, and that wouldn't work anyway --
it would simply be a source of errors.
class A
attr_accessor :foo
private_var
foo
end
private_var is a concept?
class B < A
def foo=(x)
@foo = x
end
end
Logically, this shouldn't cause a problem, because even if @foo in A
is private, then B should still be able to have it's own @foo.
Maybe there's a way around this -- and this could be potentially
very useful -- with something like:
module A
def foo=(x)
@<foo> = x
end
end
If Ruby mangles @<foo> to be something like @__A_foo__, then you can
I don't know what itsme really wants, because I think that the idea
of making variable bindings -- instance or otherwise -- frozen is a
bad idea. Freezing an object freezes that object's state -- which
includes assignment to instance variables, certainly, but also
inclues adding singleton methods, modifying the singleton object,
extending the object, etc.
I think private variables would alleviate much of the need. That would
allow a better contract for inheritence, and that was largely the
interest in my first post.
Other issues requiring freezing probably point to an interface that
exposes too many internals - e.g. use of attr_accessor or a class that
has too much responsibility or is simply too big. There is still the
issue of value objects. Any thoughts on that?
http://c2.com/cgi/wiki?ValueObject
http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
No, there isn't such built into Ruby. You could write one, but I
don't think it's a good idea.
Agreed- unless needed for a particular case.
Regards,
Nick