Path: csiph.com!weretis.net!feeder4.news.weretis.net!newsreader4.netcologne.de!news.netcologne.de!.POSTED.xdsl-84-44-158-142.netcologne.de!not-for-mail From: Patrick Roemer Newsgroups: comp.lang.java.programmer Subject: Re: True O-O Date: Tue, 18 Oct 2016 18:32:04 +0200 Organization: news.netcologne.de Distribution: world Message-ID: References: <8737k0qd38.fsf@golem.phantasia.org> <87zim5es30.fsf@golem.phantasia.org> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Injection-Date: Tue, 18 Oct 2016 16:32:05 +0000 (UTC) Injection-Info: newsreader4.netcologne.de; posting-host="xdsl-84-44-158-142.netcologne.de:84.44.158.142"; logging-data="9571"; mail-complaints-to="abuse@netcologne.de" User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.24) Gecko/20100411 Thunderbird/2.0.0.24 Mnenhy/0.7.6.0 X-Mozilla-News-Host: news://news.netcologne.de In-Reply-To: Xref: csiph.com comp.lang.java.programmer:35809 Responding to Eric Sosman: > On 10/17/2016 5:11 PM, Patrick Roemer wrote: >> >> In OO, one usually sends messages to objects, which triggers operations >> on their encapsulated state. Java's Math.max() is not how OO is >> "normally" done in my book. > > As someone who is most definitely not a dyed-in-the-wool O-O Expert, > I'd be interested to hear your opinion of things like, say I'm by no means an OO expert (and certainly not an OO zealot), but I'll bite... :) To start with, I'm as confused as anybody regarding a sound definition of OO, but here's one I like that Stefan Ram got from Alan Kay - I hope he didn't make it up. ;) "OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them." http://www.purl.org/stefan_ram/pub/doc_kay_oop_en > Collections.sort(theList); > > vs. > > theList.sortYourself(); // fictional, today Smalltalk: st> x (2 3 1 ) st> x asSortedCollection SortedCollection (1 2 3 ) st> x (2 3 1 ) st> x sort (1 2 3 ) st> x (1 2 3 ) We can get a sorted copy or ask the collection to sort itself in-place, both through messages to the collection. Doesn't seem to be a problem in itself. > It seems fairly normal to me that some operations are "built into" the > classes, while others are "imposed from without," although the criteria > for choosing between the approaches may not always be perfectly clear. I'd think that "builtin vs imposed from without" is mostly orthogonal to "message sends vs function calls". Example from this thread: A Comparator imposes an ordering strategy from without, but we still message an object and behavior is bound late - pretty in line with the above definition. > This is especially true of operations not anticipated at design time; > I can more easily imagine adding a > > Collections.getMedianElement(theCollection); // fictional > > than I can imagine adding a > > theCollection.getMedianElement(); // fictional > > to every Collection subclass. "More easily" doesn't sound like a design criterion but more like a trade-off consideration given Java and its standard library as it is today. That's perfectly reasonable, but it's not supporting the question which approach represents a better (OO) design, it's more of a counter-force. Could an implementation perhaps go to AbstractList or a default method? Let's try this in Scala: patrick@milgrim:/work/git/other/scala/scala$ git diff diff --git a/src/library/scala/collection/SeqLike.scala b/src/library/scala/collection/SeqLike.scala index 2d66225..041a87c 100644 --- a/src/library/scala/collection/SeqLike.scala +++ b/src/library/scala/collection/SeqLike.scala @@ -679,6 +679,13 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[ /* Need to override string, so that it's not the Function1's string that gets mixed in. */ override def toString = super[IterableLike].toString + + def median(): Option[A] = + if(isEmpty) + None + else + Some(apply(size / 2)) + } /** The companion object for trait `SeqLike`. patrick@milgrim:/work/git/other/scala/scala$ build/pack/bin/scala Welcome to Scala 2.12.0-20161014-083346-933bab2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_73). Type in expressions for evaluation. Or try :help. scala> Seq(1,2,3).median() res0: Option[Int] = Some(2) scala> Array(1,2,3).median() res1: Option[Int] = Some(2) scala> Vector(1,2,3).median() res2: Option[Int] = Some(2) scala> scala.collection.immutable.Queue(1,2,3).median() res3: Option[Int] = Some(2) This may be plain wrong (I'm not that familiar with the internals of the Scala collection zoo), but it gives some hope that it might be done right with limited effort. So in principle it should be possible to add a median method to the collection hierarchy. If trade-off considerations speak against it, that's probably due to design decisions in the Java language and standard library, not because going for the static function would be the right choice OO-wise. That all being said, I'd think that this method might be too special purpose for the generic Collection API. > What's the Righteous O-O Way here? Very likely it involves having an object (the collection itself or another) that reacts to a message by computing and passing the median element. :) It may very well differ from the Righteous Java way. (And I wouldn't claim that the Righteous OO way is always desirable.) I just think that one should try to distinguish arguments based on "pure principles" (of any paradigm) from those based on idiosyncrasies of a specific language design. It's perfectly fine to mix and weigh them in a concrete discussion, but one should always be clear about which is which. And this didn't seem to be the case with the Math#max() example at all. There probably is a way to do good OO. There probably is a way to do good Java. There may be a way to do good OO in Java. There may even be a way to do good Programming. And each of these ways will conflict with each other, more often than one would expect. :) Best regards, Patrick