Path: csiph.com!x330-a1.tempe.blueboxinc.net!newsfeed.hal-mli.net!feeder1.hal-mli.net!feeder.news-service.com!85.214.198.2.MISMATCH!eternal-september.org!feeder.eternal-september.org!.POSTED!not-for-mail From: Daniele Futtorovic Newsgroups: comp.lang.java.programmer Subject: Re: "Program to an interface" - When to break a design pattern Date: Fri, 06 May 2011 18:56:02 +0200 Organization: A noiseless patient Spider Lines: 205 Message-ID: References: <9dt5s6dalhetgfe99qs92c02hf0dbas44e@4ax.com> <2psjssq4zj.fsf@shell.xmission.com> <2poc3gq3p2.fsf@shell.xmission.com> <2pk4e4q2sq.fsf@shell.xmission.com> Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Injection-Date: Fri, 6 May 2011 16:56:04 +0000 (UTC) Injection-Info: mx01.eternal-september.org; posting-host="Zjm1/7nnVMUmB9baN8E6xQ"; logging-data="18555"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18+TcbuWHJrN/ZIVFd/uZ02" User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.17) Gecko/20110414 Thunderbird/3.1.10 In-Reply-To: Cancel-Lock: sha1:VF2GTAbFFAvtCYvGj2h63Rnl8mc= Xref: x330-a1.tempe.blueboxinc.net comp.lang.java.programmer:3699 On 06/05/2011 12:24, Arved Sandstrom allegedly wrote: > On 11-05-05 09:28 PM, Daniele Futtorovic wrote: >> On 06/05/2011 00:49, Arved Sandstrom allegedly wrote: >>> On 11-05-05 07:02 PM, Daniele Futtorovic wrote: >>>> On 05/05/2011 23:02, Jim Janney allegedly wrote: >>>>> Daniele Futtorovic writes: >>>>> >>>>>> On 05/05/2011 22:42, Jim Janney allegedly wrote: >>>>>>> Daniele Futtorovic writes: >>>>>>> >>>>>>>> On 05/05/2011 22:14, Jim Janney allegedly wrote: >>>>>>>>> The point of programming to the interface is to make it easier to >>>>>>>>> substitute a different implementation, which implies that any >>>>>>>>> reasonable implementation can be used. If this is not true, if the >>>>>>>>> code that uses the object relies on behavior only found in one >>>>>>>>> implementation, then there is no benefit to using the interface, >>>>>>>>> and >>>>>>>>> you make it more inviting for someone to break things later on. So >>>>>>>>> in this case, no, programming to the interface would be the wrong >>>>>>>>> thing to do. The point of design principles is to make you think >>>>>>>>> before you break them :-) >>>>>>>> >>>>>>>> Entirely disagreed. The code shown did not contain any >>>>>>>> justification for >>>>>>>> breaking the pattern in question, and on the opposite, it contained >>>>>>>> all >>>>>>>> the reasons to think more about encapsulation, which is the true >>>>>>>> underlying rationale for coding to interfaces -- not polymorphism >>>>>>>> per se. >>>>>>> >>>>>>> The justification is not in the code shown, but in the accompanying >>>>>>> remark "I need the map to retain the insertion order." There's no >>>>>>> interface in the JRE that promises this, and only one class that >>>>>>> provides it, which makes encapsulation, shall we say, difficult. >>>>>> >>>>>> That's not the point! Yes, you need a LinkedHashMap to retain >>>>>> insertion order in a Map. But retaining insertion order is >>>>>> relevant... only when inserting. >>>>> >>>>> Think a minute. When does retaining insertion order actually matter? >>>>> When you build the map, or some time later when you iterate over it? >>>>> Hint: if you never iterate over it, the order doesn't matter at all. >>>> >>>> I don't need a full minute to see that this is even further beside the >>>> point. >>> >>> Yeah, as in, your argument is incorrect, and Jim is right. >>> >>> You see this bit from the LinkedHashMap API?: >>> >>> "This implementation spares its clients from the unspecified, generally >>> chaotic ordering provided by HashMap (and Hashtable), without incurring >>> the increased cost associated with TreeMap. It can be used to produce a >>> copy of a map that has the same order as the original, regardless of the >>> original map's implementation." >>> >>> "Spares its _clients_". You provide this implementation for the >>> _clients_. Just any Map won't do, Daniele. And if you "program to the >>> interface" blindly, and return a Map from this method, then as long as >>> the OP's _unchanged_ code is used to implement >>> >>> public Map getSortedMap() >>> >>> then we'll get a LinkedHashMap and obey the desired contract. But down >>> the road - since we've failed to specify the requirement - things could >>> change, and the original implementation requirement be violated. In fact >>> a maintainer will look at the method's return type, and the >>> (unfortunate) name of te method, and in the absence of design >>> documentation decide maybe to use a SortedMap implementation instead. >>> *Which would be a mistake*...but your recommended approach would >>> encourage him to do it. >>> >>> Don't get all blinded by design principles like "program to the >>> interface". Most of the things you access in a real-world complex >>> application are exposed through classes, not interfaces. >> >> I can't say I take too kindly to your assertion that I intervened as I >> did for the sake of blindly following anything. > > OK, I'll retract the word "blind". I see you arguing strenuously for the > use of Map, and in this case I believe you're wrong. How's that? Fair enough. :) Although if I were to nitpick, I don't think I'm strenuous about it. I'm simply convinced that I am right, and all experience I can currently summon backs that. >> Neither do I agree with your interpretation of the bit of Javadoc you >> quoted. No, you do not provide a LinkedHashMap to clients. You provide a >> Map with the same iteration order as another. Nuance. > > It's not about providing a Map with the same iteration order as > "another". There might be no "other". In the case of a stock > LinkedHashMap we know that the predictable iteration order is insertion > order. But mainly the contract we're after is predictable iteration > order, regardless of how the map was loaded. That is what the Javadoc says. True, there might be no other: the Map may have been constructed and filled in the method itself. But I maintain that the iteration order of a LinkedHashMap is *not predictable*, *unless* you know the order in which elements were inserted in it (or can make reasonable assumptions as to that). > As far as your nuance is concerned, I'm interested (the _OP_ is > interested) in providing a map with predictable insertion order. Map > does not satisfy that; LinkedHashMap does. If I return Map then I am > making a mistake. > >> The return type being a LinkedHashMap, itself, doesn't tell you >> _anything_ about its iteration order. > > ??? It tells me that it has a predictable iteration order (which happens > to be insertion order). It's the _predictable_ bit that is important. > That it happens to be insertion order is a bonus but not the key point. I repeat: the iteration order is *not predictable* if you don't happen to know the insertion order (unlike e.g. a SortedMap). >> Take the following code: >> >> ################################################ >> interface Function { >> V f( K k ); >> } >> >> static LinkedHashMap map( Map input, Function f ){ >> Map ret = new HashMap(); >> for( Map.Entry entry: input.entrySet() ){ >> ret.put( entry.getKey(), f.f( entry.getValue() ) ); >> } >> return new LinkedHashMap( ret ); >> } >> >> Q: What does the return type tell you about the iteration order of the >> returned instance? >> ################################################ >> >> Like it or not, only the method's *contract*, if anything, can guarantee >> you that the returned instance will iterate in the same order as the input. > > Input? Did you see the OP's method signature? Why not stick to that? As > it happens _your_ method does something different: it takes its input, > preprocesses it, and _only then_ does anything with LinkedHashMap. > Ironically the > > return new LinkedHashMap( ret ); > > retains the insertion order of the "ret" pre-processed map, and does > what LinkedHashMap is supposed to do. > > All that fancy cruft you have prior is irrelevant and belongs in your > concocted method...which is not the original method signature. If you're > going to highlight method contracts, kindly use the same one as is being > discussed. I know this is not the same code as the OP. But I think it proves that the return type of a method being LinkedHashMap does not necessarily tell you anything about the iteration order of that method's return value. As I mentioned in reply to Jim yesterday, there is only one case I can think of where it would be justifiable to make the return type LinkedHashMap: if it's a factory method (the OP's code is not sufficient to confirm or infirm that). But even then I question the need for the caller to know that it's a LinkedHashMap. The only (borderline) case where it would be relevant would be something like this: public class SomeClass { LinkedHashMap createNewMap(){ ... } V resolve( U u ){ ... } static boolean eq( Object o1, Object o2 ){ return o1 == null ? o2 == null : o1.equals(o2); } public LinkedHashMap doSomething( Collection c ){ LinkedHashMap map = createNewMap(); for( U u: c ) map.put( u, resolve(u) ); Iterator it1 = c.iterator(), it2 = map.keySet().iterator(); for( ; it1.hasNext(); assert eq( it1.next(), it2.next() ) ); assert ! it2.hasNext(); return map; } In this case, I would be guaranteed that #doSomething() never throws an AssertionError or a NoSuchElementException. Cool. But borderline. And if the assertion were done *elsewhere* than in the very method that does the insertion, then #doSomething()'s return type being LinkedHashMap would not tell you *anything* as to whether or not the assertion holds. Please provide a code example that contradicts this. -- DF. An escaped convict once said to me: "Alcatraz is the place to be"