Also, remember our maxim "The best way to write correct code is to copy correct code." For example, all Java and C# SDK class definitions are available online. Google "java rectangle" to compare the Java class definition to the one in the book.

This Section illustrates the class concept with the design of a fraction class (integer numerator divided by an integer denominator). Remember that the goal of a class definition is to extend a language's built-in types (e.g. float, integer, string). A complementary goal is to enable software reuse by other programmers. Every class implementation has a large (and hidden) intellectual component that is also reused. In many cases, the thinking component can take much longer to evolve than writing the code that embodies it.

We use a top-down, design approach; that is, start by identifying the methods then choose a data representation. The goals are to achieve functional completeness, to provide easy-to-use methods to the user, and to be a one-and-done solution.

Functional completeness requires a class definition that encompasses all operations that you might want to perform on a fraction. For example, omitting a reciprocal method would be a design deficiency.

Classes are well-thought-out, long-term solutions. Typically, a class deﬁnition has many more methods than any example that has been presented so far. Following our own maxim, we will use the Apache Fraction class in Java as a model.

A class will, at the minimum, have the following components, which we describe in turn.

- data definitions,

- constructor (new),
- constants,

- copy constructor (copy),

- setters, getters, (set, get)

- mutators (modify class data and state),

- predicates including isInstance,

- parse and tostring methods.

In Lua, functions can be treated as values, just like data such as integers or strings. Lua supports a shorthand notation that allows functions to be deﬁned and accessed as table ﬁelds. Once defined, the functions can be accessed with the colon-notation e,g, num,dem=frac:getND().

Fraction = {

numerator = 0,

denominator = 0,

getND =

} --Fraction

The getND function can also be deﬁned separately from the table as follows:

Fraction = {

numerator = 0,

denominator = 0

} --Fraction

The even better, cool Lua notation that we recommend is the colon form of a function declaration that hides the "self" parameter on the declaration side and that automatically inserts the "self" argument on the call side. Within the function getND, the fields of Fraction can be accessed without qualification.

Fraction = { --<<-- use this syntax

numerator = 0,

denominator = 0

} --Fraction

Copy the following code into a
main.lua file and then execute it. The code
implements, one and only one, Fraction. Remember that
the goal is to allow the user to add new data types to the
language. To achieve that goal, there must be a way to
create new copies of what might be thought of as the
canonical, or default, definition of a Fraction. In
almost every language, the method name chosen to create
class instances is "new", which is referred to as a __constructor__.

Fraction = {

numerator = 0,

denominator = 0

} --Fraction

**print**(Fraction.numerator, Fraction.denominator)

**OUTPUT**

0 0

All class methods will be defined as components of Fraction. Copy the following code into a main.lua file and then execute it. The modf function from the math library is applied to enforce the constraint that the numerator and denominator are integers. Furthermore, a minus sign is only allowed for the numerator.

Fraction = { --prototype, stores data and function definitions

numerator = 0,

denominator = 0

} --Fraction

**function **Fraction:new(num, den)

**local **f = { numerator = math.modf(num),

denominator = math.abs( math.modf(den) ) }

**return **f

**end **--new

**local **frac = Fraction:new(3,4)

**print**(frac.numerator, frac.denominator)

**OUTPUT**

3 4

It would be very desirable to be able to invoke the "new" method without having a reference to Fraction (e.g. frac2=frac:new(5,6) ). However, this generates an error message: "attempt to call method 'new' (a nil value)". The reason is that the name "new" is defined in the Fraction table but was not included in the "frac" table. Luckily, Lua provides some "magic" to allow multiple tables to share definitions. Any name (not just functions) that is missing from "frac" will be queried in the Fraction table. Copy the following code into a main.lua file and then execute it.

Fraction = { --prototype, stores function definitions

numerator = 0,

denominator = 0

} --Fraction

**function **Fraction:new(num, den)

**local **f={numerator = math.modf(num),

denominator = math.abs( math.modf(den) ) }

setmetatable(f, {__index = Fraction} ) --refer to Fraction for unknown names

**return **f

**end **--new

**local **frac = Fraction:new(-3.1,4.8)
--note illegal input will be truncated to 3 4

**print**(frac.numerator, frac.denominator)

**local **frac2 = frac:new(6, 7)

**print**(frac2.numerator, frac2.denominator)

**OUTPUT**

-3 4

6 7

Examine the Apache Fraction definition (click on the previous link). It includes a constructor that implements whole-number fractions. Obviously, this is just for convenience as new(integer, 1) accomplishes the same purpose. There is rarely a single "right" class design. Note that the single "new" is functionally complete as the integer option can be programmed with it.

Another option would be to define a default constructor newDefault() that just assigned default values to the numerator and denominator.

Fraction = {

numerator = 0,

denominator = 0

} --Fraction

0 0

All class methods will be defined as components of Fraction. Copy the following code into a main.lua file and then execute it. The modf function from the math library is applied to enforce the constraint that the numerator and denominator are integers. Furthermore, a minus sign is only allowed for the numerator.

Fraction = { --prototype, stores data and function definitions

numerator = 0,

denominator = 0

} --Fraction

denominator = math.abs( math.modf(den) ) }

3 4

