Tired of 100s of stupid Getter/Setter methods

D

Dimitri Maziuk

Mark 'Kamikaze' Hughes sez:
Dimitri Maziuk <[email protected]>

Hmn. My other main OOP experience was with Objective C, which has a
somewhat Java-like inheritance system, but I just can't see the value of
creating things by composition that aren't composite.

Most OO courses spend a lot of time praising benefits of
inheritance, so people tend to use it where it isn't
entirely appropriate. Or have hard time deciding where it
is appropriate.

E.g. Stack based on Vector -- it "is a" vector, in a sense,
but without random access to the elements. So you either
have to redeclare most Vector's methods as private in
Stack to hide them (you only want isEmpty(), push(),
and pop()) -- that's a lot of typing. If language does
not allow that trick, you'll have to go the other way
and have BaseVector with everything protected, from
which you derive both Stack and Vector. That's just as
much typing, but this time redeclaring protected BaseVector
methods public in Vector, plus an extra class.

Google for oop composition vs inheritance, it should turn
up a better example. And a few other reasons for using
composition instead of inheritance.

....Java does, of
course, allow multiple inheritance in the only way that's safe: through
interfaces.

Well, I'd say interface is a behaviour specification that
has nothing to do with inheritance. As an added bonus it
allows you to fake multiple inheritance without having to
deal with its problems. That definitely has its pluses, yes.

Dima
 
D

Dave Glasser

Mark 'Kamikaze' Hughes sez:

Most OO courses spend a lot of time praising benefits of
inheritance, so people tend to use it where it isn't
entirely appropriate. Or have hard time deciding where it
is appropriate.

E.g. Stack based on Vector -- it "is a" vector, in a sense,
but without random access to the elements. So you either
have to redeclare most Vector's methods as private in
Stack to hide them (you only want isEmpty(), push(),
and pop()) -- that's a lot of typing. If language does
not allow that trick,

Java does not. Methods can be overridden with less restrictive access
(like the common practice of overriding Object.clone() with a public
clone() method) but not more restrictive access.
 
T

Tor Iver Wilhelmsen

Dave Glasser said:
Java does not. Methods can be overridden with less restrictive access
(like the common practice of overriding Object.clone() with a public
clone() method) but not more restrictive access.

But they can be overridden with exception-throwing code to ensure
they're not used.
 
C

Chris Smith

Tor said:
But they can be overridden with exception-throwing code to ensure
they're not used.

Indeed. That's more of a symptom of a problem than a reasonable
solution. You're then left with a class that professes to follow an
interface, but doesn't follow it. What you really wanted was to
implement a different interface on top of existing functionality... a
task for which composition is ideal.

Of course, in a pinch, the exception-throwing kludge will work; it's
integrated into the collections API and even given a fancy name:
"optional methods". However, even its light application there is
generally acknowledged to be one of the few great failures of that API.

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
M

Mark 'Kamikaze' Hughes

Dimitri Maziuk said:
Most OO courses spend a lot of time praising benefits of
inheritance, so people tend to use it where it isn't
entirely appropriate. Or have hard time deciding where it
is appropriate.

I find more often that a lot of people have come to OOP from
procedural programming without any formal training, and haven't bothered
to learn the native idioms.
E.g. Stack based on Vector -- it "is a" vector, in a sense,
but without random access to the elements. So you either
have to redeclare most Vector's methods as private in
Stack to hide them (you only want isEmpty(), push(),
and pop()) -- that's a lot of typing.

That's wrong, as anyone who's programmed in Forth knows. Very often
when using a stack, you want random access to the elements. A "minimal"
stack is only really useful in trivial computer science proofs, not in
real applications. A Vector is a pretty decent representation of a LIFO
stack, and it's an efficient representation, since it'll grow and shrink
at the top of the array.

If you were implementing swch a class and wanted to only see the
relevant methods, you could implement a restricted interface and
recommend using a reference to that. That's the correct solution,
rather than ignoring the fact that those methods are appropriate to the
subclass.
 
D

Dimitri Maziuk

Mark 'Kamikaze' Hughes sez:
....
If you were implementing swch a class and wanted to only see the
relevant methods, you could implement a restricted interface and
recommend using a reference to that. That's the correct solution,
rather than ignoring the fact that those methods are appropriate to the
subclass.

Yeah, but interface does not hide the other methods of implementing
class. Stack/Vector may not be a particularly good example of why you
would want to hide those methods, but sometimes you do want that even
IRL (in textbooks it comes under "hiding implementation details").

Besides, an interface in Java terms means you have to type in the
code, whereas inheritance lets you inherit the implementation.

With composition you can reuse the implementation and hide it, too.
Put it in the private field, and make whatever public interface you
like with least amount of trouble -- compared to hoops you have to
jump through to hide superclass' members or to writing code for all
methods specified by interface.

