Re: "Program to an interface" - When to break a design pattern

Discussion in 'Java' started by Michal Kleczek, May 6, 2011.

  1. wrote:

    >
    > I've seen this design pattern before
    >
    > http://witte-consulting.com/documents/design-principles/
    >
    > and, in general, I see the point of it.
    >
    > But say we've got something like this
    >
    > LinkedHashMap<String, Integer> sortedMap = this.getSortedMap();
    >
    > So you have the method
    >
    > public LinkedHashMap<String, Integer> getSortedMap() {
    > //do stuff
    > }
    >
    > (not necessarily public)
    >
    > Now the design principle says, the method signature should instead be
    >
    > public Map<String, Integer> getSortedMap() {
    > //do stuff
    > }
    >
    > The problem is, where I'm creating sortedMap above, I need the map to
    > retain the insertion order. If what's returned actually is a Map,
    > rather than a LinkedHashMap, then the results the user actually sees
    > are going to be in the wrong order. Making things worse, in this case,
    > nothing would actually break, only the end user would notice anything
    > was actually wrong.
    >
    > So in this case, it seems to me, that using LinkedHashMap in the
    > method signature makes sense. The fact that the return retains the
    > insertion order is an integral part of what the method does.
    >
    > If nothing else, it's going to save Fred Developer down the line from
    > looking at the code around this
    >
    > Map<String, Integer> sortedMap = this.getSortedMap();
    >
    > and thinking "wait, how do I know getSortedMap() is going to return a
    > result with the right ordering?", and having to waste time digging
    > into that method.
    >
    > ***
    >
    > Opinions? Angry mob with torches and pitchforks? The Spanish
    > Inquisition?
    >
    >


    Stick to the principle and specify return type as java.util.Map.

    The decision on what Map implementation to use has to obey DRY principle -
    if the place to decide is getSortedMap() then the rest of the program should
    assume getSortedMap() does the right thing.

    Imagine the requirements change and from now on the map has to retain
    reverse insertion order. Do you want to rewrite your clients?

    --
    Michal
    Michal Kleczek, May 6, 2011
    #1
    1. Advertising

  2. On 11-05-06 12:15 PM, Michal Kleczek wrote:
    > wrote:
    >
    >>
    >> I've seen this design pattern before
    >>
    >> http://witte-consulting.com/documents/design-principles/
    >>
    >> and, in general, I see the point of it.
    >>
    >> But say we've got something like this
    >>
    >> LinkedHashMap<String, Integer> sortedMap = this.getSortedMap();
    >>
    >> So you have the method
    >>
    >> public LinkedHashMap<String, Integer> getSortedMap() {
    >> //do stuff
    >> }
    >>
    >> (not necessarily public)
    >>
    >> Now the design principle says, the method signature should instead be
    >>
    >> public Map<String, Integer> getSortedMap() {
    >> //do stuff
    >> }
    >>
    >> The problem is, where I'm creating sortedMap above, I need the map to
    >> retain the insertion order. If what's returned actually is a Map,
    >> rather than a LinkedHashMap, then the results the user actually sees
    >> are going to be in the wrong order. Making things worse, in this case,
    >> nothing would actually break, only the end user would notice anything
    >> was actually wrong.
    >>
    >> So in this case, it seems to me, that using LinkedHashMap in the
    >> method signature makes sense. The fact that the return retains the
    >> insertion order is an integral part of what the method does.
    >>
    >> If nothing else, it's going to save Fred Developer down the line from
    >> looking at the code around this
    >>
    >> Map<String, Integer> sortedMap = this.getSortedMap();
    >>
    >> and thinking "wait, how do I know getSortedMap() is going to return a
    >> result with the right ordering?", and having to waste time digging
    >> into that method.
    >>
    >> ***
    >>
    >> Opinions? Angry mob with torches and pitchforks? The Spanish
    >> Inquisition?

    >
    > Stick to the principle and specify return type as java.util.Map.


    It's best to understand that principle before urging it on people. No
    less an authority than Erich Gamma has made it clear - albeit not widely
    enough nor early enough - that when GOF said "program to an interface",
    they did *NOT* mean only Java "interface" as in a construct with the
    "interface" keyword. What they actually meant was the OOP interface that
    *every* class has, coupled with a judicious choice of which interface of
    this type to pick in the type hierarchy.

    What the principle really means - and you don't have to take my word for
    it, you can look up discussions by OOP authorities - is that given that
    the object you need is in a type hierarchy - classes, abstract classes,
    interfaces - that you pick that level in the type hierarchy to work
    with, which is as high up as possible while still meeting requirements.

    The highest interface that fits the bill might actually be the interface
    of a concrete class, or the interface of an abstract class. In fact
    abstract classes are frequently better as interfaces than actual Java
    "interfaces".

    Bear in mind - and this is not invented by me; you can find this if you
    keep up on OOP literature - that there is a reasonably well-known
    distinction also between public and published interfaces (look up
    "Fowler", "public", "published", and continue to keep in mind that
    "interface" doesn't mean the Java keyword). If an interface is published
    then it's not a bad thing to use it. LinkedHashMap has a published
    interface *and* it has behaviour that other maps do not have - it is a
    correct decision to stop in the type hierarchy with LinkedHashMap if
    that's the behaviour you need.

    Further note: behaviour isn't just a list of method signatures. It's
    also what the abstract class or concrete class actually does. Not *how*
    it does it - that's implementation - but what it does - that's
    contractual _behaviour_. Keeping insertion order for iterators is
    _behaviour_...using a doubly-linked list to accomplish that is
    implementation.

    > The decision on what Map implementation to use has to obey DRY principle -
    > if the place to decide is getSortedMap() then the rest of the program should
    > assume getSortedMap() does the right thing.


    Good luck with that one. That's what a lot of Java code ends up hoping
    for, because people misunderstood "program to an interface" and forced
    the use of the highest possible level Java-keyword interfaces everywhere
    possible. There are a bunch of problems that come about then:
    downcasting all over the place, brittle code because now everything that
    relied on interface X (including the 10 classes you wrote that
    implemented it) has to be changed because you need to add a method to
    interface X (or modify a method signature in interface X), calling code
    unaware that it really ended up depending on that Map actually being a
    LinkedHashMap and only finding out when some other provider of the
    method gets swapped in...the list goes on and on.

    > Imagine the requirements change and from now on the map has to retain
    > reverse insertion order. Do you want to rewrite your clients?
    >

    Bad example in my opinion. The first time this happened I'd take it on
    the chin and accept that the client code doesn't know what it
    wants...seeing as how it's the client code that forces a change like
    this. And rather than be screwed twice I'd probably write an abstract
    class, or Java-keyword interface, that would let me cope with poor
    design by the other team.

    Be clear on this - you could have used a high-level Java "interface", as
    in the keyword type of interface. And the requirements change and now
    you need to modify the interface - you're just as hosed then, Michal.

    AHS
    Arved Sandstrom, May 7, 2011
    #2
    1. Advertising

  3. Michal Kleczek

    Lew Guest

    On 05/06/2011 07:53 PM, Arved Sandstrom wrote:
    > On 11-05-06 12:15 PM, Michal Kleczek wrote:
    >> wrote:
    >>
    >>>
    >>> I've seen this design pattern before
    >>>
    >>> http://witte-consulting.com/documents/design-principles/
    >>>
    >>> and, in general, I see the point of it.
    >>>
    >>> But say we've got something like this
    >>>
    >>> LinkedHashMap<String, Integer> sortedMap = this.getSortedMap();
    >>>
    >>> So you have the method
    >>>
    >>> public LinkedHashMap<String, Integer> getSortedMap() {
    >>> //do stuff
    >>> }
    >>>
    >>> (not necessarily public)
    >>>
    >>> Now the design principle says, the method signature should instead be
    >>>
    >>> public Map<String, Integer> getSortedMap() {
    >>> //do stuff
    >>> }
    >>>
    >>> The problem is, where I'm creating sortedMap above, I need the map to
    >>> retain the insertion order. If what's returned actually is a Map,
    >>> rather than a LinkedHashMap, then the results the user actually sees
    >>> are going to be in the wrong order. Making things worse, in this case,
    >>> nothing would actually break, only the end user would notice anything
    >>> was actually wrong.
    >>>
    >>> So in this case, it seems to me, that using LinkedHashMap in the
    >>> method signature makes sense. The fact that the return retains the
    >>> insertion order is an integral part of what the method does.
    >>>
    >>> If nothing else, it's going to save Fred Developer down the line from
    >>> looking at the code around this
    >>>
    >>> Map<String, Integer> sortedMap = this.getSortedMap();
    >>>
    >>> and thinking "wait, how do I know getSortedMap() is going to return a
    >>> result with the right ordering?", and having to waste time digging
    >>> into that method.
    >>>
    >>> ***
    >>>
    >>> Opinions? Angry mob with torches and pitchforks? The Spanish
    >>> Inquisition?

    >>
    >> Stick to the principle and specify return type as java.util.Map.

    >
    > It's best to understand that principle before urging it on people. No
    > less an authority than Erich Gamma has made it clear - albeit not widely
    > enough nor early enough - that when GOF said "program to an interface",
    > they did *NOT* mean only Java "interface" as in a construct with the
    > "interface" keyword. What they actually meant was the OOP interface that
    > *every* class has, coupled with a judicious choice of which interface of
    > this type to pick in the type hierarchy.
    >
    > What the principle really means - and you don't have to take my word for
    > it, you can look up discussions by OOP authorities - is that given that
    > the object you need is in a type hierarchy - classes, abstract classes,
    > interfaces - that you pick that level in the type hierarchy to work
    > with, which is as high up as possible while still meeting requirements.
    >
    > The highest interface that fits the bill might actually be the interface
    > of a concrete class, or the interface of an abstract class. In fact
    > abstract classes are frequently better as interfaces than actual Java
    > "interfaces".
    >
    > Bear in mind - and this is not invented by me; you can find this if you
    > keep up on OOP literature - that there is a reasonably well-known
    > distinction also between public and published interfaces (look up
    > "Fowler", "public", "published", and continue to keep in mind that
    > "interface" doesn't mean the Java keyword). If an interface is published
    > then it's not a bad thing to use it. LinkedHashMap has a published
    > interface *and* it has behaviour that other maps do not have - it is a
    > correct decision to stop in the type hierarchy with LinkedHashMap if
    > that's the behaviour you need.
    >
    > Further note: behaviour isn't just a list of method signatures. It's
    > also what the abstract class or concrete class actually does. Not *how*
    > it does it - that's implementation - but what it does - that's
    > contractual _behaviour_. Keeping insertion order for iterators is
    > _behaviour_...using a doubly-linked list to accomplish that is
    > implementation.
    >
    >> The decision on what Map implementation to use has to obey DRY principle -
    >> if the place to decide is getSortedMap() then the rest of the program should
    >> assume getSortedMap() does the right thing.

    >
    > Good luck with that one. That's what a lot of Java code ends up hoping
    > for, because people misunderstood "program to an interface" and forced
    > the use of the highest possible level Java-keyword interfaces everywhere
    > possible. There are a bunch of problems that come about then:
    > downcasting all over the place, brittle code because now everything that
    > relied on interface X (including the 10 classes you wrote that
    > implemented it) has to be changed because you need to add a method to
    > interface X (or modify a method signature in interface X), calling code
    > unaware that it really ended up depending on that Map actually being a
    > LinkedHashMap and only finding out when some other provider of the
    > method gets swapped in...the list goes on and on.
    >
    >> Imagine the requirements change and from now on the map has to retain
    >> reverse insertion order. Do you want to rewrite your clients?
    >>

    > Bad example in my opinion. The first time this happened I'd take it on
    > the chin and accept that the client code doesn't know what it
    > wants...seeing as how it's the client code that forces a change like
    > this. And rather than be screwed twice I'd probably write an abstract
    > class, or Java-keyword interface, that would let me cope with poor
    > design by the other team.
    >
    > Be clear on this - you could have used a high-level Java "interface", as
    > in the keyword type of interface. And the requirements change and now
    > you need to modify the interface - you're just as hosed then, Michal.


    Here are the relevant chapter titles from /Effective Java/, by Joshua Bloch:
    Item 18: Prefer interfaces to abstract classes
    Item 19: Use interfaces only to define types
    Item 52: Refer to objects by their interfaces
    Item 53: Prefer interfaces to reflection

    Here he refers to the Java 'interface', not the more general term you just
    discussed.

    Note that in the details of Item 52, Mr. Bloch covers using the more specific
    types when the contract (the GoF sense of "interface") calls for it:

    "If appropriate interface types exist, then ... your program will be more
    flexible if you use the interface to refer to the object; if not, just use the
    least specific class in the class hierarchy that provides the required
    functionality."


    --
    Lew
    Honi soit qui mal y pense.
    http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg
    Lew, May 7, 2011
    #3
  4. On 11-05-06 10:39 PM, Lew wrote:
    > On 05/06/2011 07:53 PM, Arved Sandstrom wrote:

    [ SNIP ]

    > Here are the relevant chapter titles from /Effective Java/, by Joshua
    > Bloch:
    > Item 18: Prefer interfaces to abstract classes
    > Item 19: Use interfaces only to define types
    > Item 52: Refer to objects by their interfaces
    > Item 53: Prefer interfaces to reflection
    >
    > Here he refers to the Java 'interface', not the more general term you
    > just discussed.
    >
    > Note that in the details of Item 52, Mr. Bloch covers using the more
    > specific types when the contract (the GoF sense of "interface") calls
    > for it:
    >
    > "If appropriate interface types exist, then ... your program will be
    > more flexible if you use the interface to refer to the object; if not,
    > just use the least specific class in the class hierarchy that provides
    > the required functionality."


    Fair enough. As far as Item 52 goes, I think that's what everyone is
    saying, given that elaboration you quoted. Item 19, no argument from me.
    Item 53, I won't argue about that either.

    Item 18, "prefer interfaces to abstract classes" is a short & sweet
    recommendation that more often than not is a good one to follow.
    Particularly in Java, where language limitations, as Bloch himself
    points out, seriously limit the use of abstract classes as type
    definitions. However, the use of the word "prefer" means to me that
    available interfaces are used over available abstract classes when
    consideration shows that it's the best choice, not just because a
    single-sentence principle says so.

    One important thing needs to be pointed out here, and it's mentioned in
    Item 18 by Bloch: " Any class that defines all of the required methods
    and obeys the general contract is permitted to implement an interface".
    The sticking point in this thread is the _general contract_ - there is
    no interface, Map or otherwise, that has the contract required by the
    OP. Short of defining a PredictableIterationOrderMap interface and
    having a subclass of LinkedHashMap implement that, one may as well use
    LinkedHashMap. Per Bloch's Item 52 - there is no appropriate interface
    type available here.

    To rephrase that, we should be choosing types - interfaces, abstract
    classes, or concrete classes - whose contracts match the requirements.
    And a bag of method signatures is not a contract (except between the
    code and the compiler). Behaviour specified in the API Javadocs (in the
    case of maps, starting right with java.util.Map) is the actual contract.

    The practise of using a Java interface at all costs, and the most
    general interface at that, with little or no attention paid to the fact
    that the general type you are passing around does not have a required
    contract, is so prevalent in the industry that these days, if you try to
    point out the intent of the design principle, people often have no clue
    what you are trying to express. It's a losing battle. There's certainly
    no shortage of folks who think that that "bag of method signatures" _is_
    the contract, that's for sure.

    AHS
    Arved Sandstrom, May 7, 2011
    #4
  5. Arved Sandstrom wrote:

    >
    > Further note: behaviour isn't just a list of method signatures. It's
    > also what the abstract class or concrete class actually does. Not *how*
    > it does it - that's implementation - but what it does - that's
    > contractual _behaviour_. Keeping insertion order for iterators is
    > _behaviour_...using a doubly-linked list to accomplish that is
    > implementation.
    >


    I think this is the real point of the discussion - Java interfaces are just
    too weak to express contracts. Note that there are many other aspects beside
    iteration ordering that are important to the overall solution but still not
    expressed (and not expressable) by interfaces: concurrency guarantees,
    performance and memory usage characteristics etc.
    Picking one aspect and trying to express it using Java types just does not
    make sense.

    One could actually argue if java.util.Set interface is really needed at all.

    Additionally - the mere fact that in the overall solution the choice of the
    map implementation having a particular characteristic is important is not
    enough to justify that _all_ code should depend on it.

    >> The decision on what Map implementation to use has to obey DRY principle
    >> - if the place to decide is getSortedMap() then the rest of the program
    >> should assume getSortedMap() does the right thing.

    >
    > Good luck with that one. That's what a lot of Java code ends up hoping
    > for, because people misunderstood "program to an interface" and forced
    > the use of the highest possible level Java-keyword interfaces everywheres
    > possible. There are a bunch of problems that come about then:
    > downcasting all over the place,


    If downcasting is needed it means the wrong type was choosen. It is not the
    case here since OP is not going to use any methods not defined in
    java.util.Map.

    --
    Michal
    Michal Kleczek, May 8, 2011
    #5
  6. On 11-05-08 07:24 AM, Michal Kleczek wrote:
    > Arved Sandstrom wrote:
    >
    >> Further note: behaviour isn't just a list of method signatures. It's
    >> also what the abstract class or concrete class actually does. Not *how*
    >> it does it - that's implementation - but what it does - that's
    >> contractual _behaviour_. Keeping insertion order for iterators is
    >> _behaviour_...using a doubly-linked list to accomplish that is
    >> implementation.

    >
    > I think this is the real point of the discussion - Java interfaces are just
    > too weak to express contracts. Note that there are many other aspects beside
    > iteration ordering that are important to the overall solution but still not
    > expressed (and not expressable) by interfaces: concurrency guarantees,
    > performance and memory usage characteristics etc.
    > Picking one aspect and trying to express it using Java types just does not
    > make sense.


    Agreed, partly. It can't be done with Java interfaces at all. It can -
    obviously - be done with the published API of abstract or concrete Java
    classes.

    What we're really arguing about here is whether it's desirable to bind
    client code to provider contracts. It's clear that Map cannot enforce a
    behavioural contract, and that LinkedHashMap does. Rather than beat that
    to death, let's acknowledge that we have one camp that argues that we
    don't want to tie the caller to a behavioural provider contract, and
    another camp (me) that says that sometimes you do want and need to do this.

    > One could actually argue if java.util.Set interface is really needed at all.


    This is one of the main points, I agree. It's also been highlighted in
    this thread precisely because most of the time the calling code
    genuinely doesn't care what implementation it gets. Most of the time the
    calling code requires simply that it gets a Map, List, Set etc, and the
    provider of the Map or List or Set has the responsibility for enforcing
    the contract...assuming one exists.

    In this case I believe a credible case can be made that the calling code
    does care about what the specific contract is: the OP said so, and
    provided a good use case. One thing I do know, based on experience, is
    that if you return a generic map from that method, that when developers
    move on to different projects and different employers, and the
    documentation (if it ever existed) fails to be maintained, and
    refactorings change the relationship of the Map provider code to the
    calling code, that sooner or later that client code is no longer going
    to get a LinkedHashMap. If the code never gets touched again after
    Version 1.0, then you were just _lucky_.

    I don't pretend to be infallible when it comes to answers for this kind
    of thing. But I do know that a knee-jerk 100% adherence to "program to a
    Java 'interface'", and highest-level interfaces at that, can be wrong.

    Absolutely, Java interfaces, *as language constructs* that are enforced
    by the compiler, are too weak to express many forms of contracts. All
    they can do is ensure that subtypes that implement an interface have a
    certain set of methods. Period.

    > Additionally - the mere fact that in the overall solution the choice of the
    > map implementation having a particular characteristic is important is not
    > enough to justify that _all_ code should depend on it.


    I'd honestly need to see more of the OP's problem before I definitely
    recommended returning LinkedHashMap. Mainly what got my hackles up was
    everyone piling in on the "program to the interface" principle without
    any obvious thought being put into it.

    Fact of the matter is that you _could_ return Map in the OP's scenario,
    and get away with it, provided that both the code that constructed and
    provided that map, and the code that used it, were one monolithic piece.
    It's when you separate the two that wishful thinking enters the picture.
    We don't in this case know enough about the relationship of the provider
    of the map and the user of the map to say, one way or the other, whether
    Map or LinkedHashMap is the best way to go.

    >>> The decision on what Map implementation to use has to obey DRY principle
    >>> - if the place to decide is getSortedMap() then the rest of the program
    >>> should assume getSortedMap() does the right thing.

    >>
    >> Good luck with that one. That's what a lot of Java code ends up hoping
    >> for, because people misunderstood "program to an interface" and forced
    >> the use of the highest possible level Java-keyword interfaces everywheres
    >> possible. There are a bunch of problems that come about then:
    >> downcasting all over the place,

    >
    > If downcasting is needed it means the wrong type was choosen. It is not the
    > case here since OP is not going to use any methods not defined in
    > java.util.Map.
    >

    With Java collections - Map and Collection mainly - you normally don't
    have to downcast, just for the reason you stated. All the subinterfaces
    and classes that extend/implement Map and Collection rarely
    significantly change the actual set of method signatures, with a few
    notable exceptions like queues. But if you "program to an interface" at
    all costs in many other type hierarchies then it can happen.

    As a conclusion, I think a few of us have at least agreed that there is
    no good mechanism in Java for defining and enforcing a large swathe of
    contractual behaviour. We rely on good Javadocs, and meticulous reading
    of same, and very good adherence to design and implementation best
    practices - all those things that all Java programmers are so very good
    at - to save us.

    AHS
    Arved Sandstrom, May 8, 2011
    #6
  7. Arved Sandstrom wrote:

    > On 11-05-08 07:24 AM, Michal Kleczek wrote:
    >> Arved Sandstrom wrote:
    >>
    >>> Further note: behaviour isn't just a list of method signatures. It's
    >>> also what the abstract class or concrete class actually does. Not *how*
    >>> it does it - that's implementation - but what it does - that's
    >>> contractual _behaviour_. Keeping insertion order for iterators is
    >>> _behaviour_...using a doubly-linked list to accomplish that is
    >>> implementation.

    >>
    >> I think this is the real point of the discussion - Java interfaces are
    >> just too weak to express contracts. Note that there are many other
    >> aspects beside iteration ordering that are important to the overall
    >> solution but still not expressed (and not expressable) by interfaces:
    >> concurrency guarantees, performance and memory usage characteristics etc.
    >> Picking one aspect and trying to express it using Java types just does
    >> not make sense.

    >
    > Agreed, partly. It can't be done with Java interfaces at all. It can -
    > obviously - be done with the published API of abstract or concrete Java
    > classes.


    Then it would be not "what" but "how".

    >
    > What we're really arguing about here is whether it's desirable to bind
    > client code to provider contracts. It's clear that Map cannot enforce a
    > behavioural contract, and that LinkedHashMap does. Rather than beat that
    > to death, let's acknowledge that we have one camp that argues that we
    > don't want to tie the caller to a behavioural provider contract, and
    > another camp (me) that says that sometimes you do want and need to do
    > this.


    I think you are missing the fact that the client is _already_ tied to a
    contract by calling getSortedMap() to get the map (since the contract of
    getSortedMap() is "return a map implementation that provides such and such
    iteration order".
    The question is rather - do we need to specify this contract as a Java type?
    I would rather say: since the compiler cannot enforce/check the contract
    anyway then it is useless - the contract specified as documentation of
    getSortedMap() is enough.

    --
    Michal
    Michal Kleczek, May 9, 2011
    #7
  8. On 11-05-09 06:04 AM, Michal Kleczek wrote:
    > Arved Sandstrom wrote:
    >
    >> On 11-05-08 07:24 AM, Michal Kleczek wrote:
    >>> Arved Sandstrom wrote:
    >>>
    >>>> Further note: behaviour isn't just a list of method signatures. It's
    >>>> also what the abstract class or concrete class actually does. Not *how*
    >>>> it does it - that's implementation - but what it does - that's
    >>>> contractual _behaviour_. Keeping insertion order for iterators is
    >>>> _behaviour_...using a doubly-linked list to accomplish that is
    >>>> implementation.
    >>>
    >>> I think this is the real point of the discussion - Java interfaces are
    >>> just too weak to express contracts. Note that there are many other
    >>> aspects beside iteration ordering that are important to the overall
    >>> solution but still not expressed (and not expressable) by interfaces:
    >>> concurrency guarantees, performance and memory usage characteristics etc.
    >>> Picking one aspect and trying to express it using Java types just does
    >>> not make sense.

    >>
    >> Agreed, partly. It can't be done with Java interfaces at all. It can -
    >> obviously - be done with the published API of abstract or concrete Java
    >> classes.

    >
    > Then it would be not "what" but "how".


    No, that's not necessarily so. Abstract classes and concrete classes
    provide both a "what" - an interface (as in contract, not Java keyword)
    - and an implementation. An abstract class with no method
    implementations actually ought to be a Java interface, but assuming we
    have one, it's 100% "what" and 0% "how". An abstract class may trend
    quite high, 70 or 80 or 90 percent, in "how", but it's still 100% in
    "what", assuming that we consider all the public methods to be part of a
    published API.

    Even a concrete class - again assuming that all of its public methods
    are part of a published API - is 100% in "what", and now 100% on "how"
    as well.

    An implemented published method in a concrete class is as much part of a
    contractual interface as a method signature in a Java interface.

    >> What we're really arguing about here is whether it's desirable to bind
    >> client code to provider contracts. It's clear that Map cannot enforce a
    >> behavioural contract, and that LinkedHashMap does. Rather than beat that
    >> to death, let's acknowledge that we have one camp that argues that we
    >> don't want to tie the caller to a behavioural provider contract, and
    >> another camp (me) that says that sometimes you do want and need to do
    >> this.

    >
    > I think you are missing the fact that the client is _already_ tied to a
    > contract by calling getSortedMap() to get the map (since the contract of
    > getSortedMap() is "return a map implementation that provides such and such
    > iteration order".
    > The question is rather - do we need to specify this contract as a Java type?
    > I would rather say: since the compiler cannot enforce/check the contract
    > anyway then it is useless - the contract specified as documentation of
    > getSortedMap() is enough.
    >

    I agree that the compiler cannot enforce the contract unless both the
    provider of the LinkedHashMap and the calling code are written
    cooperatively to use LinkedHashMap explicitly, thereby locking in the
    requirement. This is the scenario I've been positing.

    As I believe I mentioned in another post, you could get away with
    documentation, but I believe you'd have to document not just the method,
    but also all the call sites. If you're going to rely on documentation
    then perhaps your biggest win would be to change the name of the method
    - getMap() would be an atrocious choice. IMO the only defensible choice is

    Map getPredictableIterationOrderMap()

    Now *this* stands out when you're using it.

    AHS
    Arved Sandstrom, May 9, 2011
    #8
  9. Arved Sandstrom wrote:

    > On 11-05-09 06:04 AM, Michal Kleczek wrote:
    >> I think you are missing the fact that the client is _already_ tied to a
    >> contract by calling getSortedMap() to get the map (since the contract of
    >> getSortedMap() is "return a map implementation that provides such and
    >> such iteration order".
    >> The question is rather - do we need to specify this contract as a Java
    >> type? I would rather say: since the compiler cannot enforce/check the
    >> contract anyway then it is useless - the contract specified as
    >> documentation of getSortedMap() is enough.
    >>

    > I agree that the compiler cannot enforce the contract unless both the
    > provider of the LinkedHashMap and the calling code are written
    > cooperatively to use LinkedHashMap explicitly, thereby locking in the
    > requirement. This is the scenario I've been positing.
    >
    > As I believe I mentioned in another post, you could get away with
    > documentation, but I believe you'd have to document not just the method,
    > but also all the call sites. If you're going to rely on documentation
    > then perhaps your biggest win would be to change the name of the method
    > - getMap() would be an atrocious choice. IMO the only defensible choice is
    >
    > Map getPredictableIterationOrderMap()
    >
    > Now *this* stands out when you're using it.


    Sure - naming of functions is one of the most important aspects of defining
    them.
    Anyway.
    Somehow I got lost in discussion and forgot the most important thing IMHO:
    the fact that iteration order is important to the overall solution does not
    imply it is important to the client code. Take an example:

    //the program is supposed to print hashcodes of strings provided as
    //arguments in the order that the user gave them
    //forget that the map is not needed here
    public void PrintHashes {

    public static void main(String[] args) {
    final Map<String, Integer> mapOfHashes = new LinkedHashMap<>();
    calculateHashes(Arrays.asList(args), mapOfHashes);
    printMap(mapOfHashes, System.out);
    }

    private static <T> void calculateHashes(
    Iterable<? extends T> objects,
    Map<? super T, ? super Integer> hashes) {
    for (final Object object : objects) {
    hashes.put(object, object.hashCode());
    }
    }

    private static void printMap(Map<?, ?> map, PrintStream out) {
    for (final Map.Entry<?, ?> entry : map.entrySet()) {
    out.println("Key: " + entry.getKey() + " Value: " + entry.getValue());
    }
    }

    }

    According to your logic all references to Map<whatever> should be replaced
    by LinkedHashMap<String, Integer> which - sorry to say that - sounds insane
    :)

    --
    Michal
    Michal Kleczek, May 10, 2011
    #9
  10. Michal Kleczek

    Jim Janney Guest

    Michal Kleczek <> writes:

    > Arved Sandstrom wrote:
    >
    >> On 11-05-09 06:04 AM, Michal Kleczek wrote:
    >>> I think you are missing the fact that the client is _already_ tied to a
    >>> contract by calling getSortedMap() to get the map (since the contract of
    >>> getSortedMap() is "return a map implementation that provides such and
    >>> such iteration order".
    >>> The question is rather - do we need to specify this contract as a Java
    >>> type? I would rather say: since the compiler cannot enforce/check the
    >>> contract anyway then it is useless - the contract specified as
    >>> documentation of getSortedMap() is enough.
    >>>

    >> I agree that the compiler cannot enforce the contract unless both the
    >> provider of the LinkedHashMap and the calling code are written
    >> cooperatively to use LinkedHashMap explicitly, thereby locking in the
    >> requirement. This is the scenario I've been positing.
    >>
    >> As I believe I mentioned in another post, you could get away with
    >> documentation, but I believe you'd have to document not just the method,
    >> but also all the call sites. If you're going to rely on documentation
    >> then perhaps your biggest win would be to change the name of the method
    >> - getMap() would be an atrocious choice. IMO the only defensible choice is
    >>
    >> Map getPredictableIterationOrderMap()
    >>
    >> Now *this* stands out when you're using it.

    >
    > Sure - naming of functions is one of the most important aspects of defining
    > them.
    > Anyway.
    > Somehow I got lost in discussion and forgot the most important thing IMHO:
    > the fact that iteration order is important to the overall solution does not
    > imply it is important to the client code. Take an example:


    Agreed. Neither does it imply that it is *not* important. We don't
    have enough information to determine that one way or the other.

    --
    Jim Janney
    Jim Janney, May 10, 2011
    #10
  11. On 11-05-10 10:51 AM, Michal Kleczek wrote:
    > Arved Sandstrom wrote:
    >
    >> On 11-05-09 06:04 AM, Michal Kleczek wrote:
    >>> I think you are missing the fact that the client is _already_ tied to a
    >>> contract by calling getSortedMap() to get the map (since the contract of
    >>> getSortedMap() is "return a map implementation that provides such and
    >>> such iteration order".
    >>> The question is rather - do we need to specify this contract as a Java
    >>> type? I would rather say: since the compiler cannot enforce/check the
    >>> contract anyway then it is useless - the contract specified as
    >>> documentation of getSortedMap() is enough.
    >>>

    >> I agree that the compiler cannot enforce the contract unless both the
    >> provider of the LinkedHashMap and the calling code are written
    >> cooperatively to use LinkedHashMap explicitly, thereby locking in the
    >> requirement. This is the scenario I've been positing.
    >>
    >> As I believe I mentioned in another post, you could get away with
    >> documentation, but I believe you'd have to document not just the method,
    >> but also all the call sites. If you're going to rely on documentation
    >> then perhaps your biggest win would be to change the name of the method
    >> - getMap() would be an atrocious choice. IMO the only defensible choice is
    >>
    >> Map getPredictableIterationOrderMap()
    >>
    >> Now *this* stands out when you're using it.

    >
    > Sure - naming of functions is one of the most important aspects of defining
    > them.
    > Anyway.
    > Somehow I got lost in discussion and forgot the most important thing IMHO:
    > the fact that iteration order is important to the overall solution does not
    > imply it is important to the client code. Take an example:
    >
    > //the program is supposed to print hashcodes of strings provided as
    > //arguments in the order that the user gave them
    > //forget that the map is not needed here
    > public void PrintHashes {
    >
    > public static void main(String[] args) {
    > final Map<String, Integer> mapOfHashes = new LinkedHashMap<>();
    > calculateHashes(Arrays.asList(args), mapOfHashes);
    > printMap(mapOfHashes, System.out);
    > }
    >
    > private static <T> void calculateHashes(
    > Iterable<? extends T> objects,
    > Map<? super T, ? super Integer> hashes) {
    > for (final Object object : objects) {
    > hashes.put(object, object.hashCode());
    > }
    > }
    >
    > private static void printMap(Map<?, ?> map, PrintStream out) {
    > for (final Map.Entry<?, ?> entry : map.entrySet()) {
    > out.println("Key: " + entry.getKey() + " Value: " + entry.getValue());
    > }
    > }
    >
    > }
    >
    > According to your logic all references to Map<whatever> should be replaced
    > by LinkedHashMap<String, Integer> which - sorry to say that - sounds insane
    > :)


    _That_ is not _my_ logic. My logic is that in the example provided by
    the OP, there are sets of circumstances that make explicit use of
    LinkedHashMap reasonable. These sets of circumstances may include both
    the providing and calling code being non-published (that is, both of
    them are involved in implementation details), and also the desire of the
    designer/implementer to absolutely lock in this choice of Map
    implementation in that non-published code.

    Fact is, we don't know how public/published the OP intends that
    map-producing method to be. Since we don't know, I've repeatedly tried
    to more clearly explain the specific circumstances where I believe that
    not using Map won't offend against the "program to the interface"
    religion. I'm getting that there is quite a large group out there that
    doesn't countenance *ever* breaking "program to the interface". So be it.

    AHS
    Arved Sandstrom, May 10, 2011
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jim Janney
    Replies:
    43
    Views:
    1,010
    Arved Sandstrom
    May 15, 2011
  2. Steven Simpson
    Replies:
    5
    Views:
    331
  3. Roedy Green
    Replies:
    1
    Views:
    311
    Jim Janney
    May 6, 2011
  4. Pallav singh
    Replies:
    0
    Views:
    341
    Pallav singh
    Jan 22, 2012
  5. Pallav singh
    Replies:
    0
    Views:
    387
    Pallav singh
    Jan 22, 2012
Loading...

Share This Page