Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > Design Questions re Subclassing

Reply
Thread Tools

Design Questions re Subclassing

 
 
Rhino
Guest
Posts: n/a
 
      02-22-2005
My background in OO theory is not that great and I am struggling with the
right way to design something that I can describe fairly easily. Can someone
help me figure out what I need to do?

I am writing some methods that want to determine if a given value is within
a range of values. For example, if the range is 1 to 5, I want my method to
return true if the input parameter is 1, 2, 3, 4, or 5; otherwise, the
method should return false. By the same token, given a range of dates
starting with Jan 1 2005 and ending with Dec 31 2005, I want to return true
if a given date belongs to 2005 but return false otherwise.

That is trivial to code if I deal with each data type one at a time but I
think this requirement cries out for a general solution. It seems to me that
I have identified the need for a class that represents a Range of two values
of the same type. (I'm assuming that it makes no sense to have a Range that
has a low value of one type and a high value of a different type, such as
"cat" to Dec 31 2005). It seems to me that a Range is a very specialized
collection and my implementation of choice should be a TreeSet since it
always keeps the low value of the range before the high value, which makes
it very useful for the between() method I will need.

Therefore, am I on the right track so far in saying that Range should
subclass TreeSet or is there a better approach?

If Range should subclass TreeSet as I suspect, I'm not sure if Range should
be a concrete or abstract class. The reason for my uncertainty is that I
need to insure certain behaviour in my Range class:
- an instance of Range can only contain two objects
- the two objects within an instance of Range have to be the same type, e.g.
both ints or both dates or both Strings
- it should not be possible to remove any elements from the Range once it
has been created
- it should not be possible to add any elements to the Range once it has
been created
I'm not sure if I can do everything in one well-written Range class or if I
will need to create subclasses like IntRange, DateRange, etc.

I tried writing my Range class and came up with two constructors;
unfortunately neither or them successfully adds the Objects passed to the
constructor to the Range. Here is the code (please forgive all the
diagnostics):