A typical Java example is type-safe containers: I want a vector of
strings, not Objects. But you can't override on return value. The
(arguably) easiest way out is to put Vector inside StringVector and
write a bunch of one-liner accessors with (String) casts where
appropriate. Again, composition instead of inheritance.

Dima
 
B

brougham5

Tor Iver Wilhelmsen said:
But they can be overridden with exception-throwing code to ensure
they're not used.

While duct tape and bailing wire can always be used in a pinch, I'd suggest
using kludges to "fix" code is a symptom that the design should be improved.
Having a subclass throw an exception because the method doesn't make sense
in the context of the subclass seems like a violation of the liskov
substituion principle.
 
J

Joona I Palaste

Indeed. That's more of a symptom of a problem than a reasonable
solution. You're then left with a class that professes to follow an
interface, but doesn't follow it. What you really wanted was to
implement a different interface on top of existing functionality... a
task for which composition is ideal.
Of course, in a pinch, the exception-throwing kludge will work; it's
integrated into the collections API and even given a fancy name:
"optional methods". However, even its light application there is
generally acknowledged to be one of the few great failures of that API.

I have to agree. In my Software Design: Java course, a homework problem
asked me to demonstrate a problem with the Collections framework. My
answer was "optional methods", which are a design problem.
In "real" code, I once had to make an abstract class called an
"ImmutableIterator". It's a java.util.Iterator that makes remove()
throw an UnsupportedOperationException but leaves the other methods
as abstract. This is bad OO design, but with java.util.Iterator being
what it is, how could I have avoided it?
A better solution would have been java.util.Iterator without remove()
and java.util.MutableIterator inheriting from it, adding remove().
This would have made my ImmutableIterator needless.
 
M

Mark 'Kamikaze' Hughes

Dimitri Maziuk said:
Mark 'Kamikaze' Hughes sez:
Yeah, but interface does not hide the other methods of implementing
class. Stack/Vector may not be a particularly good example of why you
would want to hide those methods, but sometimes you do want that even
IRL (in textbooks it comes under "hiding implementation details").

It is a common academic exercise, because academics don't have any
real work to do, so they obsess about mathematical purity. In real
engineering, though, I've never seen a case where subclassing was
appropriate and yet you'd need to disable some methods. That's a code
smell, telling you that you need to refactor a new common superclass.
Besides, an interface in Java terms means you have to type in the
code, whereas inheritance lets you inherit the implementation.
With composition you can reuse the implementation and hide it, too.

Well, of course you have to type in the code, because the
implementation will be totally different. If there's common code, but
they're not subclassing each other, you're almost certainly doing
something wrong. Again, that's a code smell. "Fixing" it by wrapping
some other class up is like patching on your car door with duct tape.
A typical Java example is type-safe containers: I want a vector of
strings, not Objects. But you can't override on return value. The
(arguably) easiest way out is to put Vector inside StringVector and
write a bunch of one-liner accessors with (String) casts where
appropriate. Again, composition instead of inheritance.

That's just hiding the fact of casting from the user, but you're still
paying the performance penalty. java.util.Properties has String->String
mappings, on top of the Hashtable implementation, and that works well
enough for its purposes, because people *don't* abuse it. Prior to
generics going in the language, the "right" solution was to make a
template class (with "${TYPE}" as a placeholder) and use sed to generate
your actual classes. They're not going to have a common interface, no
matter what you do. A StringVector cannot take Integer parameters.
They have methods with the same names, but the signatures are totally
different. Inheritance, composition, and interfaces are all wrong for
that case.

Ultimately, though, "type-safe" containers are not necessary. In 6
years of Java development, I've never had problems with putting the
wrong things in a generic container, and I know this, because the act of
casting it when I withdraw it would be a valid safety check. It's just
paranoia from people who don't test their code adequately.
 
C

Chris Smith

Joona said:
I have to agree. In my Software Design: Java course, a homework problem
asked me to demonstrate a problem with the Collections framework. My
answer was "optional methods", which are a design problem.
In "real" code, I once had to make an abstract class called an
"ImmutableIterator". It's a java.util.Iterator that makes remove()
throw an UnsupportedOperationException but leaves the other methods
as abstract. This is bad OO design, but with java.util.Iterator being
what it is, how could I have avoided it?

Another real example. Part of what I maintain is a data access
framework for a particular application. Basically, it's a very simple
O/R mapper plus some utility classes. Recently, in response to some
scalability issues from a large customer, I took a method that
previously read objects from a database and populated a java.util.List,
and instead substituted (in some circumstances) my own implementation of
List that maintains a ResultSet and doesn't instantiate the data until
it's requested.

