Object oriented features of JScript .NET

This week’s article is all about classes in JScript .NET. I
introduced the class statement in article four of this series. This article
goes into more detail covering class access rules, inheritance,
and polymorphism. The article includes some sample code that
demonstrates how to use the features I discuss in this
article.

Understanding class access rules

Article four of this series introduced the
class statement, member
variables, constructors, and static members. While classes are a
great way to encapsulate behavior and data into a single type,
they’re not useful if you cannot hide certain details from other
code in your application. For example, a class that has an amount member would not be aware if
some other code in the application directly modifies its value –
the class could not respond to the change and would not be able
to determine if the new value is appropriate. This is where class
member attributes come in.

By default, all class members are visible to all code within
an application. This means that any code can modify a class’s
member variables or call its member functions without
restrictions – this is similar to regular JScript objects you
create yourself.

When you create a new class, you can grant or deny access to
the class’s member variables and functions using member
attributes
. There are three types of member attributes:

  • public : This is the
    default attribute; public
    members are accessible from all code within an application
  • private : These types of
    members are accessible only within the class that
    declares them – all other code within an application cannot
    access private members.
  • protected : These types of
    members are accessible within the class that declares them
    and classes that derive from the class that declares
    them (I’ll discuss the term ‘derives’ shortly).

You can change a class member’s attributes as shown in the
following listing:

class kitchenAppliance {

  private var type : int;

  protected var weight : int;

  // public is implicit - the following declarations are public
  var height: int;

  function kitchenAppliance()
  {
    //...
  }
}

The listing changes the visibility of the type member to private (meaning the variable is
accessible only within the class that defines it), and the
visibility of the weight
member to protected (meaning
that it is visible to the defining class and classes that derive
from it). The height member
variable and kitchenAppliance
function (the class’s constructor) are both publicly visible.

Understanding inheritance

Most people have more than one kitchen appliance: stove,
fridge, and perhaps a dishwasher. One thing in common with each
of these appliances is that they play a role in a typical
kitchen: we use them to prepare or store food, or clean up when
we’re done. When you think of a stove, you implicitly understand
that it is a kitchen appliance, as is a fridge and dishwasher. In
effect, a stove is a type of appliance.

When you notice or discover an “is a” relationship between two
objects, they are said to share some characteristic that
establishes the relationship between them. In the case of stoves
and dishwashers, both are types of kitchen appliances
based on their role in a typical home. You can model the
relationship between a stove and a dishwasher using JScript .NET
classes and inheritance, as shown in the following listing:

class kitchenAppliance {
  //...
}

class Stove extends kitchenAppliance {
  //...
}

class Dishwasher extends kitchenAppliance {
  //...
}

The code uses the extends
keyword to express the relationship between the Stove and Dishwasher classes through the kitchenAppliance class. The Stove and Dishwasher classes derive from
the base class kitchenAppliance. You can think of the
relationship in another way: a Stove is a specialized type of kitchenAppliance.

Inheritance is a very powerful means of managing complexity
since it allows you to compose new types based on existing types,
specializing them as necessary. Consider the following
listing:

class Dishwasher extends kitchenAppliance {
  // note: this is a partial implementation of the class
  function washDishes()
  {
  print("Washing dishes...done");
  }
}

class Stove extends kitchenAppliance {
  // note: this is a partial implementation of the class
  function boilWater()
  {
  print("Mmmmm....coffee....");
  }
}

The listing demonstrates that the Dishwasher and Stove class
each have operations that are specialized for each type – washDishes for the Dishwasher class and boilWater for the Stove class. There’s another aspect of
inheritance, called polymorphism that allows you to treat
specialized types as more general types.

Understanding Polymorphism and Casting

A polymorphic object can assume different forms based on where
it exists within its class hierarchy. For example, consider the
following listing:

// Note: genericAppliance is a kitchenAppliance type
var genericAppliance : kitchenAppliance = new Dishwasher();

print("The appliance is: " + genericAppliance.applianceAsString);
// prints: The appliance is: Dishwasher

The code declares a variable that’s a kitchenAppliance type, but creates an
instance of a Dishwasher. The
code confirms that the genericAppliance variable refers to a
Dishwasher by having it print
its string representation (third line in the above listing).

JScript .NET allows you to do this because the Dishwasher is polymorphic: you can treat
it as a Dishwasher or a kitchenAppliance. Polymorphism
allows you to create general functions that take base types for
their parameters but operate on more specialized types.

There is a minor catch, though. When you call the washDishes method, and compile the code,
the JScript .NET compiler complains since it knows that a kitchenAppliance does not have a
washDishes method. You can
resolve this by casting the genericAppliance from a kitchenAppliance (its declared type) to a
Dishwasher (its actual type),
as shown in the following listing:

genericAppliance.washDishes(); // compile-time error...
// 'Objects of type 'kitchenAppliance' do not have such a member'

Dishwasher(genericAppliance).washDishes(); // ok
// prints: Washing dishes...done

The second line of code casts (transforms) the kitchenAppliance into a Dishwasher and then calls the washDishes method. The JScript .NET
compiler attempts to confirm that all casts you attempt are legal
at compile time. If the JScript .NET compiler allows a case at
compile time, which subsequently fails at runtime it raises in
Illegal Cast exception.

Understanding how to use Inheritance and Polymorphism

