Design question - methods calling methods

L

Lew

Lew said:
It's a best practice to add a lot of "potentially useful" cruft to a
type. Implement what you need now, and refactor later if the need arises.

Darn it!

It's a best practice NOT to add a lot of cruft. NOT to.

Somehow I proofread best after I've embarrassed myself.
 
R

Rhino

Rhino, read that sentence again.

And that one.

And again.

Now proceed with the rest of this post.
For a second there, I had no idea what you were proposing.

But I reviewed the Collection Classes in the Java Tutorial and I
think I get it now.

In all honesty, I hadn't even thought of SortedMap when I wrote the
code again the other day. I just knew that I wanted the result to be
in alphabetical order, not random the way that
Locales.getAvailableLocales() provides them.

You make that desire concrete by the type of the object holding the
locales - you want them to be sorted, so it's a SortedMap.
The article on the Map interface pointed out that TreeMap would
assure that I had alphabetical order so I went with that. Now that
you've reminded me about SortedMap, I can see the merit of it. It's
not much different than TreeMap but it does give those additional
features, like range operations. I don't see those as being
_necessary_ for my humble little getLocales() method, which I'm
really just writing for myself, but some future user of the class
could conceivably benefit from those extra features. Or maybe _I_
will get a benefit from those features a little further down the
road!

You're on the wrong track here. The extra features are not what
SortedMap is about - it's fundamentally about that first paragraph in
its javadoc:

A Map that further provides a total ordering on its keys. The map is
ordered according to the natural ordering of its keys [...] This
order is reflected when iterating over the sorted map's collection
views (returned by the entrySet, keySet and values methods). Several
additional operations are provided to take advantage of the
ordering.

Yes, there are several additional operations. But the heart of the
matter is that the map has an order, which governs iteration over its
contents.
Sorry, I phrased my remarks poorly. I know the range handling and all
that are more along the lines of bonus features; the order of the data
is, of course, the main thing.
Spot on.


Yes. You did *exactly* what Lew meant when he said:

It's mostly a good idea to declare variables as an interface type
rather than a concrete type.

SortedMap is an interface type - it says 'this map is sorted somehow'.
TreeMap is a concrete type - it says 'this map is implemented as a
red-black tree, which incidentally results in it being sorted'.


It happens to be a TreeMap, but all the type declares is that it's a
map.


It happens to be a TreeMap, but all the type declares is that it's a
sorted map.


Illegal. SortedMap is an interface.

I didn't actually try that; I just threw it out to illustrate how these
things sort of all blur together in my brain....
Those last two examples are an exact analogue. The first one is a
little different - but a natural extension of the idea.

The third version says "I care that this is object is specifically a
GregorianCalendar. I am going to do things with it which specifically
would not work with any other kind of calendar.". There are times when
you might want to say that, but you would strive to be more general -
in which case you would use the second version, which says "I care
that this object is a Calendar, but i don't care which kind of
calendar - it could be Gregorian, Islamic, Japanese Imperial, Darian
Jovian, etc". However, in the second form, although you will proceed
to write your code calendar-agnostically, you are actually hardcoding
the choice of GregorianCalendar there. You could change it later, but
you'd have to go through your code and change the constructions. In
the first form, you push the calendar-agnosticism even into the
creation itself - rather than explicitly using a GregorianCalendar,
you let Calendar decide what kind of calendar should be used. Right
now, that will presumably always be a GregorianCalendar, but if at
some point someone boots up your software on Ganymede (the moon, not
the Eclipse release), it should probably be Darian, and by leaving the
decision to Calendar.getInstance, you let that happen without having
to alter your code. You delegate the decision to someone in a better
position to make it.

Now, in this particular case, there is a major caveat: different types
of Calendar are not interchangeable in the way that different types of
Map are, because they represent different ideas. If you're using this
Calendar object to handle dates which are definitively in Gregorian
(January, February and all that), then you need an actual
GregorianCalendar, and you should declare your variables accordingly.
To paraphrase Einstein, "everything should be made as generic as
possible, but no more so".
Thanks for clarifying the differences between the two groups of
statements I listed above. You did quite a nice job and I feel that I
have a little better grasp of it. But I'd be lying if I really felt I
have a really solid handle on things. I feel like it should all be more
obvious somehow and not require me to rack my brain each time this kind
of thing comes up to try to understand just what the key distinction is
between X and X'.....

That would be a real mistake. You should be making a tree. Have you
learned NOTHING?

LOL!! Good one!
The choice between Map and SortedMap here is not one of correctness.
It's one of design and style. Good design and style doesn't change the
way code runs, but it makes it easier to understand and modify. A huge
part of the business of software is understanding and modifying
existing code, and so good design and style are valuable. They're
investment for the future, rather than the here and now.
I'm fine with those general principles. I have done maintenance
programming and I have been handed badly written programs that made me
cringe just to look at them - as I'm sure we all have. I've always gone
the extra mile to make my code just as good as I was capable of making it
in every way that I could: make it work, make it clear, make it easy to
read, etc.

Thanks for your advice and your clear explanations, Tom! Much
appreciated!
 
R

Rhino

Rhino, read that sentence again.

And that one.

And again.

Now proceed with the rest of this post.
For a second there, I had no idea what you were proposing.

But I reviewed the Collection Classes in the Java Tutorial and I
think I get it now.

In all honesty, I hadn't even thought of SortedMap when I wrote the
code again the other day. I just knew that I wanted the result to be
in alphabetical order, not random the way that
Locales.getAvailableLocales() provides them.

You make that desire concrete by the type of the object holding the
locales - you want them to be sorted, so it's a SortedMap.
The article on the Map interface pointed out that TreeMap would
assure that I had alphabetical order so I went with that. Now that
you've reminded me about SortedMap, I can see the merit of it. It's
not much different than TreeMap but it does give those additional
features, like range operations. I don't see those as being
_necessary_ for my humble little getLocales() method, which I'm
really just writing for myself, but some future user of the class
could conceivably benefit from those extra features. Or maybe _I_
will get a benefit from those features a little further down the
road!