It would be very desirable to be able to invoke the "new" method without having a reference to Fraction (e.g. frac2=frac:new(5,6) ). However, this generates an error message: "attempt to call method 'new' (a nil value)". The reason is that the name "new" is defined in the Fraction table but was not included in the "frac" table. Luckily, Lua provides some "magic" to allow multiple tables to share definitions. Any name (not just functions) that is missing from "frac" will be queried in the Fraction table. Copy the following code into a main.lua file and then execute it.

Fraction = { --prototype, stores function definitions

numerator = 0,

denominator = 0

} --Fraction

denominator = math.abs( math.modf(den) ) }

setmetatable(f, {__index = Fraction} ) --refer to Fraction for unknown names

-3 4

6 7

Examine the Apache Fraction definition (click on the previous link). It includes a constructor that implements whole-number fractions. Obviously, this is just for convenience as new(integer, 1) accomplishes the same purpose. There is rarely a single "right" class design. Note that the single "new" is functionally complete as the integer option can be programmed with it.

Another option would be to define a default constructor newDefault() that just assigned default values to the numerator and denominator.

Fraction.ZERO = Fraction:new(0,1)

Fraction.ONE = Fraction:new(1,1)

Fraction.ONE_HALF = Fraction:new(1,2)

0 1

1 1

1 2

Notice that the "class constants" are accessible to all fraction variables. Be careful with constants as their reference in assignment statements does not create a copy!! All copies refer to the same table so any change would destroy the constant!!! Use a copy constructor to assign constants to variables that may later be modified.

frac.numerator = 3 --bad!! changes all variables with a reference to ZERO

After cloning, any changes to the new copy have no effect on the original variable. In the following example, frac2 is set to ONE and then modified. When frac3 is set to ONE, notice that the constant has remained untouched. Copy constructors can be used to duplicate any fraction, not just constants. Add the following code into the main.lua file and then execute it.

frac.denominator = 6

frac2.numerator = 3

print(frac3.numerator, frac3.denominator)

0 6

3 1

1 1

A

self.numerator, self.denominator = num, den

frac:setND(5,6)

3 4 0.75

5 6

The algorithms and choice of data types for use in mutators have been studied since the invention of computing. In computer science curricula, there is always a data structures and typically an algorithms class. The following "add" method is only a sampling of the possible fraction methods.

A perennial issue in designing mutators is whether to create a new object to hold the result or to modify the "self" variable. For example, should the addition of two fractions return a "new" fraction or just add the second fraction to the first?

The code implements "chaining" (see the book), which supports the combination of methods. Without chaining, every operation would require a separate assignment statement. A chaining function returns "self", which causes its input variable to be passed along to the next function in the chain.

Search online to find the code for additional mutator methods. Add the following code into the main.lua file and then execute it.

self.numerator = self.numerator+frac.numerator

self.numerator = self.numerator*frac.denominator + self.denominator*frac.numerator

self.denominator = self.denominator*frac.denominator

frac2:add(frac)

frac:add(frac2:new(3,6)):add(frac2:new(2,6)) --chaining

3/4 + 5/6 =

38 24

5/6 + 3/6 + 2/6 =

10 6

Even though "compare" covers all cases, some classes also define an "equals" predicate. Add the following code into the main.lua file and then execute it.

result=self.numerator-frac.numerator

result=self.numerator/self.denominator-frac.numerator/frac.denominator

1/3 compare 1/2

-1

1/2 compare 1/3

1

1/3 compare 33333/99999

0

The Lua "type" function only returns the name of basic types such as 'string' or 'table'. Knowing that a variable is a table does not provide sufﬁcient information about classes. Thus, an "isInstance" predicate is deﬁned that answers the questions "am I a class instance?" or "am I a Fraction?". Since Lua does not provide the information, it needs to be stored in the class' prototype as listed next. Modify the class definition and add the test code into the main.lua file and then execute it.

Fraction = {

class = ',Fraction,class,' --add this line

}

true

false

true

We also recommend defining a setFormat method so that users have control over the appearance of output. If the "format" field is set in the prototype, it would apply to all instances. However, any instance can override the default by setting its own format. Add the following code into the main.lua file and then execute it.

frac=Fraction:new(2,3)

frac:setND(-14,6)

frac:setND(-12,6)

0

2/3

-2 2/6

-2

Lua has some more "magic" in store because printing is such a frequent occurrence. By adding a special definition, the ":tostring" can be omitted entirely when printing objects. The update is included in the linked file Fraction.lua.

The "parse" method is somewhat the opposite of tostring in that it converts a string to the internal data representation of an object. This method is left as an exercise.

As a result, a coder should be able to search the web and find a class implementation for almost any object in any language. Sadly, this is not the case. Apparently, programmers like to write the same code over and over. I practice what I advocate. I have over 1100 Java modules posted here.

It would be annoying if the use of a Fraction object required copying and pasting its source code. The Lua language includes the syntax to support the deﬁnition of classes in separate modules (text ﬁles such as Fraction.lua). In the main.lua program, a "require" function can be executed at any time to retrieve a copy of a class deﬁnition, which can then be used to create new instances or to access constants. Further, all instances can also be used to create new instances and to access class constants.

local frac=Fraction:new(0,3)

print(frac)

frac=Fraction:new(2,3)

print(frac)

frac:setND(-14,6)

print(frac)

frac:setND(-12,6)

print(frac)

print(frac:copy())

To create the Fraction.lua module, the prototype and methods were copied to file Fraction.lua and then a "return Fraction" statement was added to the end of the file.