As it stands, the kitchenAppliance class is adequate but has
some fundamental design problems:

  • The type member variable
    is a String, which means that
    a “stove” and “StoVe” are different types of appliances.
  • The weight member variable
    is a simple integer type, which does not capture the weight’s
    units (100 pounds, or 100 kilograms?). There aren’t any means of
    converting units of measure from one unit to another.
  • The dimensions of the kitchen appliance share the same
    problem as the weight member
    variable. In addition, the dimensions are all simple integers,
    and do not enforce any type of constraints (you could provide the
    height measurements and forget
    to provide a width and depth measurements).

Here’s some code that uses a newer implementation of the
kitchen appliances sample:

var myFridge = new Fridge();
// the Fridge weighs 350 pounds
myFridge.weight = new quantity(350 , new unitPounds() );
// cubicDimentions has a constructor that makes it easy to record
// initial measurements
myFridge.dimentions = new cubicDimentions(2,new unitMeter(),
  1.5 , new unitMeter(),
  2.5 , new unitMeter());
// display the details of my Fridge...
print(myFridge);
/* output:

Fridge / 350 pounds
  Height: 2 meters
  Width : 1.5 meters
  Depth : 2.5 meters

*/

The code demonstrates that the newer implementation is much
easier to use and the code is a lot more concise. Chances are
that you would have been able to predict most of the output based
only on the above code. The newer implementation goes a little
further – consider the following fragment (a continuation of the
above sample):

// continued...
var demoQty : quantity;
demoQty = converter.convert(
  myFridge.weight ,
  new quantity( 0 , new unitKg()));
print("Equivilent weight in " + demoQty.units.typeAsString +
      " is: " + demoQty.amount);
/* output:

Equivilent weight in kilograms is: 159.09091186523438

*/

The fragment demonstrates a conversion class (converter) converting the fridge’s weight,
from whatever units it happens to currently be in, into a new
unit of measure (kilograms). The fragment illustrates that a
quantity type encapsulates
measurements, like weights and dimensions, making them easier to
work with and also shows how the class encapsulates units of
measure.

The converter class has a
single static function called
convert, which performs the
conversion from one unit of measure to another. Here’s what the
convert function looks like:

static function convert(fromQty:quantity ,toQty: quantity) :
                           quantity
{
  var ratio:float;
  var newQty : quantity;
  ratio = toQty.units.conversionRatio(fromQty.units);
  if(ratio == -1)
    newQty = new quantity( -1,new unit());
  else
    newQty = new quantity( (ratio*fromQty.amount),
                           toQty.units);
  return newQty;
}

The function takes two parameters, both of which are quantity types and returns a new
quantity that represents the
new units and units of measure. The line that does all of the
work in the function is this one:

newQty = new quantity( (ratio*fromQty.amount),toQty.units);

The line simply multiplies a quantity’s amount member by a ratio and constructs a new quantity object. So where does the ratio come from? A quantity has two
members: an amount which is a
float type and units which is a unit type. Here’s part of what a typical
unit class looks like:

class unitPounds extends unit
{
  function conversionRatio(intoUnit:unit) : float
  {
    if(intoUnit.type == unitsOfMeasure.kg)
      return (2.2);
    else
      return -1;
  }
}

The unit’s conversionRatio
member function takes a unit
type as a parameter and evaluates it to determine if it’s
possible to convert from itself to the units that the parameter’s
unit type uses. If the
conversion is possible, the function returns a conversion ratio
and returns -1 if the conversion is not possible. If you take a
look back at the convert
function, you’ll see that it checks for the -1 result, otherwise
it just performs the conversion using the ratio. This is a simple
approach that attempts to encapsulate as much information as
possible within each class, thereby relieving class users of
having to understand the intricacies of units and their
conversion ratios.

The key benefit that the design demonstrates is that classes
enable you to work at very high levels of abstraction making code
easier to design, write, and maintain. Although you can just as
easily implement this sample using only procedural code, it would
get very difficult to maintain once the system uses a certain
number of units of measure and measurements.

The implementation of the classes uses a number of object
oriented programming techniques including inheritance and
overloading. Refer to the sample code that accompanies this
article for more details.

Downloading and working with the sample code

The sample code is a JScript .NET console application that
implements the functionality I described in this article. The
primary feature of the application is its code, not its output;
as a result, the code does very little with regards to generating
output or exercising the application at large. The intent of the
sample is primarily to provide an object oriented implementation
that you can study, experiment with, and extend. Once you
download the application, compile it using the command jsc appliance.js and then run the
sample by typing appliance at
the command prompt. I originally wrote the application on an
early beta version of the .NET Framework and have since modified
it to work with .NET version 1.0 – your mileage may vary if you
try the application on any beta versions.

  • Download the sample code
    here
  • Note: you may have to right-click and select “Save As”
    from the pop-up menu to download the file in case it loads into
    your browser when you left-click on the link

    Essam Ahmed is the author of “JScript .NET Programming” (ISBN 0764548689, Published by Hungry Minds September 2001), many
    articles (including some at CodeGuru.com) and book reviews (also available at CodeGuru.com).
    Contact Essam at essam@designs2solutions.com,
    or at his Web site

    More by Author

    Get the Free Newsletter!

    Subscribe to Developer Insider for top news, trends & analysis

    Must Read