You're on the wrong track here. The extra features are not what
SortedMap is about - it's fundamentally about that first paragraph in
its javadoc:

A Map that further provides a total ordering on its keys. The map is
ordered according to the natural ordering of its keys [...] This
order is reflected when iterating over the sorted map's collection
views (returned by the entrySet, keySet and values methods). Several
additional operations are provided to take advantage of the
ordering.

Yes, there are several additional operations. But the heart of the
matter is that the map has an order, which governs iteration over its
contents.
Sorry, I phrased my remarks poorly. I know the range handling and all
that are more along the lines of bonus features; the order of the data
is, of course, the main thing.
Spot on.


Yes. You did *exactly* what Lew meant when he said:

It's mostly a good idea to declare variables as an interface type
rather than a concrete type.

SortedMap is an interface type - it says 'this map is sorted somehow'.
TreeMap is a concrete type - it says 'this map is implemented as a
red-black tree, which incidentally results in it being sorted'.


It happens to be a TreeMap, but all the type declares is that it's a
map.


It happens to be a TreeMap, but all the type declares is that it's a
sorted map.


Illegal. SortedMap is an interface.

I didn't actually try that; I just threw it out to illustrate how these
things sort of all blur together in my brain....
Those last two examples are an exact analogue. The first one is a
little different - but a natural extension of the idea.

The third version says "I care that this is object is specifically a
GregorianCalendar. I am going to do things with it which specifically
would not work with any other kind of calendar.". There are times when
you might want to say that, but you would strive to be more general -
in which case you would use the second version, which says "I care
that this object is a Calendar, but i don't care which kind of
calendar - it could be Gregorian, Islamic, Japanese Imperial, Darian
Jovian, etc". However, in the second form, although you will proceed
to write your code calendar-agnostically, you are actually hardcoding
the choice of GregorianCalendar there. You could change it later, but
you'd have to go through your code and change the constructions. In
the first form, you push the calendar-agnosticism even into the
creation itself - rather than explicitly using a GregorianCalendar,
you let Calendar decide what kind of calendar should be used. Right
now, that will presumably always be a GregorianCalendar, but if at
some point someone boots up your software on Ganymede (the moon, not
the Eclipse release), it should probably be Darian, and by leaving the
decision to Calendar.getInstance, you let that happen without having
to alter your code. You delegate the decision to someone in a better
position to make it.

Now, in this particular case, there is a major caveat: different types
of Calendar are not interchangeable in the way that different types of
Map are, because they represent different ideas. If you're using this
Calendar object to handle dates which are definitively in Gregorian
(January, February and all that), then you need an actual
GregorianCalendar, and you should declare your variables accordingly.
To paraphrase Einstein, "everything should be made as generic as
possible, but no more so".
Thanks for clarifying the differences between the two groups of
statements I listed above. You did quite a nice job and I feel that I
have a little better grasp of it. But I'd be lying if I really felt I
have a really solid handle on things. I feel like it should all be more
obvious somehow and not require me to rack my brain each time this kind
of thing comes up to try to understand just what the key distinction is
between X and X'.....

That would be a real mistake. You should be making a tree. Have you
learned NOTHING?

LOL!! Good one!
The choice between Map and SortedMap here is not one of correctness.
It's one of design and style. Good design and style doesn't change the
way code runs, but it makes it easier to understand and modify. A huge
part of the business of software is understanding and modifying
existing code, and so good design and style are valuable. They're
investment for the future, rather than the here and now.
I'm fine with those general principles. I have done maintenance
programming and I have been handed badly written programs that made me
cringe just to look at them - as I'm sure we all have. I've always gone
the extra mile to make my code just as good as I was capable of making it
in every way that I could: make it work, make it clear, make it easy to
read, etc.

Thanks for your advice and your clear explanations, Tom! Much
appreciated!
 
L

Lew

On 05/22/2010 09:30 PM,

Tom Anderson wrote
I didn't actually try that;

Clearly not.
I just threw it out to illustrate how these things
sort of all blur together in my brain....

You were advised not to do that. Strongly.

I strongly advise you, again!, to stick with SSCCEs until you stop making that
kind of mistake. That you have not learned your lesson about posting
uncompilable code is starting to make you look careless and lazy.

(Naturally this does not apply to posts that intentionally illustrate code
that does not compile, but such posts invariably include verbiage that
indicates that the code is intentionally illegal.)
 
R

Rhino

Lew said:
Rhino said:
In all honesty, I hadn't even thought of SortedMap when I wrote the
code again the other day. I just knew that I wanted the result to be
in alphabetical order, not random the way that
Locales.getAvailableLocales() provides them. The article on the Map
interface pointed out that TreeMap would assure that I had
alphabetical order so I went with that. Now that you've reminded me
about SortedMap, I can see the merit of it. It's not much different
than TreeMap but it does give those additional features,

WTF are you talking about? I got lost in your antecedent-free
pronouns. Are you referring to the methods in 'TreeMap' that are not
implementations of 'SortedMap' methods?

As for 'SortedMap' not being "much different [from] TreeMap', well
that makes perfect sense since 'TreeMap' implements 'SortedMap'.
OTOH, some might say that an interface in some ways is always "much
different" from a concrete class, but that difference is the basis of
the recommendation to move to a wider ("looser") type.
like range operations. I don't see those as being _necessary_ for my
humble little getLocales() method, which I'm really just writing for
myself, but some future user of the class could conceivably benefit
from those extra features. Or maybe _I_ will get a benefit from those
features a little further down the road! I've modified the code to
produced a SortedMap - just replaced all "Map" with "SortedMap", dead
easy! - and reran my unit tests. Naturally, they still worked fine.

You're the API writer. YOU dictate what "some future user" gets to
do.
Fair enough.
It's a best practice to add a lot of "potentially useful" cruft to a
type. Implement what you need now, and refactor later if the need
arises.
(I saw your amendment about the missing "not" in that paragraph.)

I probably tend to overbuild a lot of the time anyway, in the sense of
adding functionality that isn't all that likely to be used. My current
thread about the internationalization/localization is proof of that. I
tend to anticipate needs that others aren't sure will ever happen. Not
all the time of course; it's just a tendency, not an obsession ;-)
If you need access the 'TreeMap' methods that aren't part of the
'SortedMap' interface, then by all means declare the variable that
needs that access as 'TreeMap', otherwise don't.