public Range(Object firstObject, Object secondObject) {

super();

System.out.println("firstObject: " + firstObject);
System.out.println("secondObject: " + secondObject);

/* Verify that both objects are the same class. */
String firstObjectClass = firstObject.getClass().getName();
String secondObjectClass = secondObject.getClass().getName();
if (!firstObjectClass.equals(secondObjectClass)) {
throw new IllegalArgumentException("The two Objects passed to
this constructor must be the same class.");
}

boolean result = add(firstObject);
if (result != true) {
throw new IllegalArgumentException("The first Object was not
added to the Range.");
}

result = add(secondObject);
if (result != true) {
throw new IllegalArgumentException("The second Object was not
added to the Range.");
}

System.out.println("Range(Object, Object) size: " + size());
}



and

public Range(Object[] range) {

super();

System.out.println("range.length: " + range.length);
System.out.println("range[0]: " + range[0]);
System.out.println("range[1]: " + range[1]);

if (range.length != 2) {
throw new IllegalArgumentException(
"The range array must contain exactly two elements.");
}

/* Verify that both objects are the same class. */
String firstObjectClass = range[0].getClass().getName();
String secondObjectClass = range[1].getClass().getName();
if (!firstObjectClass.equals(secondObjectClass)) {
throw new IllegalArgumentException("The two objects passed to
this constructor must be the same class.");
}

boolean result = add(range[0]);
if (result != true) {
throw new IllegalArgumentException("The first Object was not
added to the Range.");
}

result = add(range[1]);
if (result != true) {
throw new IllegalArgumentException("The second Object was not
added to the Range.");
}

System.out.println("Range(Object[]) size: " + size());
}


For some reason, the add() methods are not adding the values to the Range
class; I don't understand WHY they aren't working or how to find out. The
values I'm passing in are Strings, specifically "cat" and "dog".

I'm trying to figure out if I have a fundamental design flaw here or if I've
just made a silly (but not obvious, at least to me) coding error.

I would also like to confirm that the best way to prevent the Range from
holding more or less than 2 objects is to override the add(), addAll() and
remove() methods so that they don't do anything.

Can anyone help me confirm that my design is sound and/or help me figure out
why my Objects are not being added to the Range? I've worked on this for the
last few hours and I'm just not seeing the problem yet....

--
Rhino
---
rhino1 AT sympatico DOT ca
"There are two ways of constructing a software design. One way is to make it
so simple that there are obviously no deficiencies. And the other way is to
make it so complicated that there are no obvious deficiencies." - C.A.R.
Hoare


 
Reply With Quote
 
 
 
 
klynn47@comcast.net
Guest
Posts: n/a
 
      02-22-2005
What about something like this

public class Range {
private long low;
private long high;

public Range(long low,long high) {
...
}

public Range(String start,String end) {
convert start and end to numbers. Use
some base date like 1990 and convert
each to longs which represent the number
of days since the base
}

public boolean contains(int number) {
...
}

public boolean contains(String date) {
...
}
}

 
Reply With Quote
 
 
 
 
Oscar kind
Guest
Posts: n/a
 
      02-22-2005
Rhino <(E-Mail Removed)> wrote:
> I am writing some methods that want to determine if a given value is within
> a range of values. For example, if the range is 1 to 5, I want my method to
> return true if the input parameter is 1, 2, 3, 4, or 5; otherwise, the
> method should return false. By the same token, given a range of dates
> starting with Jan 1 2005 and ending with Dec 31 2005, I want to return true
> if a given date belongs to 2005 but return false otherwise.
>
> That is trivial to code if I deal with each data type one at a time but I
> think this requirement cries out for a general solution. It seems to me that
> I have identified the need for a class that represents a Range of two values
> of the same type. (I'm assuming that it makes no sense to have a Range that
> has a low value of one type and a high value of a different type, such as
> "cat" to Dec 31 2005). It seems to me that a Range is a very specialized
> collection and my implementation of choice should be a TreeSet since it
> always keeps the low value of the range before the high value, which makes
> it very useful for the between() method I will need.
>
> Therefore, am I on the right track so far in saying that Range should
> subclass TreeSet or is there a better approach?


How about this (untested):

class Range<T extends Comparable>
{
private T start;
private T end;

public Range(T start, T end)
{
this.start = start;
this.end = end;
}

public boolean isInRange(T value)
{
return (start <= value && value <= end);
}
}


--
Oscar Kind http://home.hccnet.nl/okind/
Software Developer for contact information, see website

PGP Key fingerprint: 91F3 6C72 F465 5E98 C246 61D9 2C32 8E24 097B B4E2
 
Reply With Quote
 
Tony Morris
Guest
Posts: n/a
 
      02-22-2005
> class Range<T extends Comparable>
> {
> private T start;
> private T end;
>
> public Range(T start, T end)
> {
> this.start = start;
> this.end = end;
> }
>
> public boolean isInRange(T value)
> {
> return (start <= value && value <= end);
> }
> }
>


You can't use relational operators on a variable that is the parameter of a
parameterized type.

--
Tony Morris
http://xdweb.net/~dibblego/


 
Reply With Quote
 
John C. Bollinger
Guest
Posts: n/a
 
      02-22-2005
Tony Morris wrote:

[Oscar Kind wrote:]
>>class Range<T extends Comparable>
>>{
>> private T start;
>> private T end;
>>
>> public Range(T start, T end)
>> {
>> this.start = start;
>> this.end = end;
>> }
>>
>> public boolean isInRange(T value)
>> {
>> return (start <= value && value <= end);
>> }
>>}
>>

>
>
> You can't use relational operators on a variable that is the parameter of a
> parameterized type.


You can use the == and != operators on any pair of primitives or any
pair of references. You cannot use <= on references of any stripe,
however, regardless of the nature of the references' declared types. I
imagine Oscar probably meant to use the compareTo(T) method of
Comparable<T>, and got sidetracked. That raises the point that
Comparable is generic in Java 1.5, and a fully generic treatment is
therefore warranted, perhaps something like this:

class Range<T extends Comparable<? super T>> {
private T start;
private T end;

public Range(T start, T end) {
this.start = start;
this.end = end;
}

public boolean contains(T value) {
return ((start.compareTo(value) <= 0)
&& (value.compareTo(end) <= 0));
}
}


--
John Bollinger
http://www.velocityreviews.com/forums/(E-Mail Removed)
 
Reply With Quote
 
Chris Uppal
Guest
Posts: n/a
 
      02-22-2005
Rhino wrote:

> - an instance of Range can only contain two objects
> - the two objects within an instance of Range have to be the same type,
> e.g. both ints or both dates or both Strings
> - it should not be possible to remove any elements from the Range once it
> has been created
> - it should not be possible to add any elements to the Range once it has
> been created
> I'm not sure if I can do everything in one well-written Range class or if
> I will need to create subclasses like IntRange, DateRange, etc.


Given that list of restrictions, your Range class will not be acting much like
a TreeSet. Users of you class will have to "know" that they are dealing with a
Range and not treat it as a Set, or even as a TreeSet. For that reason, I
don't think that subclassing TreeSet is a good idea. You /might/ want to use
one internally, but I don't see what it would gain you.

Actually, it's not clear to me that it should be considered to be any kind of a
Collection. It is /similar/, but you can't iterate over it (unless you make it
more complicated than it sounds as if you need), and that is a fundamental
ability of the java Collections. I'd just make a standalone class that held
its endpoints in two fields, and had contains() and isEmpty() methods for
consistency with Collections, but didn't inherit from any of the collection
classes or implement any of the collection interfaces.

BTW, one design decision you'll have to make is whether the Range includes its
end-points. It would seem natural for it to include both, but the normal Java
looping idiom:
for (int i = first; i < last; i++)
works with "ranges" that are open at one end.

Another BTW, I wouldn't bother with all those class checks. You probably don't
need them even in pre-generics code, and the generics stuff makes it even more
unnecessary in J5.

-- chris


 
Reply With Quote
 
Rhino
Guest
Posts: n/a
 
      02-22-2005

"Chris Uppal" <(E-Mail Removed)-THIS.org> wrote in message
news:421b4cc1$0$38041$(E-Mail Removed).. .
> Rhino wrote:
>
> > - an instance of Range can only contain two objects
> > - the two objects within an instance of Range have to be the same type,
> > e.g. both ints or both dates or both Strings
> > - it should not be possible to remove any elements from the Range once

it
> > has been created
> > - it should not be possible to add any elements to the Range once it has
> > been created
> > I'm not sure if I can do everything in one well-written Range class or

if
> > I will need to create subclasses like IntRange, DateRange, etc.

>
> Given that list of restrictions, your Range class will not be acting much

like
> a TreeSet. Users of you class will have to "know" that they are dealing

with a
> Range and not treat it as a Set, or even as a TreeSet. For that reason, I
> don't think that subclassing TreeSet is a good idea. You /might/ want to

use
> one internally, but I don't see what it would gain you.
>

Thank you, Chris, this is exactly the kind of feedback I was seeking.

> Actually, it's not clear to me that it should be considered to be any kind

of a
> Collection. It is /similar/, but you can't iterate over it (unless you

make it
> more complicated than it sounds as if you need), and that is a fundamental
> ability of the java Collections. I'd just make a standalone class that

held
> its endpoints in two fields, and had contains() and isEmpty() methods for
> consistency with Collections, but didn't inherit from any of the

collection
> classes or implement any of the collection interfaces.
>

You raise a good point in questioning whether TreeSet should be extended in
the first place for my Range class. My thinking was that I had a Collection
on my hands, albeit a very specialized one containing exactly two values.
Perhaps you're right and that just isn't a close enough similarity to
justify making Range a subclass of TreeSet. I liked the fact that I could
supply my two values to the TreeSet subclass in any order since TreeSet will
store them in the proper order "automagically" but I can achieve the same
behaviour by simply comparing the two values and storing the lower value as
"low" and the higher value as "high". Perhaps I should do as you say and
create a Range class that only subclasses Object rather than TreeSet.

> BTW, one design decision you'll have to make is whether the Range includes

its
> end-points. It would seem natural for it to include both, but the normal

Java
> looping idiom:
> for (int i = first; i < last; i++)
> works with "ranges" that are open at one end.
>

That's a good point and one that I had considered. I'm essentially trying to
imitate the behaviour of the 'between' operator in DB2 and it includes both
end points, i.e. 'where age between 18 and 21' matches with 18, 19, 20 and
21. Perhaps I should make my Range class flexible and include some booleans
in my between() method. Something like this (uncompiled and untested):

public boolean between(Object value, boolean includesLowValue, boolean
includesHighValue) {

if (includesLowValue && includesHighValue) {
if (value >= low && value <= high) return true;
else return false;
}

if (includesLowValue && !includesHighValue ) {
if (value >= low && value < high) return true;
else return false;
}

if (!includesLowValue && includesHighValue) {
if (value > low && value =< high) return true;
else return false;
}

if (!includesLowValue && !includesHighValue) {
if (value > low && value < high) return true;
else return false;
}
}

That would allow a user to make the between() method behave any way they
want.

> Another BTW, I wouldn't bother with all those class checks. You probably

don't
> need them even in pre-generics code, and the generics stuff makes it even

more
> unnecessary in J5.
>

But how else do I make sure that I get two Objects of the same kind as the
end-points of my Range? Surely I don't want to have the end-points be two
different types? (Except that a range that has two numbers of different
types, e.g. 4 to 5.8, would make sense but a range of "cat" to 42 almost
certainly wouldn't.)

How else can I ensure that the two Objects in the constructor are the same
(or compatible) type?

I'm using Java 1.5 but I'm really not up on all the Generics stuff yet.
(Translation: I read the "what's new" article in the API but haven't really
digested the proper use of Generics yet. Also, I'm not completely sure I
want to use techniques that aren't backward compatible with pre-1.5 versions
of Java yet.)

Rhino


 
Reply With Quote
 
Oscar kind
Guest
Posts: n/a
 
      02-22-2005
John C. Bollinger <(E-Mail Removed)> wrote:
> Tony Morris wrote:
>>
>> You can't use relational operators on a variable that is the parameter of a
>> parameterized type.

>
> You can use the == and != operators on any pair of primitives or any
> pair of references. You cannot use <= on references of any stripe,
> however, regardless of the nature of the references' declared types. I
> imagine Oscar probably meant to use the compareTo(T) method of
> Comparable<T>, and got sidetracked. That raises the point that
> Comparable is generic in Java 1.5, and a fully generic treatment is
> therefore warranted, perhaps something like this:


[cut: code exampl]

Thanks! I forgot I shouldn't answer posts while under time pressure
(getting to work on time). John hit my intention perfectly, although he
probably did better on the generics part (I'm not yet comfertable enough
with all of the syntax).


--
Oscar Kind http://home.hccnet.nl/okind/
Software Developer for contact information, see website

PGP Key fingerprint: 91F3 6C72 F465 5E98 C246 61D9 2C32 8E24 097B B4E2
 
Reply With Quote
 
Rhino
Guest
Posts: n/a
 
      02-22-2005
To followup on Chris' suggestions, I am working on my Range2 class, which
subclasses only Object.

Here is what I have so far:

abstract public class Range2 {

public static int NUMBER_OF_ELEMENTS = 2;

Object firstValue = null;
Object secondValue = null;

/**
* Constructor Range2(Object, Object) creates an instance of the class.
*
* @param Object
* firstObject one of the two Object values that will
encompass
* the range
* @param Object
* secondObject the other of the two Object values that will
encompass
* the range
*/
public Range2(Object firstObject, Object secondObject) {

System.out.println("firstObject: " + firstObject);
System.out.println("secondObject: " + secondObject);

/* Verify that both objects are the same class. */
String firstObjectClass = firstObject.getClass().getName();
String secondObjectClass = secondObject.getClass().getName();
if (!firstObjectClass.equals(secondObjectClass)) {
throw new IllegalArgumentException("The two Objects passed to
this constructor must be the same class.");
}

/* Add the two objects to the Range. */
firstValue = firstObject;
secondValue = secondObject;

System.out.println("Range2(Object, Object) size: " + size());
}

/**
* Constructor Range2(Object[]) creates an instance of the class.
*
* @param Object[]
* range an array of objects
*/
public Range2(Object[] range) {

System.out.println("range.length: " + range.length);
System.out.println("range[0]: " + range[0]);
System.out.println("range[1]: " + range[1]);

if (range.length != 2) {
throw new IllegalArgumentException(
"The range array must contain exactly two elements.");
}

/* Verify that both objects are the same class. */
String firstObjectClass = range[0].getClass().getName();
String secondObjectClass = range[1].getClass().getName();
if (!firstObjectClass.equals(secondObjectClass)) {
throw new IllegalArgumentException("The two Objects passed to
this constructor must be the same class.");
}

/* Add the two objects to the Range. */
firstValue = range[0];
secondValue = range[1];

System.out.println("Range(Object[]) size: " + size());
}

/**
* Method size() returns the size of this Range2; the value will always
be 2.
*
* @return the size of this Range2 (always 2)
*/
public int size() {

return NUMBER_OF_ELEMENTS;
}

/**
* Method getValues() returns all of the values in this Range2.
*
* @return all of the values in this Range2
*/
public Object[] getValues() {

Object[] values = new Object[size()];

values[0] = firstValue;
values[1] = secondValue;

return values;
}

abstract public boolean between(Object input);

}


The between() method is the challenge here. I found that I couldn't compare
two Objects to see which was larger since Object doesn't allow for that sort
of comparison.

Therefore, I assume that I will need to subclass Range2 and let each
subclass have its own between() method. In some cases, Range2 subclasses
will compare via '>' and '<' operators but in other cases, they will use
compareTo() methods. As a result, I think I need to make between() an
abstract method within Range2 and that Range2 therefore has to be an
abstract class. Then, my subclasses of Range2 will need to implement their
own versions of between() and can use Range2's versions of size() and
getValues().

Also, in the absence of a way to compare between the two values that are
passed to the constructors, all I can really say about my Range2 class is
that it contains two values of the same type: I CANNOT say that the first
value is lower than the second and I have no way of determining which is
lower. Any logic that needs comparisons has to wait until the subclasses,
which can do comparisons.

Am I thinking of all this in the right way? I want to make sure that my
design is sound.

Rhino


 
Reply With Quote
 
Chris Uppal
Guest
Posts: n/a
 
      02-23-2005
John C. Bollinger wrote:

> class Range<T extends Comparable<? super T>> {


Shudder...

(but not entirely).

But that is as nothing to my so-far favourite bit of generics from the library:

public Enum<E extends Enum<E>>

I can remember using similar (in form, not semantics) mind-bendingly,
apparently unboundedly recursive, templates in C++. But (perhaps fortunately)
time has erased my memories of how and why.

BTW, I had a pop at creating generical Range and PluggableRange classes, and
found it infuriatingly difficult (but then, I needed the practise). I may
start a new thread later about some of the difficulties.

-- chris


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Questions about subclassing an int Steven W. Orr Python 2 01-05-2008 02:30 AM
Re: Questions....questions....questions Patrick Michael A+ Certification 0 06-16-2004 04:53 PM
Design choice: subclassing or "supplementing" J Krugman Perl Misc 4 04-19-2004 04:44 AM
Subclassing UserControl makes the Visual Studio designer does not work S Guiboud ASP .Net 1 07-18-2003 03:55 PM
Re: Questions re subclassing at "load time" Edward K. Ream Python 0 06-24-2003 01:45 AM



Advertisments