April 24, 2024, 03:40:23 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


OOP Tutorial 2

Started by Parker, March 11, 2007, 10:32:20 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Parker

In the last tutorial I extended the POINT type to the CPoint class and added a constructor and destructor so that it initializes x and y to 1. The class looked like this:
class CPoint
def x as int
def y as int
declare CPoint( )
declare _CPoint( )
end class

sub CPoint::CPoint( )
x = 1
y = 1
end sub

sub CPoint::_CPoint( )
end sub


In this tutorial I am going to continue the CPoint class by adding methods and I will say a little about using getter and setter methods to access variables. This will require using the public and private modifiers which I will also explain.




First I'm going to introduce methods. If you know about using subroutines, methods in classes should feel right at home. Methods are simply subroutines that belong to a certain class. As I stated before, methods have a hidden parameter called 'THIS', which is a pointer to the current class instance. If I have three variables a, b, and c, all of the same class which contains a method called method(), I can write this:
a.method() '' THIS is a pointer to a
b.method() '' THIS is a pointer to b
c.method() '' THIS is a pointer to c
Remember that THIS is only valid inside of a class method. Outside of class methods, THIS is not a reserved word and has no special meaning, so you cannot use THIS to refer to the class that you last called a method on. THIS is a parameter in reality, but since it is hidden you don't have to think of it that way. If you'd like, you can think of THIS as a variable that is magically defined whenever you call a method on a certain variable. What you do have to know is that THIS will always be a pointer to the variable that the method was called on.

In reality, the THIS pointer is the only way that methods differ from normal subroutines. And as I stated before, you only need to use the THIS pointer when the class member variable conflicts with a parameter or global variable. Therefore it should be easy to add methods to our class. We are going to add methods to get x and y, and also to set them. These methods will look like this:
sub CPoint::SetX( int newx )
x = newx
end sub

sub CPoint::GetX( ),int
return x
end sub

sub CPoint::SetY( int newy )
y = newy
end sub

sub CPoint::GetY( ),int
return y
end sub


If you add these methods and compile the code however, you should get errors. While you don't normally have to declare subroutines, methods must be declared inside of the class that they are contained in. The reasoning behind this is the same reason that you have to define variables inside of a TYPE. If you didn't, the compiler would have to look through every source file that contains a variable of that class and add all the variables and methods. And if that weren't enough, it still wouldn't know if the methods return a result that is being discarded or if they have optional parameters, or some attribute that is on them. There are other issues too, like not having code that references every variable and method and the compiler not knowing which order they should be in. So you have to declare methods and variables inside of the class.

The declares for our four new methods make the class look like this:
class CPoint
private
def x as int
def y as int

public
declare CPoint( )
declare _CPoint( )

declare SetX( int newx )
declare GetX( ),int
declare SetY( int newy )
declare GetY( ),int
end class


You may have noticed that I wrote the words private and public inside of the class too. First I will explain what they do, and then I will explain why I used them.

Private, public, and protected are access modifiers. We don't deal with protected until we get to inheritance, so I will leave it out of the discussion until then. Public is the default in EBASIC. In C++ the default is private. For this reason it is always smart to explicitly specify which access you want so that you don't get unexpected results. Public means that anybody can access the members. Anyone using that class can call a public method, and they can read and write to a public variable. In a lot of cases this is fine. However, in some cases you don't want just anybody to change a variable. Or sometimes you write methods that are helpers and nobody outside of the class needs to use them. Fortunately for you, EBASIC provides the private modifier. Private means that access to a variable or method is restricted to that class only. If you try to access a private member from a method inside of the class, the code will compile fine, but if you try to access it from outside the class you will get an error.

There is a reason why I made x and y private. It is considered good OOP practice to hide your variables from the outside world. It may seem awkward to work through get and set methods at first, but there is a reason behind this. For one, you may not always want people to modify your variables. If you have a counter variable, you may not want people to mess with it, but they need to be able to read it. Make it private and provide a GetCounter() method. If you are hiding a concept altogether but still need a variable for the class to function, make it private so that no one has to worry about it. If you have a helper method that is not reliable unless called from somewhere within that class, or it's just not needed otherwise, make it private and people will know that they don't need to worry about it. It is considered good OOP practice to provide get and set methods for variables, even if you don't need to restrict access to them. Who knows? You may need to in the future, and it's better to have the functionality present than absent so that you don't have to change other code.

Depending on the language you may need to use the private, public, and protected modifiers differently. In C++ and Aurora they have a colon after them and modify every variable that follows them until a new modifier is found. For example,
private:
  int x; // x is private
  int y; // y is also private
In C# and Java they proceed the variable or method. For example,
private int x; // x is private
private int y; // y is also private
int z; // z is _not_ private because the modifier is absent
In EBASIC it acts as in Aurora and C++, but you don't need the colon. Use my CPoint class as an example.

In our CPoint class, I am going to make use of information hiding. I have decided that our coordinate system doesn't have any coordinates less than 1. Therefore we shouldn't let anyone set the x or y to 0 or less. If we left x and y public, you could easily set them to 0, -4, or anything else that they shouldn't be set to. This could cause undefined behavior in the program, or make more of a mess for the programmer to worry about later, so it's best to restrict these through the set method.

Now we have methods that look like this:
sub CPoint::SetX( int newx )
if newx < 1 then return
x = newx
end sub

sub CPoint::GetX( ),int
return x
end sub

sub CPoint::SetY( int newy )
if newy < 1 then return
y = newy
end sub

sub CPoint::GetY( ),int
return y
end sub


At this point we have a fully functional CPoint class which only allows coordinates of 1 or greater.
class CPoint
private
def x as int
def y as int

public
declare CPoint( )
declare _CPoint( )

declare SetX( int newx )
declare GetX( ),int
declare SetY( int newy )
declare GetY( ),int
end class

sub CPoint::CPoint( )
x = 1
y = 1
end sub

sub CPoint::_CPoint( )
end sub

sub CPoint::SetX( int newx )
x = newx
end sub

sub CPoint::GetX( ),int
return x
end sub

sub CPoint::SetY( int newy )
y = newy
end sub

sub CPoint::GetY( ),int
return y
end sub


In the next tutorial I will cover inheritance by extending our CPoint class into a CPoint3D which also has a Z value.

As always, questions will be answered, just reply.

Parker