LOL.

Since 'TreeMap' /is-a/ 'SortedMap', that's perfectly legit. You can,
and often should upcast without danger.


Yes.
Good :)
This variable 'sortedLocales' only has access to features promised by
the 'Map' interface, not the additional methods of the 'TreeMap' or
'SortedMap' types.

It's also named with a lie, because the code could change to assign an
unsorted 'Map' to the variable without causing a compiler error, thus
creating a problem at run time. You never want to push
compile-time-avoidable mistakes to run time.


The variable 'sortedLocales' has access to the methods of 'SortedMap'
but not those of 'TreeMap' not in the 'SortedMap' type. The name is
not a lie, because any refactoring of the code must use a 'SortedMap'
subtype to assign to the variable.


This is "codecrap" as Eric likes to call it. It will not compile.
I was just throwing that out to illustrate the way these similar-looking
bits of code sometimes blur together in my brain....
Are you familiar with the difference between interfaces and classes?
I'm not sure how to answer that. I've read formal definitions of both and
have written both and gotten them to run. But even after all the time
I've spent writing Java - I started in 1997 but had a 4 year gap that
ended a few months back where I wrote no Java at all - I don't feel solid
on a lot of the theory. Maybe I'm expecting too much of myself but I feel
like I should know instantly and intuitively what the key differences are
between classes and interfaces and not have to wrestle with "should I use
an interface or class here". But maybe guys like you and Eric and Tom
have to think long and hard about that too. I really don't know.
"So forth"? What "so forth"? What do you mean?
Just that I could imagine other variants on the same sort of statements.
These examples are by no means "the same token".
I didn't mean to suggest that the calendar examples had anything to do
with the Map examples, just that the three statements in each group LOOK
similar but have very important differences. Unfortunately, those
differences don't jump out at me when I see them used in examples or
whatnot. I feel like it should be dead obvious instantly why the
programmer wrote one or the other or what bad things would happen if I
changed one to the other. But that doesn't happen for me.
This does not guarantee that you will get a 'GregorianCalendar' or any
other particular type of 'Calendar'.


This guarantees that 'cal' points to a 'GregorianCalendar', but the
behaviors specific to 'GregorianCalendar' but not 'Calendar' cannot be
accessed through it.


This guarantees that 'gal' points to a 'GregorianCalendar', and the
behaviors specific to 'GregorianCalendar' can be accessed through it.


cal.hashCode()
Good one :)
No.

Employers hire people at all levels of experience and knowledge, with
appropriate adjustments to compensation, depending on the mix they
need for their team. I know of very few projects that hire only
virtuosos, and I've worked on many that actually hired incompetent
programmers.

Which last you aren't, BTW.
Well thank you, that's a very nice thing to say. Maybe I just expect too
much of myself. I suppose there's a certain envy here. I see guys like
you and Eric and Tom explaining things so articulately to people like me
and I wish that I had the confidence in my own ability to be able to
answer those questions as well. But it feels like 95% of the people in
this group know a lot more than I do.....

Still, I'm going to learn a lot more hanging around here getting
questions answered by people who know their stuff than by hanging around
with people who know less than I do. I'll keep plugging away at it. Maybe
in a couple of years I'll feel a little more capable of answering the
kind of questions most people here ask....

Thanks for your help with this - and so many of my other questions!
 
L

Lew

I'm not sure how to answer that. I've read formal definitions of both and
have written both and gotten them to run. But even after all the time
I've spent writing Java - I started in 1997 but had a 4 year gap that
ended a few months back where I wrote no Java at all - I don't feel solid
on a lot of the theory. Maybe I'm expecting too much of myself but I feel
like I should know instantly and intuitively what the key differences are
between classes and interfaces and not have to wrestle with "should I use
an interface or class here".

The Java tutorial describes the difference, but doesn't really address your
question, so I'll take a stab.

Interfaces are pure contract - they describe the signatures of the methods
that class instances must implement, but may not contain any method
implementation nor instance members. (It's also usually a bad idea to give
them 'static' members.)

Classes may contain some implementation, and must be completely implemented if
not 'abstract'.

A class can implement an interface - that is, it is a subtype of the interface
that fills in the missing implementation details.

So you can have an interface:

public interface Foo
{
public void fooishBehavior();
}

Notice the semicolon instead of a method body - no curly braces in the method.
It's a promise that there will be such a method in any class that implements
the interface.

You can have an implementing class:

public class FooImpl implements Foo
{
public void fooishBehavior()
{
}
}

The class's claim that it 'implements Foo' requires it to implement the 'Foo'
methods, or at least be an abstract class. We'll ignore abstract classes for
you to study on your own.

The interface is a type - this is the key. All subtype classes or interfaces
are necessarily also of that type. 'extends' and 'implements' both express a
subtype relationship, and a subtype thing /is-a/ thing of its parent types.
(Inheritance expresses /is-a/; membership, a.k.a. composition, expresses /has-a/.)

The key is to think in terms of types, not classes. Interfaces are pure
expressions of type, and classes express a way for the type to do something
actual.

Different implementors of an interface can do quite different things, but they
must do the things promised by the type they implement.

Think of "type" as a contract - it shall have these behaviors!

All implementors of 'Foo' above must implement (or pass on the responsibility
abstractly to implement) that 'fooishBehavior()' method. How they do so is up
to them.

Variables have a type - that's what's important. When you use a variable of
type 'Foo', all you really care about is to use its 'fooishBehavior()'.

You should not (usually - always the disclaimer) care how the thing does its
'fooishBehavior()'.

So you typically have a construct like

Foo foo = new FooImpl();

Since everything of a subtype ('FooImpl') /is-a/ thing of the supertype
('Foo'), this is legal, and since 'Foo' is the type you care about, this is
proper.

Later you might refactor the code. Perhaps you discover that
'FooImpl#fooishBehavior()' is not thread safe but you need it to be. However,
the logic of the program doesn't need to know that, only that 'foo' has the
method, safe or not. So you change that initialization to:

