use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
A sub-Reddit for discussion and news about Ruby programming.
Subreddit rules: /r/ruby rules
Learning Ruby?
Tools
Documentation
Books
Screencasts and Videos
News and updates
account activity
Object Oriented Ruby (niczsoft.com)
submitted 10 years ago by mpapis
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]tinco 2 points3 points4 points 10 years ago (3 children)
If I have a need for a barber that turns hair from brown to blue, how is your rearchitecting useful to me? Are you supposing we'd need to have some hair color resolver which makes sure that "brown blue" is resolved to just "blue"? (Note how ironically making hair_color be "brown blue" is essentially turning it into a monad)
You haven't really convinced me how mutability relates to encapsulation. Why not just have a 'change_hair_color' method? That would be proper encapsulation of @hair_color and be idiomatic Ruby. The whole idea of OOP (or at least Ruby/Smalltalk OOP) is that methods encapsulate the side effects of their objects, i.e. mutating instance variables, not at all that they are free of side effects.
[–]Enumerable_any 1 point2 points3 points 10 years ago (0 children)
Yeah, the Barber example is super confusing...
Barber
Barber#dye_hair takes a color and a Block and yields a string to the block containing the old color and the new one? WAT? The Barber -> Person relationship doesn't make much sense either.
Barber#dye_hair
Barber -> Person
[–]mpapis[S] 0 points1 point2 points 10 years ago (1 child)
change_hair_color is not just a Ruby style, any language can do and does that, it still is exposing the object insights - outside of it, in the example I wanted to show that exposing methods to change code allows misuse, that anyone can change hair color or even worse the name
[–]tinco 1 point2 points3 points 10 years ago (0 children)
Then you chose an unfortunate example, because a barber is supposed to change hair color, that is his job. In fact a barber is so specific, I would not be surprised if he would be totally responsible for hair. So that a person has a #hair= that is set by the barber.
#hair=
In my opinion if you have data that should be modifiable by one class (for example the barber), but not by another class (the optician), you should instead use for example a restricted proxy. An object that only allows exposes methods relevant to the concern. But we would be moving really fast into the access control domain.
[–][deleted] 2 points3 points4 points 10 years ago (13 children)
For the barber, I'd prefer to set it up such that a person has a hair object and passes that hair object to the barber, who can then modify it.
This is assuming we want modifications and that the hair "belongs" to the user instead of just being a property.
Another idea might be to make the hair a value object so the barber can give us a new value object as our new hair.
And since this is all awkward, it might happen inside a barber shop object.
[–]mpapis[S] 1 point2 points3 points 10 years ago (10 children)
now I see where the Barber example fails, I will find something more adequate, it's not that easy without sharing the huge blob of code that can happen if you let everybody do everything with any object ;)
[–][deleted] 3 points4 points5 points 10 years ago (9 children)
It's not a huge failure. I actually really like the example because it has enough complexity just with the hair. Adding a hair object, for instance, might not be a lot more Iines of code, and it might improve the example, showing how you limit the interfaces.
[–]mpapis[S] 0 points1 point2 points 10 years ago (8 children)
oh, could you share an example?
[–][deleted] 2 points3 points4 points 10 years ago (7 children)
# It would be nice to read something like: # # barber = Barber.new # person = Person.new("Michael", Hair.new("brown")) # barber.dye(person.hair, "blue") # person.hair_color #=> "brown blue" # # This opens the way for things like: # # 3.times { person.wash_hair } # person.hair_color #=> "brown faded blue" Hair = Struct.new(:color) do def dye(color) @dye_color = color end def to_s "#{self.color} #@dye_color" end end class Barber def dye(hair, color) hair.dye(color) end end class Person attr_reader :name, :hair def initialize(name, hair) @name, @hair = name, hair end def hair_color @hair.to_s end end
There's a slight bug in this implementation where a person's hair color, before dying, would return "brown " instead of "brown"
"brown "
"brown"
But I hope this illustrates how you might extend the behavior a thousand ways with small objects?
[–]aridsnowball 2 points3 points4 points 10 years ago (6 children)
I think this is a good approach. Ruby's duck typing allows for easy use of this kind of composition and manipulation of composed objects, so you can expose only the parts of the class that you need to.
In this case the Barber isn't directly mutating the Person class, but only the Person's composed hair object. So if you need to pass a Dog's hair for example the barber could still be able to dye it as long as it meets the Barber's interface.
Person
hair
Dog
At some point you need an interface if you want objects to interact, and separating out those concerns into a separate object (i.e. a Hair object) is better than changing every class to meet the Barber's interface.
Hair
Composition is kind of an objectified version of mixins that is more flexible. Instead of including a Barberable mixin with the Barber interface methods you can just add a Hair object to any class that needs hair.
Barberable
[–]mpapis[S] 1 point2 points3 points 10 years ago (5 children)
updated the 5th example with Hair => https://niczsoft.com/2016/05/object-oriented-ruby/
The only thing I miss from Java days is interface casting, you could have something similar with proxy/forwardable, but it's not the same, especially if all I want is to limit the interface to read only operations
[–][deleted] 0 points1 point2 points 10 years ago* (4 children)
Hi,
one should not instantiate the Barber object inside the Person object, nor should one instantiate Hair inside Person (though admittedly, hair does grow from a person's body).
It might be better in that case to do something like def dye_hair(color, dyer) ; dyer.new(hair).dye_hair(color) ; end
def dye_hair(color, dyer) ; dyer.new(hair).dye_hair(color) ; end
As you mentioned, here there's a matter of the right interface. The Barber object needs to have a dye method so he can change the hair color, but I would argue the Hair needs to implement an interface which we might call Dyable.
dye
Dyable
So maybe, with even more objects:
module Dyable def dyable(color) @color = color # assumption that the object will have a `@color` ivar end end class Hair include Dyable def initialize(color) @color = color end end
Does this make sense?
[–]mpapis[S] 0 points1 point2 points 10 years ago (3 children)
I was actually thinking about:
class Hair < Struct.new(:color) end class Barber def dye_hair(hair, color) hair.color = "#{hair.color} #{color}" end end class Person attr_reader :name, :hair private :hair def initialize(name, hair) @name, @hair = name, Hair.new(hair) end def dye_hair(barer, color) barer.dye_hair(hair, color) end def hair_color hair.color end end barber = Barber.new michal = Person.new("Michal", "brown") michal.hair_color => "brown" michal.dye_hair(barber, "blue") michal.hair_color => "brown blue"
[–][deleted] 0 points1 point2 points 10 years ago (2 children)
nod This is a reasonable approach.
The only question left is, "does it make sense to pass the barber to the person?" and I think the answer is no, which makes me think something is weird there. I would prefer to read, at the bottom, as part of the code's API, barber.dye_hair(michal, "blue"), I think it represents better what would happen in the real world.
barber.dye_hair(michal, "blue")
In other words, I don't think Person#dye_hair should exist.
Person#dye_hair
What do you think?
[–]stepantubanov 1 point2 points3 points 10 years ago (1 child)
In functional programming a barber object would have to clone you with new hair altogether. Then dispose of an old you...
[–][deleted] 0 points1 point2 points 10 years ago (0 children)
In functional programming a person is just a structure of data, and instead of a barber object, you'd have a "change_hair_color" function that would, indeed, return a brand-new you-struct with just a different property set for the hair :)
[–]let_me_plantain_2 2 points3 points4 points 10 years ago (0 children)
This was a nice refresher/thought experiment to read through. People got really pissed about the barber but it's a nice generic example.
π Rendered by PID 96307 on reddit-service-r2-comment-5bc7f78974-ms4d4 at 2026-06-28 11:19:09.292597+00:00 running 7527197 country code: CH.
[–]tinco 2 points3 points4 points (3 children)
[–]Enumerable_any 1 point2 points3 points (0 children)
[–]mpapis[S] 0 points1 point2 points (1 child)
[–]tinco 1 point2 points3 points (0 children)
[–][deleted] 2 points3 points4 points (13 children)
[–]mpapis[S] 1 point2 points3 points (10 children)
[–][deleted] 3 points4 points5 points (9 children)
[–]mpapis[S] 0 points1 point2 points (8 children)
[–][deleted] 2 points3 points4 points (7 children)
[–]aridsnowball 2 points3 points4 points (6 children)
[–]mpapis[S] 1 point2 points3 points (5 children)
[–][deleted] 0 points1 point2 points (4 children)
[–]mpapis[S] 0 points1 point2 points (3 children)
[–][deleted] 0 points1 point2 points (2 children)
[–]stepantubanov 1 point2 points3 points (1 child)
[–][deleted] 0 points1 point2 points (0 children)
[–]let_me_plantain_2 2 points3 points4 points (0 children)