Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Wanted: strong typing, but values can be different types. 2

Status
Not open for further replies.

dbleyl

Programmer
Mar 26, 2001
117
US
Hello,

Have a class, which has a data member that can take on two different personalities:

1.) Null, "N/A" (or any of it's variants).
2.) A non-negative int.
3.) Two non-negative ints representing a range.

I'm currently storing this as a string, which is validated
by a validator object, using regex's before creating the object.

This seems like a prime opportunity to subclass, but the object itself doesn't have very many methods, and subclassing to fork encapsulated data instead of behaviour seems very weak.

Is another object called for that wraps the notion of this mutating data member? (with a hasA() method?) Is there a way to avoid wasting the allocation of a string and two ints for every parent?

Using java, so no C++ Unions. Should I continue to use a validated String? The objects are generated from a file created by an outside organization, so changing the inputs isn't an option, regardless of how nice it would be...

TIA
 
Hi, as you suggested, i would use a wrapper class that defines clearly the mutating behaviour.
I propose the following interface (C++/eiffel like syntax) :

class MutableData
{
public: //Queries

bool is_null ();
bool is_positive_integer ();
bool is_range ();

int positive_int ();
require://pre-conds
is_positive_integer ()
ensure://post-conds
return >= 0

int lower_range ();
require:
is_range ()
ensure:
return >= 0
return <= higher_range ()

int higer_range ();
require:
is_range
ensure:
return >= lower_range ()

public: //COMMANDS
set_null ();
ensure::
is_null ()

set_positive_integer (int new_integer);
require:
new_integer >= 0
ensure:
is_positive_integer ()
positive_integer () == new_integer

set_range (int lower, int higher)
require:
lower >= 0
lower <= higher
ensure:
is_range()
lower_range () == lower
higher_range () == higher

invariant:
is_null () == not is_positive_integer () and not is_range ()
is_positive_integer () == not is_null () and not is_range ()
is_range () == not is_null () and not is_positive_integer ()
is_range () => lower_range () <= higher_range ()

};

--
Globos
 
Thanks for the code example. I've written a similar java class, and applied the Builder pattern to instantiate a whole heirarchy of objects. Looks good so far.
 
I am trying to think a bit beyond what you have described. You did not describe what context the class was used in, but it reminds me of the input that was generated by functions in AutoLisp. There are a lot of commands that accept either a null entry (used for default values), a predefined keyword, a number or two points (a distance).

The keyword usually (but not always) changed the behaviour of the command. For example, the
Code:
circle
command asked for a radius:

Code:
circle
Radius (Diameter/<10>):

You could now press [enter] for the default value of 10, enter &quot;D&quot; for diameter (in which case you get an additional prompt for it, so it changes the behaviour of the command), type a number or select two points for a distance.

Back to your class.

If you use the Null for default values, you may pass this default to the class, so it can have more than one ways of getting its data, but generates one result nevertheless. Your class is a kind of &quot;input interpreter&quot; then.

It all depends on how much you need the original input to the class and how much you need the results. If you have a tendency to handle the mutating values with one class, there surely must be a connection between them. Can you give a hint of what you are using the types for?

Best regards
 
Thanks for the reply DQ. Here is some contextual info:
Here's a similar example (different domain):

class GarmentItem
data member size

Where size can be a single number (10), a range(32-34), or none (one-size-fits-all).

The objects are being built from intercepted spreadsheets that are part of a legacy process, which originate outside of the organization.

Two major issues:
1. No dtd/schema validation for incoming spreadsheets,
and can't change structure.
2. Items with 'one-size-fits-all' come into the system
in many different formats 'NA', 'n/a', 'osfa', blank,
null.

Now image that if an Item has a scalar or range size, some operations will be performed, whereas otherwise, they won't.
All the Items must meet a bare minimum, i.e. '$WWRc' isn't a valid size but '' is.

The end product includes caching the objects in a db and generating pleasant and consistent reports.

So far, I've delegated the creation process to a Builder class, which delegates the validation and processing of each data member to Parser classes, such as SizeParser.
These objects are Singletons which either return a valid object or null. The Builder checks for nulls where they aren't acceptable (all Garment objects must have a size object which can't be null, although it may have 'null' for it's size). The Builder class itself returns an Item or null, based on whether the data members were valid enough.

So far this seems to be ok. Not that I know Lisp, but is the idea similar to type lists?
 
Another idea:

class Range
{
Integer start;
Integer stop;

public Range ()
{
start = null;
stop = null;
}

public Range (int value)
{
start = new Intger (value);
stop = start;
}

public Range (int start, int stop)
{
this.start = new Integer (start);
this.stop = new Integer (stop);
}

// ...
}
 
stefanwagner,

I like the idea of a scalar being a degenerative form of a range, i.e. a scalar is a range whose begin and end are equal.


public Range (int value)
{
start = new Intger (value);
stop = start;
}


I have finished the first version of the code, but I'll keep this in mind for the second.

I also like the idea of using package scope for the class and data members to keep the client interface clean.

As my version stands, I have two additional functions , isRange() and exists().

I expect the client to call exists before accessing as scalar or range, and isRange() before calling the range accessors.

As an aside, I've convinced some of the sponsors to try something better for exchanging data, and we've started work on an XML Schema to clean things up...

Thanks!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top