Foo foo = new ConcurrentFooImpl();

The rest of the program, knowing only that 'foo' is of type 'Foo', needs no
rewrite. The hidden secret of the refactored 'foo' buys you thread safety,
and the rest of the program needs no recompile. Magic!

This came up in a project where I worked when we substituted something like

Map <Foo, Bar> whatever = Collections.synchronizedMap(
new HashMap <Foo, Bar> () );

with

Map <Foo, Bar> whatever = new ConcurrentHashMap <Foo, Bar> ();

and got a huge boost in the program's performance.
 
L

Lew

I'm not sure how to answer that. I've read formal definitions of both and
have written both and gotten them to run. But even after all the time
I've spent writing Java - I started in 1997 but had a 4 year gap that
ended a few months back where I wrote no Java at all - I don't feel solid
on a lot of the theory. Maybe I'm expecting too much of myself but I feel
like I should know instantly and intuitively what the key differences are
between classes and interfaces and not have to wrestle with "should I use
an interface or class here".

The Java tutorial describes the difference, but doesn't really address your
question, so I'll take a stab.

Interfaces are pure contract - they describe the signatures of the methods
that class instances must implement, but may not contain any method
implementation nor instance members. (It's also usually a bad idea to give
them 'static' members.)

Classes may contain some implementation, and must be completely implemented
(if not 'abstract').

A class can implement an interface - that is, it is a subtype of the interface
that fills in the missing implementation details.

So you can have an interface:

public interface Foo
{
public void fooishBehavior();
}

Notice the semicolon instead of a method body - no curly braces in the method.

It's a promise that there will be such a method in any class that implements
the interface.

You can have an implementing class:

public class FooImpl implements Foo
{
public void fooishBehavior()
{
}
}

The class's claim that it 'implements Foo' requires it to implement the 'Foo'
methods, or at least be an abstract class. We'll ignore abstract classes for
you to study on your own.

The interface is a type - this is the key. All subtype classes or interfaces
are necessarily also of that type. 'extends' and 'implements' both express a
subtype relationship, and a subtype thing /is-a/ thing of its parent types.
(Inheritance expresses /is-a/; membership, a.k.a. composition, expresses /has-a/.)

The key is to think in terms of types, not classes. Interfaces are pure
expressions of type, and classes express a way for the type to do something
actual.

Different implementors of an interface can do quite different things, but they
must do the things promised by the type they implement.

Think of "type" as a contract - it shall have these behaviors!

All implementors of 'Foo' above must implement (or pass on the responsibility
abstractly to implement) that 'fooishBehavior()' method. How they do so is up
to them.

Variables have a type - that's what's important. When you use a variable of
type 'Foo', all you really care about is to use its 'fooishBehavior()'.

You should not (usually - always the disclaimer) care how the thing does its
'fooishBehavior()'.

So you typically have a construct like

Foo foo = new FooImpl();

Since everything of a subtype ('FooImpl') /is-a/ thing of the supertype
('Foo'), this is legal, and since 'Foo' is the type you care about, this is
proper.

Later you might refactor the code. Perhaps you discover that
'FooImpl#fooishBehavior()' is not thread safe but you need it to be. However,
the logic of the program doesn't need to know that, only that 'foo' has the
method, safe or not. So you change that initialization to:

Foo foo = new ConcurrentFooImpl();

The rest of the program, knowing only that 'foo' is of type 'Foo', needs no
rewrite. The hidden secret of the refactored 'foo' buys you thread safety,
and the rest of the program needs no recompile. Magic!

This came up in a project where I worked when we substituted something like

Map <Foo, Bar> whatever = Collections.synchronizedMap(
new HashMap <Foo, Bar> () );

with

Map <Foo, Bar> whatever = new ConcurrentHashMap <Foo, Bar> ();

and got a huge boost in the program's performance.
 
T

Tom Anderson

Darn it!

It's a best practice NOT to add a lot of cruft. NOT to.

Somehow I proofread best after I've embarrassed myself.

It does seem a lot easier to find mistakes after you've committed them to
the internet. Perhaps we should agree that this is our new advanced
posting technique - post a rough draft first, then post the edits when you
realise what the mistakes are!

tom
 
T

Tom Anderson

Lew said:
Rhino said:
In all honesty, I hadn't even thought of SortedMap when I wrote the
code again the other day. I just knew that I wanted the result to be
in alphabetical order, not random the way that
Locales.getAvailableLocales() provides them. The article on the Map
interface pointed out that TreeMap would assure that I had
alphabetical order so I went with that. Now that you've reminded me
about SortedMap, I can see the merit of it. It's not much different
than TreeMap but it does give those additional features,

WTF are you talking about? I got lost in your antecedent-free
pronouns. Are you referring to the methods in 'TreeMap' that are not
implementations of 'SortedMap' methods?

As for 'SortedMap' not being "much different [from] TreeMap', well
that makes perfect sense since 'TreeMap' implements 'SortedMap'.
OTOH, some might say that an interface in some ways is always "much
different" from a concrete class, but that difference is the basis of
the recommendation to move to a wider ("looser") type.
like range operations. I don't see those as being _necessary_ for my
humble little getLocales() method, which I'm really just writing for
myself, but some future user of the class could conceivably benefit
from those extra features. Or maybe _I_ will get a benefit from those
features a little further down the road! I've modified the code to
produced a SortedMap - just replaced all "Map" with "SortedMap", dead
easy! - and reran my unit tests. Naturally, they still worked fine.

You're the API writer. YOU dictate what "some future user" gets to
do.
Fair enough.
It's a best practice to add a lot of "potentially useful" cruft to a
type. Implement what you need now, and refactor later if the need
arises.
(I saw your amendment about the missing "not" in that paragraph.)

I probably tend to overbuild a lot of the time anyway, in the sense of
adding functionality that isn't all that likely to be used. My current
thread about the internationalization/localization is proof of that. I
tend to anticipate needs that others aren't sure will ever happen. Not
all the time of course; it's just a tendency, not an obsession ;-)

