Example: Creating An Object-Oriented Application in SCL |
While designing the class structure, you might find that some classes share functionality with other classes. In that case, you can extend classes by creating subclasses to prevent duplication of functionality.
In Constructors, the DDATA class implicitly extended OBJECT.CLASS. In fact, any class without an explicit EXTENDS clause in the CLASS statement extends OBJECT.CLASS. To explicitly extend a class, add the EXTENDS clause shown below:
class y extends x; endclass;
In this case, class Y extends the class X. Alternatively, Y is a subclass of X, and X is the parent class of Y.
This enables Y to share X's functionality. For example, if the class X were
class x; m: method; put 'Hello'; endmethod; endclass;
and the class y were
class y extends x; endclass;
then you could call the method M using an example of the class Y:
init: dcl y y = _new_ y(); y.m(); return;
Access Modifiers |
The access modifiers that we mentioned above - public, private and protected - can now be explained. A variable (or method) that is declared as public can be accessed anywhere. A variable (or method) that is declared as protected can be accessed only by non-proper subclasses of the class in which its declared. Protected variables can be accessed only from the class in which they are declared. This is also true for protected variables that are accessed from the subclasses of those classes.
These modifiers restrict access to certain variables (or methods) that should not be seen outside the class or class hierarchy in which they are declared. For example, there is no need for any class outside the DATA class hierarchy to access the fid variable, so it is declared as protected but could also be declared as private.
The DDATA Class as a Subclass |
To illustrate how subclassing works with the DDATA class, this exercise creates a similar class for external data files. The following SCL functions will be used:
These SCL functions will be used to create a class called FDATA to represent an external file. It is important to note similarities to the DDATA class. In particular, each class will have a name, input mode, and file identifier, so a class will be created to store this information. Then the subclasses DDATA and FDATA will be created from the DATA class. The parent data class will beclass data; public num type; public string dname; public string mode; protected num fid; data: method f: num n: string m:string; fid = f; dname = n; mode = m; endmethod; endclass;
In addition to the name, mode and file id, a type variable is stored to indicate whether FDATA is an external file or a SAS data set.
The constructor DATA will be called whenever an example of the DATA class is created. It will also be called automatically whenever any subclasses of data are created if the constructor in the subclass has not be overridden. If the constructor has been overridden, you must use _super to call the parent constructor. You must also use _super if the argument list that is used in the _NEW_ operator does not match the argument list of the parent constructor. This will be the case for DDATA and FDATA.
To extend the DATA class, modify the DDATA CLASS statement, data declarations, and constructor as follows:
class ddata extends data; /* Class data */ protected num nvars; /* Constructor method */ ddata: method n: string m:string nv:num; fid = open(n, m); _super(fid, n, m); nvars = nv; type = 1; endmethod;
In this example, the DDATA constructor will call the data constructor via the _super call. This sets the name, mode and file identifier that are stored in the parent class data. The DDATA constructor still sets nvars and also sets the type field to indicate that the file is a data set. The rest of the class will remain the same.
The FDATA Class |
The declaration and constructor of the FDATA class will be similar to those of the DDATA class, as shown in the following:
class fdata extends data; /* Constructor method */ fdata: method n: string m: string; dcl string ref = ""; dcl num rc = filename(ref, n); fid = fopen(ref, m); _super(fid, n, m); type = 2; endmethod; /* FREAD method */ read: method return=num; dcl num rc = fread(fid); return rc; endmethod; /* FGET method */ cpy: method n: num return=string; dcl string c = ""; dcl num rc = fget(fid, c); return c; endmethod; /* FCLOSE method */ _term: method /(state='O'); if (fid) then fclose(fid); _super(); endmethod; endclass;
Use FDATA to read an external class by instantiating it and looping through the data:
init: dcl fdata f = _new_ fdata("some_file", "i"); dcl num more = ^f.read(); do while(more); dcl string s s2; s = f.cpy(1); s2 = f.cpy(2); put s s2; more = ^f.read(); end; f._term(); return;
This code assumes that the external file is formatted with each line containing two character variables separated by a blank. For example:
Geoffrey Chaucer Samuel Johnson Henry Thoreau George Eliot Leo Tolstoy
Overloaded Methods |
Method overloading is the process of defining multiple methods that have the same name, but which differ in parameter number, type, or both. Method overloading lets you use the same name for methods that are related conceptually but take different types or numbers of parameters.
For example, you may have noticed that the CPY method in FDATA has a numeric parameter that apparently serves no useful purpose. You do not need to specify a variable number for an external file. This parameter is used so that in the future when you use interfaces, the CPY method in FDATA matches the one in DDATA. For now, the parameter is not needed. One way of resolving this is to overload the CPY method by creating another CPY method with a different parameter list, as shown in the following code:
cpy: method return=string; dcl string c=""; dcl num rc = fget(fid, c); return c; endmethod; cpy: method n: num return=string; return cpy(); endmethod;
In this example, the original CPY method ignores the parameter and calls a CPY method that returns the character value. By doing this, you have defined two methods that have the same name but different parameter types. With this simple change, you do not have to worry about which method to call.
The CPY method can be used as follows:
s = f.cpy();
Overloaded methods can be used any time you need to have multiple methods with the same name but different parameter lists. For example, you may have several methods that are conceptually related but which operate on different types of data, or you may want to create a method with an optional parameter, as in the CPY example.
To differentiate between overloaded methods, the compiler refers to the method signature, which is a list of the method's parameter types. A method signature provides a means of extending a method name, so that the same name can be combined with multiple different signatures to produce multiple different actions. Method signatures are created automatically when a method is added to a class and when the compiler is parsing a method call. Method signatures appear as part of the information that the Class Editor displays about a method.
Copyright © 2009 by SAS Institute Inc., Cary, NC, USA. All rights reserved.