This broke some remote corner of the application. On looking into it,
it turns out that some other developer had ignored the clear
documentation stating that this list should be treated as read-only, and
had instead discovered that the method was returning an ArrayList, and
had modified that list as if it were some private working-space for an
algorithm. I fixed the client code, and added bold tags around the
appropriate warning in the API docs for the data framework... but I
doubt the developer read the documentation before doing so last time,
and I doubt the next developer will read the docs before doing so again.
It would be nice to be able to return an UnmodifiableList that simply
doesn't HAVE those methods.

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
D

Dimitri Maziuk

Mark 'Kamikaze' Hughes sez:
Dimitri Maziuk <[email protected]>
wrote on Mon, 29 Dec 2003 21:10:50 +0000 (UTC):

It is a common academic exercise, because academics don't have any
real work to do, so they obsess about mathematical purity.

There are a couple of posts in this thread that seem to disagree.
Ultimately, though, "type-safe" containers are not necessary.

Sure, and everything can be written in assembly and without
all those new-fangled "subroutine" things. Still, in a modern
OO language I'd like containers where at least if I put in a
String, I get a String back out.

....In 6
years of Java development, I've never had problems with putting the
wrong things in a generic container, and I know this, because the act of
casting it when I withdraw it would be a valid safety check. It's just
paranoia from people who don't test their code adequately.

All that means is you don't write class libraries for other people
to use.

Dima
 
M

Mark 'Kamikaze' Hughes

Dimitri Maziuk said:
Mark 'Kamikaze' Hughes sez:
There are a couple of posts in this thread that seem to disagree.

Nobody else posted on that subject at all. There are others who've
picked up procedural-programmer memes about using classes as structs
instead of using inheritance, but at least nobody else has been infected
with Ada-isms (for lack of a better term).
Sure, and everything can be written in assembly and without
all those new-fangled "subroutine" things. Still, in a modern
OO language I'd like containers where at least if I put in a
String, I get a String back out.

You do get a String back out. The reference is not typecast to
String, but the object has never stopped being a String. A cast doesn't
change data in Java, it's just a run-time assertion that assures the
compiler that the type of the object is X, so the reference can be
assigned to a variable. Maybe you understand that already, but your way
of phrasing that suggests that you do not.

Python is a modern OO language--far more modern, more pleasant, and
more OO than Java--and it has no typed references. It has tuples,
lists, and dicts as built-in types, and their contents are not
"type-safe". It's like programming in Java with only Object references,
except the language is designed intelligently so doing that does not
suck. And the result of that choice is not that it's harder, not
lower-level, and not more dangerous, but faster, higher-level, and safer
to develop in; it's also somewhat slower at run-time, but less than
you'd think. Python's just one example, there are many others. Ask a
Lisper whether type-safe lists are necessary.

Type-safe containers are a C++-ism. C++ doesn't have a single-root
inheritance tree, so generic containers were much more difficult to do
right. Those C++-ers came to Java, and demanded templates, because
that's how they'd always done it. Sun, being the spineless jellyfish
they are, buckled. That doesn't make it holy writ.
All that means is you don't write class libraries for other people
to use.

BZZT! It would be hard to get a more wrong answer. I have, many
times, for rather good pay. I just don't normally assume that other
programmers are morons or actively malicious (I reserve that attitude
for the end-users), and I don't write code that's so fragile that using
the declared interface will make it blow up. Either behaviour would be
a career-limiting move in my experience.
 
B

brougham5

Chris Smith said:
This broke some remote corner of the application. On looking into it,
it turns out that some other developer had ignored the clear
documentation stating that this list should be treated as read-only, and
had instead discovered that the method was returning an ArrayList, and
had modified that list as if it were some private working-space for an
algorithm. I fixed the client code, and added bold tags around the
appropriate warning in the API docs for the data framework... but I
doubt the developer read the documentation before doing so last time,
and I doubt the next developer will read the docs before doing so again.
It would be nice to be able to return an UnmodifiableList that simply
doesn't HAVE those methods.

Instead of adding more docs, make it so that client code abuses don't affect
anything else.

Don't return an ArrayList that can be modified. At the very least, return a
new, deep copy of the list so that if it is modified, the rest of the
program doesn't care. Even better, return a wrapper of sorts that doesn't
allow its data to be changed if changing the data will cause problems.

If you have to rely on other developers following documentation to keep your
code from breaking, you are writing bad code.
 
C

Chris Smith

Mark said:
Python is a modern OO language--far more modern, more pleasant, and
more OO than Java--and it has no typed references. It has tuples,
lists, and dicts as built-in types, and their contents are not
"type-safe". It's like programming in Java with only Object references,
except the language is designed intelligently so doing that does not
suck. And the result of that choice is not that it's harder, not
lower-level, and not more dangerous, but faster, higher-level, and safer
to develop in; it's also somewhat slower at run-time, but less than
you'd think.

Sounds like you'd rather be developing in Python. If that's the case,
fine.

