# General Ruby OOP question

**URL:** <https://www.ruby-forum.com/t/general-ruby-oop-question/190412>\
**Category:** Ruby\
**Created:** [June 19, 2010, 3:56pm UTC](https://www.ruby-forum.com/t/general-ruby-oop-question/190412 "2010-06-19T15:56:08Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![judge](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/9de0a6/32.png) [@judge](https://www.ruby-forum.com/u/judge)\
**Post date:** [June 19, 2010, 3:56pm UTC](https://www.ruby-forum.com/t/general-ruby-oop-question/190412/1 "2010-06-19T15:56:08Z")

</div>

Hello,

I have a general question about OOP with Ruby. If I have 2 class: Home  
and Person for instance, such as:

> > class Home  
> > def initialize  
> > @person1 = Person.new  
> > @person2 = Person.new  
> > # …  
> > end
> > 
> > def open\_a\_window; end
> > 
> > # …
> > 
> > end

> > class Person  
> > def play\_wii\_game; end  
> > end

Then, a home instance can include a lot of people. And that’s cool. And  
a home instance can apply action on a person using a method just like:  
@person2.play\_wii\_game

But if a person want to open a window in its home? Here, with a such  
design it’s impossible, isn’t it.

So to allow this kind of action, I think we need to pass as parameter  
self inside Home initialization process, becoming:

> > class Home  
> > def initialize  
> > @person1 = Person.new(self)  
> > @person2 = Person.new(self)  
> > # …  
> > end
> > 
> > def open\_a\_window; end
> > 
> > # …
> > 
> > end

> > class Person  
> > def initialize(home)  
> > @home = home  
> > end
> > 
> > def open\_a\_window  
> > @home.open\_a\_window  
> > end
> > 
> > def play\_wii\_game; end  
> > end

Now, a instantiated person can do it using open\_a\_window proxy method.  
And any other proxifyable home’s methods.

But is it ethics? I mean, is that lawful under the principles of  
object-oriented programming. Maybe this is so much power, to pass self  
inside person instance inside its home… 'cause it’s almost as if there  
was no class, no partitioning.

What do you think about this?  
Thanks for any considerations.

---

<div class="post-metadata">

**Author:** ![judge](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/9de0a6/32.png) [@judge](https://www.ruby-forum.com/u/judge)\
**Post date:** [June 19, 2010, 5:34pm UTC](https://www.ruby-forum.com/t/general-ruby-oop-question/190412/2 "2010-06-19T17:34:38Z")

</div>

On Jun 19, 9:56 am, “Paul A.” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > > end  
> > > @person2.play\_wii\_game  
> > > def initialize  
> > > @home = home  
> > > And any other proxifyable home’s methods.
> 
> But is it ethics? I mean, is that lawful under the principles of  
> object-oriented programming. Maybe this is so much power, to pass self  
> inside person instance inside its home… 'cause it’s almost as if there  
> was no class, no partitioning.
> 
> What do you think about this?  
> Thanks for any considerations.

It’s fine. But why is Home creating people? Perhaps they should be  
added to a home? In which case you could create a home and then a  
person with a home and the person would be automatically added to that  
home.

home = Home.new  
person1 = Person.new(home1)

Or you could create both the home and person separately but when you  
add the person to the home they will pick up a reference to it.

home = Home.new  
person1 = Person.new  
home \<\< person1

In Home:

class Home

```
def <<(person)
  @persons << person
  person.home = self
end

```

~trans

---

<div class="post-metadata">

**Author:** ![judge](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/9de0a6/32.png) [@judge](https://www.ruby-forum.com/u/judge)\
**Post date:** [June 19, 2010, 5:38pm UTC](https://www.ruby-forum.com/t/general-ruby-oop-question/190412/3 "2010-06-19T17:38:09Z")

</div>

On Sat, Jun 19, 2010 at 4:56 PM, Paul A. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> But is it ethics? I mean, is that lawful under the principles of  
> object-oriented programming. Maybe this is so much power, to pass self  
> inside person instance inside its home… 'cause it’s almost as if there  
> was no class, no partitioning.

There is nothing wrong from an OOP point of view with one object  
containing a reference to another object. Even in the real world, it  
makes sense that a person knows where their home is.

In some languages, like C++, this can introduce some typing and API  
complications, but not in Ruby where methods are invoked by sending  
messages to the objects.

The problems that you might have with this approach are related to  
design requirements. For example, what if one person can, optionally,  
have more than one home?

Ammar

---

<div class="post-metadata">

**Author:** ![judge](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/9de0a6/32.png) [@judge](https://www.ruby-forum.com/u/judge)\
**Post date:** [June 19, 2010, 6:24pm UTC](https://www.ruby-forum.com/t/general-ruby-oop-question/190412/4 "2010-06-19T18:24:24Z")

</div>

On Sun, Jun 20, 2010 at 12:37 AM, Ammar A. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> In some languages, like C++, this can introduce some typing and API  
> complications, but not in Ruby where methods are invoked by sending  
> messages to the objects.
> 
> The problems that you might have with this approach are related to  
> design requirements. For example, what if one person can, optionally,  
> have more than one home?

I think I’d model it somewhat like this (untested):

class Window  
attr\_accessor :open  
end

class Home  
attr\_accessor :window

def initialize  
@window = Window.new  
end  
end

class Person  
attr\_accessor :location

def enter(location)  
self.location = location  
end

def open\_window  
if location.window.respond\_to?(:open=)  
location.window.open = true  
else  
raise(“The location doesn’t have any windows you can open”)  
end  
end  
end

guy = Person.new  
home = Home.new  
guy.enter(home)  
guy.open\_window

---

<div class="post-metadata">

**Author:** ![judge](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/9de0a6/32.png) [@judge](https://www.ruby-forum.com/u/judge)\
**Post date:** [June 19, 2010, 6:30pm UTC](https://www.ruby-forum.com/t/general-ruby-oop-question/190412/5 "2010-06-19T18:30:51Z")

</div>

On Sat, Jun 19, 2010 at 8:56 AM, Paul A. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > > end  
> > > a home instance can apply action on a person using a method just like:  
> > > @person1 = Person.new(self)  
> > > @home = home  
> > > And any other proxifyable home’s methods.

Plus, you get a nice little benefit in that the Person#open\_a\_window  
method  
can contain the conditionals for dealing with the edge case of when  
there is  
no home.

class Person  
def open\_a\_window  
@home.open\_a\_window if @home  
end  
def has\_home?  
!!@home  
end  
end

Then later you can say things like  
person.open\_a\_window if person.hot?

rather than having to cart the exception logic all over your  
implementaion  
person.open\_a\_window if person.hot? && person.has\_home?

Though, I suppose, it is a rather limited model in that your person may  
be  
at work, or at a friend’s home.

---

<div class="post-metadata">

**Author:** ![judge](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/9de0a6/32.png) [@judge](https://www.ruby-forum.com/u/judge)\
**Post date:** [June 19, 2010, 8:15pm UTC](https://www.ruby-forum.com/t/general-ruby-oop-question/190412/6 "2010-06-19T20:15:38Z")

</div>

On 2010-06-19 09:20:26 -0700, Michael F. said:

> def open\_window  
> if location.window.respond\_to?(:open=)  
> location.window.open = true  
> else  
> raise(“The location doesn’t have any windows you can open”)  
> end  
> end  
> end

This is a Law of Demeter violation. The home should be responsible for  
opening its own windows.

This code makes assumptions about both the house and its windows on the  
behalf of the person (that it has exactly one window, that windows are  
opened by setting their ‘open’ accessor) and even then it deals with  
these assumptions inconsistently.

1. It checks for the behavior of the window without first checking for  
the existance of the window. If you really must program defensively, at  
least be consistent. Better still to not program defensively.

2. It raises a misleading (or at least ambiguious) error. Far better to  
simply try it and let the interpreter tell you where you failed. Is a  
NoMethodError on a window object really any less informative? Given the  
unnecessarily complex code path (because of the Demeter violation), I  
would argue that it is more informative.

In any event, you shouldn’t be violating Demeter in the first place.  
The following Law of Demeter violation better illustrates the problem:

class Person  
attr\_accessor :wallet  
end

class Store  
def accept\_payment(person, amount)  
person.wallet.amount -= amount  
end  
end

NB: I actually originally wrote it as person.wallet.withdraw(amount)  
(because I instinctively hide state and expose behavior in cases like  
these) and then realized that this is one less Demeter violation than  
the original example code.

People should be allowed to make payments themselves. I don’t want the  
store reaching into my wallet and just taking my money. I _certainly_  
don’t want the store reaching into my wallet and then telling me how  
much money I have when it’s done.

class Store  
def accept\_payment(person, amount)  
person.pay(amount)  
end  
end

This lets me decide where I keep my money, how I pay and if I can pay at  
all.

Furthermore, allowing assignment to the window’s ‘open’ accessor lets  
client code do `window.open = [1,2,3]` and other such nonsense. A  
window has exactly two states and its behavior should be modeled in a  
way that enforces this relationship and keeps its state hidden behind  
an interface, not by allowing client code to set its state in arbitrary  
ways. Window#open and Window#close.

The home should delegate window opening and closing to its window. The  
person should ask the home to close its window. Or the person should  
know about the window, which makes more sense in the real world but  
less sense in this OOP system.

Hopefully we can avoid making concrete OOP mistakes even while we’re  
answering completely hypothetical OOP questions.

Also, can someone please write me a Person#sudo\_make\_me\_a\_sandwich!