This is a universal tendency. Everyone has it to some extent. From what i
remember of the dim depths of time, i had it rather strongly when i was
starting out programming, although it manifested itself in writing
overcomplicated class hierarchies and mad epicyclic structures of composed
objects. Kent Beck once accused me of suffering from 'premature
abstraction'. I think it's common to suffer from it strongly when you
first start thinking about design, and to grow out of it in time. Well, or
to become an architect, architects being the Peter Pans of the programming
world.
I'm not sure how to answer that. I've read formal definitions of both
and have written both and gotten them to run. But even after all the
time I've spent writing Java - I started in 1997 but had a 4 year gap
that ended a few months back where I wrote no Java at all - I don't feel
solid on a lot of the theory. Maybe I'm expecting too much of myself but
I feel like I should know instantly and intuitively what the key
differences are between classes and interfaces and not have to wrestle
with "should I use an interface or class here". But maybe guys like you
and Eric and Tom have to think long and hard about that too. I really
don't know.

It's unusual to be in a situation where there is a real choice between a
class and an interface. When you're deciding the type of a variable, you
ask, as Lew said, "what is the most general type that guarantees the
behaviour i want from this variable". You don't worry about whether that
type is a class or an interface.

When you're designing an inheritance hierarchy to exploit polymorphism,
you can choose to make the root type an interface or an exposed base
class. Like:

public abstract class DataStore {
public abstract Data load(String id);
}
public class RelationalDataStore extends DataStore {
public Data load(String id) {
// code
}
}

vs

public interface DataStore {
public Data load(String id);
}
public class RelationalDataStore implements DataStore {
public Data load(String id) {
// code
}
}

The mainstream modern school of thought is that you should always choose
the interface, so there's no real dilemma there either.

tom
 
A

Arne Vajhøj

Rhino wrote:
...
...

I also get tempted to overbuild. When I started programming, there was
some justification, because making design changes in a large program was
difficult and risky.

These days, it is so easy to refactor that there is generally little
benefit to adding complexity that is not needed for immediate needs.

The exception is designing APIs that will be used in code outside your
control.

API's is a problem because you the most optimal refactoring may
expose an incompatible API.

But I believe that there are also other cases where refactoring
is not so easy.

One example is the topic of the thread. Internationalizing an
existing app is not difficult, but it is usually a lot of work.

I18N is a well-known counter-argument to YAGNI.

Arne
 
A

Arne Vajhøj

This is a universal tendency. Everyone has it to some extent. From what
i remember of the dim depths of time, i had it rather strongly when i
was starting out programming, although it manifested itself in writing
overcomplicated class hierarchies and mad epicyclic structures of
composed objects. Kent Beck once accused me of suffering from 'premature
abstraction'. I think it's common to suffer from it strongly when you
first start thinking about design, and to grow out of it in time.

http://www.ariel.com.au/jokes/The_Evolution_of_a_Programmer.html

I would word it a little bit different: abstraction is good - *IF* it
serves a purpose.

Well,
or to become an architect, architects being the Peter Pans of the
programming world.
:)


It's unusual to be in a situation where there is a real choice between a
class and an interface. When you're deciding the type of a variable, you
ask, as Lew said, "what is the most general type that guarantees the
behaviour i want from this variable". You don't worry about whether that
type is a class or an interface.

When you're designing an inheritance hierarchy to exploit polymorphism,
you can choose to make the root type an interface or an exposed base
class. Like:

public abstract class DataStore {
public abstract Data load(String id);
}
public class RelationalDataStore extends DataStore {
public Data load(String id) {
// code
}
}

vs

public interface DataStore {
public Data load(String id);
}
public class RelationalDataStore implements DataStore {
public Data load(String id) {
// code
}
}

The mainstream modern school of thought is that you should always choose
the interface, so there's no real dilemma there either.

I think that is exaggarating.

Inheriting from abstract base classes is used all over the
Java API.

Joshua Bloch used the word "Prefer" in this context.

Sometimes is do make sense to reuse implementation functionality
(which is what the abstract base class offers).

Quite frequently a compromise is used with both an interface
and an abstract base class. This means that the 95% can
extend and only implement the few methods they need, but
still allow the remaining 5% to implement the interface
with full flexibility.

Arne
 
R

Rhino

Lew said:
On 05/22/2010 09:30 PM,

Tom Anderson wrote


Clearly not.


You were advised not to do that. Strongly.

I strongly advise you, again!, to stick with SSCCEs until you stop
making that kind of mistake. That you have not learned your lesson
about posting uncompilable code is starting to make you look careless
and lazy.
Honestly, I think you're being just a little too rigid now.

I was trying to get across the idea that, at first glance, Map<String,
Locale> sorted Locales = new SortedMap<String, Locale>() might be
something I would CONSIDER writing to address the suggestion that was
made to me. I was trying to get across the idea that these statements all
start to look alike and interchangeable at some point, at least to me. I
know that this is not actually the case but it SEEMS like it sometimes.
Fundamentally, I'm just expressing frustration with myself that it isn't
more intuitive by now which of these statements is the one to use and
which ones don't even make sense.

I know (now) that the statement in question wouldn't actually compile or
make sense and I didn't actually try to compile it anywhere. It was
strictly a hypothetical and I truly think it is reasonable to toss out a
hypothetical now and again even if it is only to dismiss it very quickly
as the responders rightly did. I will continue to do that - very
sparingly I expect - and do so at my own peril. Maybe some of you will
disgusted with me for doing so and stop responding to me. If so, that's
life. I'll do what penance is needed to get back into everyone's good
graces or move on to some other discussion group if I have to.
 
R

Rhino

API's is a problem because you the most optimal refactoring may
expose an incompatible API.

But I believe that there are also other cases where refactoring
is not so easy.

One example is the topic of the thread. Internationalizing an
existing app is not difficult, but it is usually a lot of work.

I18N is a well-known counter-argument to YAGNI.

Arne

YAGNI? Sorry, I don't know that acronym.....
 
L

Lew

Honestly, I think you're being just a little too rigid now.

