![]() |
The NetRexx Tutorial
- More on NetRexx Classes
|
Introduction In this chapter we'll look at some "details" we intentionally left uncovered in the previous discussion.
Basic Concepts
Patterns and Pattern Design Pattern Design is used to sketch a solution to some particular Object Oriented problem. It has probably already happened to you (as it did to me) to think: "I've already solved this problem (or a similar one) in the past." Then you rush to your code and try to find the solution again. If I'm allowed to make such comparison, then, "Design Patterns" stand to "Object Oriented Programming" as "Algorithms" stand to "Procedural Programming". Even further, Gamma, Helm, Johnson and Vlissides text stands to "Design Patterns" as Knuth's stands to "Algorithms".
The key issue is to make your software reusable. Using Design Patterns, you not only make it such, but you also reuse other's people efforts to find the right solution.
Usage of Abstract Classes
A Simple (?) problem Let us consider a class hierarchy for a simple problem: we consider the "universe" of 2D rectangular objects, where we'll find Rectangles and Squares. A Venn diagram representing our "universe" might be useful:
+---------------------------------------------+ U
| | N
| RECTANGLE | I
| | V
| +--------------+ | E
| | | | R
| | SQUARE | | S
| | | | E
| +--------------+ |
+---------------------------------------------+
Venn diagram of the
"universe" class RECTANGLE
with a subclass (SQUARE)
Making an Object Model Recalling what we saw in the previous section, we can try to implement the above diagram using NetRexx. The first thing I'd think of is to make a Rectangle class, and have Square defined as a subclass of Rectangle.
Let's make an Object Model for this diagram:
+--------------+
| |
| RECTANGLE |
| |<-----( RE1 )
+--------------+
|
*
* *
*****
|
+--------------+
| |
| SQUARE |
| |<-----( SQ1 )
+--------------+
Note that, in our diagram:
So, from our picture we can say phrases like: the class "SQUARE" "is-a-subclass-of" the class "RECTANGLE", or the object "SQ1" is an instance of the class "SQUARE".
Implementing it in NetRexx The actual implementation is trivial: so just look at the code.
+----------------------------------------------------------------------+
| -- abex1.nrx |01
| -- Implements Rectangles and Squares |02
| -- |03
| class abex1 public |04
| properties public |05
| |06
| method main(args=String[]) public static |07
| args = args |08
| |09
| RE1 = _Rectangle(1,2) |10
| say RE1.area() |11
| |12
| SQ1 = _Square(2) |13
| say SQ1.area() |14
| |15
| exit 0 |16
| |17
| class _Rectangle |18
| properties public |19
| length |20
| width |21
| method _Rectangle(l=Rexx,w=Rexx) public |22
| length = l |23
| width = w |24
| method area public |25
| return this.length*this.width |26
| method set_width(w=Rexx) public |27
| this.width = w |28
| method set_length(l=Rexx) public |29
| this.length = l |30
| method perimeter public |31
| return 2*(this.length+this.width) |32
| |33
| class _Square extends _Rectangle |34
| method _Square(s=Rexx) public |35
| super(s,s) |36
| method area public |37
| return this.length*this.length |38
| method perimeter public |39
| return 4*this.length |40
+----------------------------------------------------------------------+
abex1.nrx
|
|
Download the source for the abex1.nrx example
Critics to the above implementation There is a series of problems with the above implementation; I analyse them in order of increasing importance.
SQ1 = _Square(3) SQ1.setlength(4)which is, in my opinion, REALLY a bad thing: we allow people to make squares with different sides.
Using Abstract Class To correctly represent the Venn Diagram, we MUST use three classes. The universe class will be an "abstract" class, that we can call 2DSHAPE.
Let's revise our Object Model:
+--------------+
| |
| 2DSHAPE |
| |
+--------------+
|
*
* *
+--------*****-----------------+
| |
| |
+--------------+ +--------------+
| | | |
| SQUARE | | RECTANGLE |
| |<--( SQ1 ) | |<--( RE1 )
+--------------+ +--------------+
Implementation In order to create an abstract class (i.e. a class that contains at least an abstract method), we use the keyword abstract (note that in C++ the keyword virtual is used).
That's how you'd implement in NetRexx:
+----------------------------------------------------------------------+
| -- abex2.nrx |01
| -- abstract class example |02
| -- |03
| class abex2 public |04
| |05
| method main(args=String[]) public static |06
| args = args |07
| R1 = _Rectangle(2,3) |08
| say R1.area() |09
| S1 = _Square(3) |10
| say S1.area() |11
| say 'You defined' _2Dshape.nobjects 'shapes.' |12
| exit 0 |13
| |14
| class _2Dshape abstract |15
| properties public static |16
| nobjects = 0 |17
| method _2dShape() public |18
| nobjects = nobjects+1 |19
| method area public returns Rexx abstract |20
| method perimeter public returns Rexx abstract |21
| |22
| class _Rectangle extends _2Dshape |23
| properties private |24
| length |25
| width |26
| method _Rectangle(l=Rexx,w=Rexx) public |27
| super() |28
| length = l |29
| width = w |30
| method area public |31
| return length*width |32
| method perimeter public |33
| return 2*length*width |34
| |35
| class _Square extends _2Dshape |36
| properties private |37
| side |38
| method _Square(s=Rexx) public |39
| super() |40
| side = s |41
| method area public |42
| return side*side |43
| method perimeter public |44
| return 4*side |45
| |46
+----------------------------------------------------------------------+
abex2.nrx
|
|
Download the source for the abex2.nrx example
Interfaces *** This section is:*** and will be available in next releases
Dynamical Interfaces
Sample code The interface part will look as follows:
+----------------------------------------------------------------------+
| -- runnable.nrx |01
| -- |02
| class runnable interface |03
| method run() public |04
+----------------------------------------------------------------------+
runnable.nrx
|
|
Download the source for the runnable.nrx example
+----------------------------------------------------------------------+
| -- dyna2.nrx |01
| -- |02
| class dyna2 public |03
| |04
| method main(args=String[]) public static |05
| arg = Rexx(args) |06
| do |07
| r = runnable; |08
| un = Class.forName(arg); |09
| r = runnable un.newInstance() |10
| r.run() |11
| catch e= Exception |12
| say e |13
| end |14
| exit 0 |15
| |16
| class test1 implements runnable |17
| method run public |18
| say 'Hello from class TEST1' |19
| |20
| class test2 implements runnable |21
| method run public |22
| say 'Hello from class TEST2' |23
| |24
+----------------------------------------------------------------------+
dyna2.nrx
|
|
Download the source for the dyna2.nrx example
+----------------------------------------------------------------------+
| -- dyna3.nrx |01
| -- |02
| class dyna3 public |03
| |04
| method main(args=String[]) public static |05
| arg = Rexx(args) |06
| loop forever |07
| say 'Enter Class name (A,B,C) or quit' |08
| parse ask.upper() name |09
| if name = 'QUIT' then leave |10
| do |11
| r = runnable; |12
| un = Class.forName(name); |13
| r = runnable un.newInstance() |14
| r.run() |15
| catch e= Exception |16
| say e |17
| end |18
| say 'There are' A.n 'instances for A.' |19
| say 'There are' B.n 'instances for B.' |20
| say 'There are' C.n 'instances for C.' |21
| end |22
| say 'End.' |23
| exit 0 |24
| |25
| -- class A |26
| -- |27
| class A implements runnable |28
| properties static |29
| n = 0 |30
| method A public |31
| n = n+1 |32
| method run public |33
| say 'Hello from class A' |34
| |35
| -- class B |36
| -- |37
| class B implements runnable |38
| properties static |39
| n = 0 |40
| method B public |41
| n = n+1 |42
| method run public |43
| say 'Hello from class B' |44
| |45
| -- class C |46
| -- |47
| class C implements runnable |48
| properties static |49
| n = 0 |50
| method C public |51
| n = n+1 |52
| method run public |53
| say 'Hello from class C' |54
+----------------------------------------------------------------------+
dyna3.nrx
|
|
Download the source for the dyna3.nrx example
This is what we get running dyna3:
.............................................................. Enter Class name (A,B,C) or quit A Hello from class A There are 1 instances for A. There are 0 instances for B. There are 0 instances for C. Enter Class name (A,B,C) or quit A Hello from class A There are 2 instances for A. There are 0 instances for B. There are 0 instances for C. Enter Class name (A,B,C) or quit A Hello from class A There are 3 instances for A. There are 0 instances for B. There are 0 instances for C. Enter Class name (A,B,C) or quit B Hello from class B There are 3 instances for A. There are 1 instances for B. There are 0 instances for C. Enter Class name (A,B,C) or quit C Hello from class C There are 3 instances for A. There are 1 instances for B. There are 1 instances for C. Enter Class name (A,B,C) or quit B Hello from class B There are 3 instances for A. There are 2 instances for B. There are 1 instances for C. Enter Class name (A,B,C) or quit quit End. .............................................................. |
*** This section is:*** and will be available in next releases
Patterns
The Singleton The idea of Singleton is simple: we want to make sure that a class has ONLY one instance, and we want to provide a global point of access to it.
The structure is (GAMMA, 96, p. 127)
+----------------------------+ | Singleton | +----------------------------+ +----------------------\ | static Instance() *-------------> | return uniqueInstance| | (...) | +----------------------+ | SingletonOperation() | | GetSingletonData() | | | | | | | +----------------------------+ | static UniqueInstance | | (...) | | singletonData | | | | | +----------------------------+ |
NetRexx Implementation of the Singleton The NetRexx implementation of the Singleton Pattern might look like:
+----------------------------------------------------------------------+
| -- Singleton.nrx |01
| -- NetRexx Implementation of Singleton |02
| -- see GAMMA, 1996, p.127 |03
| -- |04
| class Singleton public |05
| |06
| properties private static |07
| _instance = Singleton NULL |08
| |09
| method Singleton() private |10
| |11
| method Instance() returns Singleton public static |12
| if _instance = NULL then |13
| do |14
| _instance = Singleton() |15
| return _instance |16
| end |17
| return _instance |18
+----------------------------------------------------------------------+
Singleton.nrx
|
|
Download the source for the Singleton.nrx example
Let's look at it closely. The first "uncommon" feature we find is:
method Singleton() private
i.e. the constructor is declared as private. Clients will not be capable to access it with a normal:
s = Singleton()
Instead, they're forced to use the Instance() member function, declared as static.
This means that the clients will need to write:
s = Singleton.Instance()
in order to get the unique Singleton's instance.
*** This section is:*** and will be available in next releases
An history class.
Description of the problem It is sometimes interesting to record the actions that an user enters when dealing with an interactive program. This is, for example, the case of the history command in an UNIX shell.
First approach. When I dealt for the first time with an implementation of an history command, my solution was to define a history buffer (with his length):
properties public static
cmdbuf = Rexx(")
cmdbufl = 20
and 2 methods to save/dump the history:
+----------------------------------------------------------------------+
| -- method......: historyd |44
| -- purpose.....: display the history |45
| -- |46
| method historyd(cur=Rexx) public static |47
| if cur < cmdbufl |48
| then st = 1 |49
| else st = cur-cmdbufl |50
| loop i = st to cur-1 |51
| say i.right(5) cmdbuf[i] |52
| end |53
| |54
+----------------------------------------------------------------------+
xshell1.nrx(Method:historyd)
|
|
Download the complete source for the xshell1.nrx library
+----------------------------------------------------------------------+
| -- method......: history |55
| -- purpose.....: history |56
| -- |57
| method history(a=Rexx,n=Rexx) public static |58
| if a <> '' then |59
| do |60
| cmdbufl = a |61
| end |62
| else |63
| do |64
| historyd(n) |65
| end |66
| |67
+----------------------------------------------------------------------+
xshell1.nrx(Method:history)
|
|
Download the complete source for the xshell1.nrx library
In the main loop, I was calling saving the entered command in the buffer
cmdbuf[cmdno] = todo cmdno = cmdno+1
The history class The commands are saved in the history buffer inside a circular buffer
+----------------------------------------------------------------------+
| -- method......: save |66
| -- purpose.....: |67
| -- |68
| method save(entry=Rexx) public |69
| k = lastrec // maxrec |70
| if record[k] <> NULL then |71
| do |72
| if entry = record[k] |73
| then return |74
| end |75
| lastrec = lastrec+1 |76
| k = lastrec // maxrec |77
| record[k] = entry |78
| |79
+----------------------------------------------------------------------+
history.nrx(Method:save)
|
|
Download the complete source for the history.nrx library
+----------------------------------------------------------------------+
| -- method......: dump |45
| -- purpose.....: |46
| -- |47
| method dump(n=Rexx) public |48
| first = lastrec - n + 1 |49
| loop i=first to lastrec |50
| k = i// maxrec |51
| if record[k] = NULL then iterate |52
| if record[k] = '' then iterate |53
| say i.right(5) record[k] |54
| end |55
| |56
+----------------------------------------------------------------------+
history.nrx(Method:dump)
|
|
Download the complete source for the history.nrx library
+----------------------------------------------------------------------+
| -- method......: retrieve |57
| -- purpose.....: |58
| -- |59
| method retrieve(n=Rexx) public returns Rexx |60
| if n < lastrec - maxrec then return " |61
| if n > lastrec then return " |62
| k = n// maxrec |63
| return record[k] |64
| |65
+----------------------------------------------------------------------+
history.nrx(Method:retrieve)
|
|
Download the complete source for the history.nrx library
his = history(100)
loop
-- get user input
his.save(USER_INPUT)
end
Additional sources of information You can find additional information about patterns at:
http://st-www.cs.uiuc.edu/users/patterns/
with some tutorial information at:
http://www.enteract.com/~bradapp/docs/patterns-intro.html http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/patterns/
Chapter Summary *** This section is:*** and will be available in next releases