On Sun, Nov 20, 2011 at 8:42 PM, Intransition <transfire / gmail.com> wrote:
> I seem to run into a couple of design issue a lot and I never know what i=
s
> really proper.=A0On one hand I often hear that I should limit coupling an=
d
> stick to single responsibility, but when I do I often find it difficult t=
o
> get the information to part of the program when it is needed.=A0For examp=
le,
> =A0 class Singer
> =A0 =A0 def initialize(name)
> =A0 =A0 =A0 @name =3D name
> =A0 =A0 end
> =A0 =A0 attr :name
> =A0 end
> Then should Song be:
> =A0 class Song
> =A0 =A0 def new(singer)
> =A0 =A0 =A0 @singer =3D singer
> =A0 =A0 end
> =A0 end
> or
> =A0 class Song
> =A0 =A0 def new(singer_name)
> =A0 =A0 =A0 @singer_name =3D singer_name
> =A0 =A0 end
> =A0 end
> The later has less coupling, so according to principles I should use it. =
But
> if I later discover something in Song needs to know more about the singer=
,
> I'm in a bad way. e.g.
> =A0 class Song
> =A0 =A0 ...
> =A0 =A0 def play
> =A0 =A0 =A0 puts "Belting it out by #{@singer.name}, winner of
> #{@singer.grammy_count} grammies!"
> =A0 =A0 end
> =A0 end
> I'd be in a fix if I had used the later Song class instead of the former.
> But then I suspect someone would remind me of SRP, single responsibility
> principle, and suggest instead:
> =A0 class SongPlayer
> =A0 =A0 def initialize(singer, song)
> =A0 =A0 =A0 @singer, @song =3D singer, song
> =A0 =A0 end
> =A0 =A0 def play
> =A0 =A0 =A0 puts "Belting it out by #{@singer.name}, winner of
> #{@singer.grammy_count} grammies!"
> =A0 =A0 end
> =A0 end
> And yea, I guess that makes sense, since another singer might do a cover =
of
> some one else's song, right? But then, would it really be the exact same
> song? In most of my cases it's never the same "song" so I never have that
> kind of scenario. So is the SRP worth the extra classes it brings to the
> code then?

I believe it helps to look at this with a bit more abstraction (think:
UML class diagram).  For me the solution of this example is pretty
clear: a Song has a relationship with a Singer (or Interpret) - and
not with a singer's name.  In fact it's a n:1 relationship.  If you
want to model the act of singing you could add a class Performance
which binds together a Song and an Interpret (not necessarily the
original Interpret) along with additional properties like location and
time (there may be more: duration, unplugged?, paid?, charity_concert?
...).

I think the mere fact that it is quite obvious that you may want to
access more features than just the singer's name when dealing with a
Song is indicative of the fact that you should not use the name only
but the Singer instance instead.  IMHO using the name would be too
loose coupling - which isn't good either because - as you notice - it
needs much more context to get at the Singer from the name only.  Also
note, that a singer's name may not be unique, so it does not lend
itself easily to a primary key which must be unique.  And if you start
adding an id to the Song then you need at least an additional Hash
with id as key and Singer as value to find the proper Singer back.

There is a saying by a guy which was pretty slick and I think it
applies here as well:
http://www.brainyquote.com/quotes/quotes/a/alberteins103652.html
;-)

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/