You aren't being "rigid" enough.
I was trying to get across the idea that, at first glance, Map<String,
Locale> sorted Locales = new SortedMap<String, Locale>() might be
something I would CONSIDER writing to address the suggestion that was

You should never "CONSIDER" writing 'new SomeInterface()'.
I know (now) that the statement in question wouldn't actually compile or
make sense and I didn't actually try to compile it anywhere. It was

You should have done.
strictly a hypothetical and I truly think it is reasonable to toss out a
hypothetical now and again even if it is only to dismiss it very quickly

Not if it's blatantly ridiculous like 'new SomeInterface()' which on the very
face of it is obviously wrong.
as the responders rightly did. I will continue to do that - very
sparingly I expect - and do so at my own peril. Maybe some of you will
disgusted with me for doing so and stop responding to me. If so, that's
life. I'll do what penance is needed to get back into everyone's good
graces or move on to some other discussion group if I have to.

What the hell does anyone's "good graces" have to do with it?

We're, or at least I'm trying to provide information and advice that will help
you!

Now you petulantly whine that you're going continue to engage in bad practices
out of some bizarre obstinacy.

This only hurts yourself. It's not a question of anyone else's opinion but of
how you can best serve yourself in your attempt to progress as a developer.

You disregard the advice "at [your] own peril", all right, but it's not a
social peril, it's the peril that you'll never get any better as a software
developer.

I strongly advise you to get off your high horse about this issue.

"Penance". WTF?
 
R

Rhino

Lew said:
The Java tutorial describes the difference, but doesn't really address
your question, so I'll take a stab.

Interfaces are pure contract - they describe the signatures of the
methods that class instances must implement, but may not contain any
method implementation nor instance members. (It's also usually a bad
idea to give them 'static' members.)

Classes may contain some implementation, and must be completely
implemented if not 'abstract'.

A class can implement an interface - that is, it is a subtype of the
interface that fills in the missing implementation details.

So you can have an interface:

public interface Foo
{
public void fooishBehavior();
}

Notice the semicolon instead of a method body - no curly braces in the
method.
It's a promise that there will be such a method in any class that
implements
the interface.

You can have an implementing class:

public class FooImpl implements Foo
{
public void fooishBehavior()
{
}
}

The class's claim that it 'implements Foo' requires it to implement
the 'Foo' methods, or at least be an abstract class. We'll ignore
abstract classes for you to study on your own.
No worries; I've created and used some abstract classes in the past. I
was going to say they seemed quite straightforward but maybe I should
look into them again, just to make sure I'm not failing to consider
important aspects! But I'll do that on my own and post if I have
questions.

Having already read your note twice, I'm starting to have trouble
distinguishing between an interface that contains an abstract method and
an abstract class that has an abstract method. But I should probably mull
this over a bit more before attempting any followup questions. I don't
think I understand the interface thoroughly yet so no point in muddying
the waters with abstract classes just yet....
The interface is a type - this is the key. All subtype classes or
interfaces are necessarily also of that type. 'extends' and
'implements' both express a subtype relationship, and a subtype thing
/is-a/ thing of its parent types. (Inheritance expresses /is-a/;
membership, a.k.a. composition, expresses /has-a/.)

The key is to think in terms of types, not classes. Interfaces are
pure expressions of type, and classes express a way for the type to do
something actual.

Different implementors of an interface can do quite different things,
but they must do the things promised by the type they implement.

Think of "type" as a contract - it shall have these behaviors!

All implementors of 'Foo' above must implement (or pass on the
responsibility abstractly to implement) that 'fooishBehavior()'
method. How they do so is up to them.

So, because Set is an interface, all of its implementors, including
HashSet, LinkedHashSet and TreeSet, need to implement all of its (non-
abstract) methods. But HashSet can implement the contains() method
differently than TreeSet does if that seems advisable.

Variables have a type - that's what's important. When you use a
variable of type 'Foo', all you really care about is to use its
'fooishBehavior()'.
I always tend to think of variables as things like

String greeting = "Hello";

from my COBOL days. But you're using variable where I might say instance,
right? As in this case, where the variable myform is being instantiated:

Form myForm = new Form();

I'm pretty sure you mean the latter because 'myForm' in this case
presumably has some number of behaviours (methods) to do this and that.
Then again, by virtue of being a String, 'fred' has behaviours too, such
as length() and substring(). Hmm, maybe this is a "distinction without a
difference". After all,

String greeting = "Hello";

is equivalent to:

String greeting = new String("Hello");

You should not (usually - always the disclaimer) care how the thing
does its 'fooishBehavior()'.

So you typically have a construct like

Foo foo = new FooImpl();

Since everything of a subtype ('FooImpl') /is-a/ thing of the
supertype ('Foo'), this is legal, and since 'Foo' is the type you care
about, this is proper.
Or, to use an example from the API:

Set<String> mySet = new TreeSet<String>();

Would you consider that a parallel example? In other words, in your
example, was Foo an interface while FooImpl was a class?

Later you might refactor the code. Perhaps you discover that
'FooImpl#fooishBehavior()' is not thread safe but you need it to be.
However, the logic of the program doesn't need to know that, only that
'foo' has the method, safe or not. So you change that initialization
to:

Foo foo = new ConcurrentFooImpl();
Since I'm not 100% sure whether you mean Foo to be an interface or a
class, let me just ask if you're saying that your example is logically
equivalent to changing:

Set<String> mySet = new TreeSet<String>();

to

Set<String> mySet = new HashSet<String>();

In my example, you may have decided that the performance benefits of the
HashSet outweigh the fact that your data is in alphabetical order within
the TreeSet.

The rest of the program, knowing only that 'foo' is of type 'Foo',
needs no rewrite. The hidden secret of the refactored 'foo' buys you
thread safety, and the rest of the program needs no recompile. Magic!

This came up in a project where I worked when we substituted something
like

Map <Foo, Bar> whatever = Collections.synchronizedMap(
new HashMap <Foo, Bar> () );

with

Map <Foo, Bar> whatever = new ConcurrentHashMap <Foo, Bar> ();

and got a huge boost in the program's performance.

You have to love it when a one line change makes a big improvement like
that :)
 
R

Rhino