Nevertheless, Java provides a specific advantage in terms of typing of
references by the compiler. That is a very reasonable advantage to
want. Among other things, it:

a. Increases performance (though that's hardly its main objective).

b. Decreases the very real possibility of bugs due to confusion
about available types. Sorry, but this possibility doesn't go
away because you assert so.

c. Gives more information to smart development tools such as code
editors, so that they can be more helpful.

That all being the case, and whether you prefer a less typed language or
not, making choices that make it easier and less painful to work within
the typed environment makes sense for Java.
Type-safe containers are a C++-ism. C++ doesn't have a single-root
inheritance tree, so generic containers were much more difficult to do
right. Those C++-ers came to Java, and demanded templates, because
that's how they'd always done it. Sun, being the spineless jellyfish
they are, buckled. That doesn't make it holy writ.

I certainly don't think so. In fact, seems to me that in a language
where types are so important, it'd be good to do anything possible to
let the compiler find out more about types. That eliminates kludgy
syntax like casting (which is uniquely not checked by the compiler,
among all type constructs), and increases most of the advantages listed
above.

If nothing else, it would be *so* nice to finally be able to declare a
method to return a "List of Strings" -- instead of declaring it to
return a List and adding in human-only documentation that the List
contains Strings -- that generics are worth it for that alone.

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
C

Chris Smith

Instead of adding more docs, make it so that client code abuses don't affect
anything else.

Don't return an ArrayList that can be modified. At the very least, return a
new, deep copy of the list so that if it is modified, the rest of the
program doesn't care.

Perhaps you weren't paying attention. The whole point of the
modification was to prevent instantiating millions of objects from the
database when I possibly only care about the first 25. Doing a deep
copy into an ArrayList would turn an optimization into a less efficient
way of accomplishing nothing.
If you have to rely on other developers following documentation to keep your
code from breaking, you are writing bad code.

That wasn't the issue. I was relying on another developer following
documentation to keep that other developer's code from breaking.

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
M

Michael Borgwardt

Chris said:
Perhaps you weren't paying attention. The whole point of the
modification was to prevent instantiating millions of objects from the
database when I possibly only care about the first 25. Doing a deep
copy into an ArrayList would turn an optimization into a less efficient
way of accomplishing nothing.

I suspect he meant "shallow copy".

That wasn't the issue. I was relying on another developer following
documentation to keep that other developer's code from breaking.

Still, you were practically *asking* for trouble. When returning a List
that must not be modified, at least use Collections.unmodifiableList(),
that's exactly what it's for!
 
D

Dimitri Maziuk

Mark 'Kamikaze' Hughes sez:
Type-safe containers are a C++-ism. C++ doesn't have a single-root
inheritance tree, so generic containers were much more difficult to do
right. Those C++-ers came to Java, and demanded templates, because
that's how they'd always done it. Sun, being the spineless jellyfish
they are, buckled. That doesn't make it holy writ.

May I point out that when you try to insert an orange into
vector<apple>, compiler barfs on or around the offending line?
Whereas List of Airplanes will happily accept a Pig, and when
you call fly() for each element later on... don't stand
directly under them as they fly past.

(Of course, anyone who's seen a typical g++ error message may
well argue that pigs flying overhead are a much lesser evil.
And I'll probably agree.)

Dima
 
C

Chris Smith

Michael said:
I suspect he meant "shallow copy".

Mmm? Any copy of the list defeats the point.
Still, you were practically *asking* for trouble. When returning a List
that must not be modified, at least use Collections.unmodifiableList(),
that's exactly what it's for!

Yes, I could have done so. Since I wrote the original code years ago, I
don't recall my exact thought processes... but I probably just didn't
think about it. Nevertheless, I can emphatically claim that I would
have returned a read-only List if the interface had existed as a
superinterface of List.

Anyway, I don't recall any longer how this fit into anything in the
first place...

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
?

=?ISO-8859-1?Q?Thomas_Gagn=E9?=

Doesn't someone inserting pigs into their airplane list have larger
problems than the (in)ability to insert the pig? Their problem is a
faulty algorithm, not the insert. How the heck did a pig end-up being
processed as an airplane?
 
M

Michael Borgwardt

Thomas said:
Doesn't someone inserting pigs into their airplane list have larger
problems than the (in)ability to insert the pig? Their problem is a
faulty algorithm, not the insert. How the heck did a pig end-up being
processed as an airplane?

That's exactly the question. And it may be difficult to answer once things
have progressed to the stage where one tries to make it fly. It may have
spent a long time in that List and it may be very difficult or impossible
to reconstruct the circumstances under which it was put in there.

And that's exactly what makes type-safe containers (and type-safety in general)
are a big improvement: They make errors manifest earlier and thus speed up
debugging.
 

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,999
Messages
2,570,243
Members
46,835
Latest member
lila30

Latest Threads

Top