Rhino wrote:
In all honesty, I hadn't even thought of SortedMap when I wrote the
code again the other day. I just knew that I wanted the result to
be in alphabetical order, not random the way that
Locales.getAvailableLocales() provides them. The article on the Map
interface pointed out that TreeMap would assure that I had
alphabetical order so I went with that. Now that you've reminded me
about SortedMap, I can see the merit of it. It's not much different
than TreeMap but it does give those additional features,

WTF are you talking about? I got lost in your antecedent-free
pronouns. Are you referring to the methods in 'TreeMap' that are
not implementations of 'SortedMap' methods?

As for 'SortedMap' not being "much different [from] TreeMap', well
that makes perfect sense since 'TreeMap' implements 'SortedMap'.
OTOH, some might say that an interface in some ways is always "much
different" from a concrete class, but that difference is the basis
of the recommendation to move to a wider ("looser") type.

like range operations. I don't see those as being _necessary_ for
my humble little getLocales() method, which I'm really just writing
for myself, but some future user of the class could conceivably
benefit from those extra features. Or maybe _I_ will get a benefit
from those features a little further down the road! I've modified
the code to produced a SortedMap - just replaced all "Map" with
"SortedMap", dead easy! - and reran my unit tests. Naturally, they
still worked fine.

You're the API writer. YOU dictate what "some future user" gets to
do.
Fair enough.
It's a best practice to add a lot of "potentially useful" cruft to a
type. Implement what you need now, and refactor later if the need
arises.
(I saw your amendment about the missing "not" in that paragraph.)

I probably tend to overbuild a lot of the time anyway, in the sense
of adding functionality that isn't all that likely to be used. My
current thread about the internationalization/localization is proof
of that. I tend to anticipate needs that others aren't sure will ever
happen. Not all the time of course; it's just a tendency, not an
obsession ;-)

This is a universal tendency. Everyone has it to some extent. From
what i remember of the dim depths of time, i had it rather strongly
when i was starting out programming, although it manifested itself in
writing overcomplicated class hierarchies and mad epicyclic structures
of composed objects. Kent Beck once accused me of suffering from
'premature abstraction'. I think it's common to suffer from it
strongly when you first start thinking about design, and to grow out
of it in time. Well, or to become an architect, architects being the
Peter Pans of the programming world.
I remember a first year Algebra course at university where the professor
couldn't say ANYTHING without making it as abstract as humanly possible.
Where you or I might say "1 + 1 = 2", he'd write something like " N,
where it is a member of a universe of possible number systems which may
include integers, when added to X, where it is a member of a universe of
a possible number systems which may include integers, may result in Z,
where it is a member of a universe of possible number systems which may
include integers". Or something like that anyway. And of course he'd
write it on the board in proper algebraic notation which I've long
forgotten. The abstractions, which I'm grossly oversimplifying, made his
point, which was probably very simple, so obscure that I was utterly lost
within moments of him starting. I struggled as best I could for a couple
of weeks or so but I was so completely lost, I realized I would fail very
badly if I stayed in this class. I soon found another algebra course,
taught by a prof who was far less abstract, and was fine. As insightful
and interesting as the first prof's abstractions probably were, they were
way beyond anything I could follow at the time ;-) (For what it's worth,
a friend who knew others in the class said that a very large percentage
of the rest of the students in that class said virtually all of them had
dropped the course not long after I did. I hope they prof learned to be a
little more concrete from that experience :)

It's unusual to be in a situation where there is a real choice between
a class and an interface. When you're deciding the type of a variable,
you ask, as Lew said, "what is the most general type that guarantees
the behaviour i want from this variable". You don't worry about
whether that type is a class or an interface.
Sorry, I probably put my initial remarks badly.

Sigh. I've accumulated a number of confusions over my time with Java.
Sometimes, I can put them aside and still get by just fine. Sometimes, I
think I've finally seen the light and understand the obscure point for a
while, but then an example or a remark a newsgroup like this makes me
wonder if I understood the point correctly after all. And of course I'm
not sufficiently disciplined to actually write all of these confusions
down or to systematically resolve them via research and/or newsgroup
questions.....

I'm pretty sure that one of those fundamental confusions was on my mind
when I made my very general remark about confusion between interfaces and
classes but since I truly can't think of anything specific, and since I
don't have the time right now to mentally reconstruct and type my List of
Java Confusions for you, let's just put all that aside for some other
time.

Right now, I think I would benefit from the answer to a new mystery.

I do database stuff fairly frequently and mostly use my copy of DB2 as my
database engine. Naturally, I use ResultSets for a lot of this work. I
know that ResultSet is an interface, not a class, but it is rich in
exactly the sorts of methods I need to grab the Strings, integers and
whatnot in the database or to write them to the database. A short time
ago, I was looking at the source for ResultSet and found that not one of
the many methods in that interface are implemented; every single method
seems to be abstract. I see in the JavaDoc that there are a number of
"sub-interfaces" - I'm really not sure what a sub-interface is and how it
is different from a interface and that it has a "super-interface" -
again, I'm not sure what that is! - but the perplexing thing is that I am
not explicitly using the super-interface or any of the sub-interfaces yet
my code still works fine. That kind of thing disorients me; I wonder how
that could possibly be working and why I don't need to change from
ResultSet to a real class or another sub-interface or whatever. My
pragmatic self is capable of just ignoring all of that and saying "It's a
mystery but I'm not going to question it since it obviously works." But
my more idealistic self is bothered that it isn't obvious to me why the
ResultSet works despite its troubling aspects.

Can you possibly shed some light on what is happening there?
When you're designing an inheritance hierarchy to exploit
polymorphism, you can choose to make the root type an interface or an
exposed base class. Like:

public abstract class DataStore {
public abstract Data load(String id);
}
public class RelationalDataStore extends DataStore {
public Data load(String id) {
// code
}
}

vs

public interface DataStore {
public Data load(String id);
}
public class RelationalDataStore implements DataStore {
public Data load(String id) {
// code
}
}

The mainstream modern school of thought is that you should always
choose the interface, so there's no real dilemma there either.
Okay. That seems clear enough :)
 
E

Eric Sosman

Honestly, I think you're being just a little too rigid now.

Rhino, you are asking strangers for free help, free advice,
free assistance. Said strangers are accustomed to being paid for
exercising their expertise, but for reasons of their own they put
it at your disposal gratis. Under the circumstances, you are in
no position to dictate terms, much less to insist that they put
up with your persistent (intentional?) sloppiness.

If you'll play by my rules, I'm willing to try to help. But
if you insist on mocking them, I'll take my ball and go home.
Other potential helpers will have different preferences, and
different sticking points -- but sooner or later, you will have
to do things their way or pay them enough to put up with you.
Can't speak for others, but as for me: Mend your ways, or I will
go mine.

Beggars can't be choosers.

TANSTAAFL.
 
L

Lew

Having already read your note twice, I'm starting to have trouble
distinguishing between an interface that contains an abstract method and
an abstract class that has an abstract method. But I should probably mull
this over a bit more before attempting any followup questions. I don't
think I understand the interface thoroughly yet so no point in muddying
the waters with abstract classes just yet....

An interface MUST NOT have any implementation of its methods. It's pure type.

An abstract class MAY have implementation for any or all of its methods. It's
type with some implementation.

A type may inherit (extend or implement) any number of interfaces. A class
may only extend from one class.
So, because Set is an interface, all of its implementors, including
HashSet, LinkedHashSet and TreeSet, need to implement all of its (non-
abstract) methods.

There are no non-abstract methods in an interface.
But HashSet can implement the contains() method
differently than TreeSet does if that seems advisable.

Correct.
I always tend to think of variables as things like

String greeting = "Hello";

from my COBOL days. But you're using variable where I might say instance, right?

No.

An instance is the object itself. A variable is a pointer to the instance.
As in this case, where the variable myform is being instantiated:

Form myForm = new Form();

No, the variable is being initialized. The instance is being instantiated.
("Instance", "instantiate" - see the connection?)
I'm pretty sure you mean the latter because 'myForm' in this case
presumably has some number of behaviours (methods) to do this and that.

No, the instance has behaviors. The variable only gives you access to invoke
those behaviors.
Then again, by virtue of being a String, 'fred' has behaviours too, such
as length() and substring(). Hmm, maybe this is a "distinction without a
difference". After all,

I don't know what you're talking about here. Again, the variable does not
have behaviors, it only gives you (possibly limited) access to those behaviors
by pointing to the object that actually does have the behaviors (and possibly
others to which the variable does not give access).
String greeting = "Hello";

is equivalent to:

String greeting = new String("Hello");

No, those are not at all equivalent.
Or, to use an example from the API:

Set<String> mySet = new TreeSet<String>();

Would you consider that a parallel example? In other words, in your
example, was Foo an interface while FooImpl was a class?

Did you actually *read* the sample code I provided for 'Foo' and 'FooImpl'?
The answer is there:
Tch, tch.
Since I'm not 100% sure whether you mean Foo to be an interface or a
class, let me just ask if you're saying that your example is logically

I really don't get how you aren't. I get the feeling you aren't paying attention.
equivalent to changing:

Set<String> mySet = new TreeSet<String>();

to

Set<String> mySet = new HashSet<String>();

In my example, you may have decided that the performance benefits of the
HashSet outweigh the fact that your data is in alphabetical order within
the TreeSet.

What performance benefits?

Given that you need results in sorted order, you are nearly certain not to
have any. Even without that, what performance benefits?

Performance is never an argument that wins over logical correctness.

"I can give you wrong answers in half the time of correct ones!"
 
L

Lew

Please trim your posts.
Right now, I think I would benefit from the answer to a new mystery.

Should have a new thread, then.
I do database stuff fairly frequently and mostly use my copy of DB2 as my
database engine. Naturally, I use ResultSets for a lot of this work. I
know that ResultSet is an interface, not a class, but it is rich in
exactly the sorts of methods I need to grab the Strings, integers and
whatnot in the database or to write them to the database. A short time
ago, I was looking at the source for ResultSet and found that not one of
the many methods in that interface are implemented; every single method
seems to be abstract. I see in the JavaDoc that there are a number of
"sub-interfaces" - I'm really not sure what a sub-interface is and how it
is different from a interface and that it has a "super-interface" -
again, I'm not sure what that is! - but the perplexing thing is that I am
not explicitly using the super-interface or any of the sub-interfaces yet
my code still works fine. That kind of thing disorients me; I wonder how
that could possibly be working and why I don't need to change from
ResultSet to a real class or another sub-interface or whatever. My
pragmatic self is capable of just ignoring all of that and saying "It's a
mystery but I'm not going to question it since it obviously works." But
my more idealistic self is bothered that it isn't obvious to me why the
ResultSet works despite its troubling aspects.

Can you possibly shed some light on what is happening there?

Yes.

OK, I will actually do so.

'ResultSet' is not implemented in the standard Java API, but in the JDBC driver.

The instantiating class is always a subtype of 'ResultSet' specific to that
driver.

The details are hidden by the driver library. That's the WHOLE POINT of
interface-driven programming (a subset of type-driven programming)!

ALL you need to know is the interface! You want to AVOID the impulse "to
change ... to a real [sic] class or another sub-interface or whatever".

'ResultSet' works, like all interface-declared variables, because the variable
always points to an actual object which is an instance of a class.

VARIABLES ARE POINTERS!

The actual action happens in the object to which the variable points.

This is the heart and soul of polymorphism - you refer (point) to a supertype,
but the behavior is enacted by an instance of the subtype.

All the variable does is give you access to the behavior through the public
members of the variable's type. That access is always to the actual behavior
of the instance.

VARIABLES ARE POINTERS!

All your code cares about is that 'ResultSet' has certain behaviors. Those
are the behaviors promised by the type.

That way, when you change databases, say from (blecch!) MySQL to (ahhhh!)
PostgreSQL, you don't have to rewrite your code much. You still have a
'ResultSet' whose members are the members your code is already using; at run
time the JDBC driver returns a pointer to the actual instance to the
'ResultSet' variable.

VARIABLES ARE POINTERS!
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top