A Dirty Guide to OOP #1

Post your Gambas programming questions here.
User avatar
stevedee
Posts: 379
Joined: Monday 20th March 2017 6:06pm

A Dirty Guide to OOP #1

Post by stevedee » Friday 24th January 2020 4:00pm

If you want to get started with Object Orientated Programming (OOP) within your Gambas programs, here is a simple place to start.

OOP is all about identifying aspects of your project that can be implemented as objects. An object is usually a noun (e.g. fruit, animal, machine) with Properties (e.g. colour) Methods (e.g. delete) and sometimes Events (e.g. LostFocus).

Although "Class" in general programming can mean just a bunch of code in a module, in OOP a Class is the code you write as a kind of template from which you create Objects.

So here is a simple "fruit" example; Start a new project and then right-click in the Project window and select New > Class... and name it "clsFruit.class".

Another principle of OOP is encapsulation. We usually hide the internal class properties from the outside world and use "Getter" functions & "Setter" functions to read & write them. So even in our simple example, we need to Declare in the clsFruit.class any properties that we want to make available. Initially we need a Property called "Name" and another called "Colour" :-
' Gambas class fileclsFruit.class
'external refs
Property Name As String        'the fruit Name
Property Colour As String      'the fruit Colour
...then we create Private properties:-
'internal refs
Private sFName As String        'the fruit Name
Private sFColour As String      'the fruit Colour
...then we create Getter & Setter functions:-
'Getter functions
Private Function Name_Read() As String
  Return sFName
End

Private Function Colour_Read() As String
  Return sFColour
End

'Setter functions
Private Sub Name_Write(Value As String)
  sFName = Value
End

Private Sub Colour_Write(Value As String)
  sFColour = Value
End
At this stage our class doesn't do much, but we can use it.

On the main project form add this code to create a new instance of our class:-
Public myApple As New ClsFruit
Note: If autocomplete is working on your system, you should see the class name and properties listed as you type.

Now add a Button and add this as its click event:-
Public Sub Button1_Click()

  myApple.Name = "apple"
  myApple.Colour = "red"
  Me.Text = "I have a " & myApple.Colour & " juicy " & myApple.Name
  myApple.
End
So you should now have a working example. You can create as many objects as you like from this new class, for example:-
Public myOrange As New ClsFruit
Public myPlum As New ClsFruit
Public myBanana As New ClsFruit
...and each instance can be given its own unique name & colour.


If this is of interest to anyone, I'm happy to write more.

User avatar
Got2BeFree
Posts: 91
Joined: Saturday 26th November 2016 2:52am
Location: Lost

Re: A Dirty Guide to OOP #1

Post by Got2BeFree » Friday 24th January 2020 10:45pm

stevedee wrote:
Friday 24th January 2020 4:00pm
If this is of interest to anyone, I'm happy to write more.
Yes. I have no formal training in programming so I enjoy reading these short "lessons with examples" (the same with your personal website).
sholzy

I'm wondering around lost in the past, not knowing where the present is.

User avatar
grayghost4
Posts: 83
Joined: Wednesday 05th December 2018 5:00am
Location: Concord, CA usa

Re: A Dirty Guide to OOP #1

Post by grayghost4 » Saturday 25th January 2020 6:34am

Yes I will read everything you write

User avatar
stevedee
Posts: 379
Joined: Monday 20th March 2017 6:06pm

Re: A Dirty Guide to OOP #1

Post by stevedee » Saturday 25th January 2020 5:05pm

A Dirty Guide to OOP #2

Looking at the 'Fruit' example, you may wonder if its really worth the trouble. But one of the central principles of software development is "code re-use". If someone is paying you to write code, they don't want you to keep writing the same or similar stuff. Not only is this time consuming, but its also detrimental to code quality.

So whenever you find a use for some function more than just once or twice, the code should be written into a Module (I think Gambas calls this a static class). And if the task lends itself to becoming an Object, then write this code as a Class (I think Gambas calls this a creatable class).

Also, imagine that we expand our fruit class to have properties & methods for taste, size, position, visibility & so on, and we use it in a crazy fruit game where 100's of pieces of fruit are created and destroyed. If you didn't use objects, your code would probably be huge!

Anyway, I want to expand our fruit class by including a read-only property. All fruits have a skin, so lets add a boolean called HasSkin:-
' Gambas class fileclsFruit.class
'external refs
Property Name As String        'the fruit Name
Property Colour As String      'the fruit Colour
Property Read HasSkin As Boolean     'does this fruit have a skin?

'internal refs
Private sFName As String        'the fruit Name
Private sFColour As String      'the fruit Colour
Private bSkin As Boolean

'Getter functions
Private Function Name_Read() As String
  Return sFName
End

Private Function Colour_Read() As String
  Return sFColour
End

Private Function HasSkin_Read() As Boolean
  Return bSkin
End
Notice that we use "Read" to declare the property and we don't included a setter. But we do need to set this property to True by default:-
Public Sub _new()
  'set defaults here
  bSkin = True
End
And now in the main form, we add some code to check if our fruit has a skin:-
Public Sub Button1_Click()

  myApple.Name = "apple"
  myApple.Colour = "red"
  Me.Text = "I have a " & myApple.Colour & " juicy " & myApple.Name
  If myApple.HasSkin Then
    Me.Text &= " & it has a skin"
  Else
    Me.Text = "My juicy " & myApple.Name & " has no skin"
  Endif
End
Once you remove the skin from fruit, there is no going back. So here is a simple Method called Peel:-
Public Sub Peel()
  bSkin = False
End
...and updated main form code:-
Public Sub Button2_Click()
  myApple.Peel
End

User avatar
stevedee
Posts: 379
Joined: Monday 20th March 2017 6:06pm

Re: A Dirty Guide to OOP #1

Post by stevedee » Monday 27th January 2020 11:36am

A Dirty Guide to OOP #3

For most simple, non-graphical objects that you write, you may only need to implement Properties and Methods as described in the 2 earlier parts of this guide. (Take a look at my Wireless-range project which includes 2 class/objects: viewtopic.php?f=13&t=802)

But another aspect of the OOP paradigm which we should consider is "inheritance", which continues the "code re-use" idea by allowing one object to form the starting point for another. For Properties in particular, this means that the same names & meanings are maintained for many objects. Which makes it easier for us programmers, as we have less to remember.

For example, most of the Gambas visual components inherit from the Control class/object. So many properties (such as Name, Text, Enabled, Background, Width & so on) are the same for all descendants of Control, and quickly gain familiarity with those new to the language.

However, routine use of Inheritance in your written code can easily get out of hand. An object with too many ancestors will quickly accumulate a lot of Properties & Methods, and can make the code very difficult to read & understand (...I'm thinking of my time with C++).

Gambas allows you to use a maximum of (I think) 16 generations or levels of Inheritance, which in my opinion is about 15 too many! (...yes, this is the Dirty part of my simple guide).

Anyway, let's give simple inheritance a go!

Start a new project for vegetables, and copy the clsFruit.class file to the new project's .src folder. Add a new class called clsVeggie.class.

In clsVeggie write "Class clsVeggie Inherits clsFruit" and then create a boolean Property the OOP way, called RootVeg:-
Class clsVeggie Inherits ClsFruit

Property RootVeg As Boolean
Private bRootVeg As Boolean

Private Function RootVeg_Read() As Boolean

  Return bRootVeg
End

Private Sub RootVeg_Write(Value As Boolean)

  bRootVeg = value
End
In the Form class of this new project, create a new instance of clsVeggie and then add some trivial code to test it. Maybe some nonsense like this:-
Public myVeg As New ClsVeggie

Public Sub Form_Open()

  myVeg.Name = "veg-edible"
  myVeg.Colour = "Red"
End

Public Sub Button1_Click()

  myVeg.RootVeg = False
  WhichVeg
End

Public Sub Button2_Click()

  myVeg.RootVeg = True
  WhichVeg
End

Public Sub WhichVeg()
  
  If myVeg.RootVeg Then
    Me.Text = "My " & myVeg.Colour & " " & myVeg.Name & " is a root vegetable"
  Else
    Me.Text = "My " & myVeg.colour & " " & myVeg.Name & " is NOT a root vegetable"
  Endif
End


Note: Although this example demonstrates the principle that the new clsVeggie has inherited the basic Properties & Methods from clsFruit, there are a few points to note:-
1) autocomplete did not work for me when typing in lines like myVeg.Name (maybe that is a limitation of the IDE)
2)The format for Inherits is: Class <this-class>Inherits<ancestor-class>
I found that simply: Inherits<ancestor-class> also works at the time of writing, however its probably safer to use the full version, as the format could be tightened up in subsequent releases without notice.
3) my example may not show the correct way to implement inheritance...who knows? I haven't been able to find an example of how to do this...so if you have a better example, please post it here.
Last edited by stevedee on Tuesday 28th January 2020 10:13am, edited 2 times in total.

User avatar
stevedee
Posts: 379
Joined: Monday 20th March 2017 6:06pm

Re: A Dirty Guide to OOP #1

Post by stevedee » Monday 27th January 2020 2:05pm

A Dirty Guide to OOP #4

There may be times when you want your Object to raise Events, especially if your program is for a computer with switches or sensors attached to its inputs.

Returning to our Fruit class example, I'm going to add a "rotten fruit" Event.

In clsFruit, add a line to declare the Event, a timestamp in the class constructor, and a new Method called "sniff". The complete class code should now look like this:-
' Gambas class fileclsFruit.class
Event Rotten(sAlarm As String)
'external refs
Property Name As String        'the fruit Name
Property Colour As String      'the fruit Colour
Property Read HasSkin As Boolean     'does this fruit have a skin?

'internal refs
Private sFName As String        'the fruit Name
Private sFColour As String      'the fruit Colour
Private bSkin As Boolean
Private dteBuy As Date

'Getter functions
Private Function Name_Read() As String
  Return sFName
End

Private Function Colour_Read() As String
  Return sFColour
End

Private Function HasSkin_Read() As Boolean
  Return bSkin
End

'Setter functions
Private Sub Name_Write(Value As String)
  sFName = Value
End

Private Sub Colour_Write(Value As String)
  sFColour = Value
End


'constructor
Public Sub _new()
  'set defaults here
  bSkin = True
  dteBuy = Now()    'date we buy the fruit
   
End

'Methods
Public Sub Peel()
  bSkin = False
End


Public Sub Sniff()
'is the fruit still OK to eat?
  
  If DateDiff(dteBuy, Now(), gb.Second) > 10 Then
    Raise Rotten("is rotten!")
  Endif
End
In the Form class, add a third Button, change the object Declaration line to include a local name for the Event, and a routine to run when the event is triggered:-
Public myApple As New ClsFruit As "FruitEvent"


Public Sub Form_Open()
  myApple.Name = "apple"
  myApple.Colour = "red"
End


Public Sub Button1_Click()
  Me.Text = "I have a " & myApple.Colour & " juicy " & myApple.Name
  If myApple.HasSkin Then
    Me.Text &= " & it has a skin"
  Else
    Me.Text = "My juicy " & myApple.Name & " has no skin"
  Endif
End

Public Sub Button2_Click()
  myApple.Peel
End

Public Sub Button3_Click()
  myApple.Sniff
End

Public Sub FruitEvent_Rotten(msg As String)
  Me.Text = "Oh dear! my " & myApple.Name & " " & msg
End
When you run this code, sniffing the fruit by clicking Button3 is not a problem until the life of the fruit is exceeded.

So what's happening here is that:-
1)The Event called "Rotten" is declared in our object/class
2)An Object is Declared and Instantiated in our form code which includes a local name for the Class Event (you can call it anything)
3)The Event routine in our form is referenced by our 'local event name'_'Class event name' (i.e. FruitEvent_Rotten)
4)When the "Raise" keyword is executed in our Object, the FruitEvent_Rotten routine is called and the string containing the message is passed as an argument.

User avatar
sjsepan
Posts: 62
Joined: Saturday 12th October 2019 10:11pm
Location: Leeper, PA, USA
Contact:

Re: A Dirty Guide to OOP #1

Post by sjsepan » Monday 27th January 2020 8:33pm

Hi SteveD,
Like the guide so far. BTW, the syntax you may need probably looks like...
' Gambas class file

Class clsVeggie Inherits clsFruit
...because that's how I got my Dock and Anchor classes to inherit from DockAnchorBase in my DockAnchor library.
SteveS
https://github.com/ssepan2/DockAnchor
_______________
stevedee wrote:
Monday 27th January 2020 11:36am
A Dirty Guide to OOP #3


Note: Although this example demonstrates the principle that the new clsVeggie has inherited the basic Properties & Methods from clsFruit, there are a few points to note:-
1) autocomplete did not work for me when typing in lines like myVeg.Name (maybe that is a limitation of the IDE)
2)The format for Inherits is supposed to be:-
MissingAs.png

...but I couldn't get this to compile.
3) my example may not show the correct way to implement inheritance...who knows? I haven't been able to find an example of how to do this...so if you have a better example, please post it here.

User avatar
stevedee
Posts: 379
Joined: Monday 20th March 2017 6:06pm

Re: A Dirty Guide to OOP #1

Post by stevedee » Tuesday 28th January 2020 10:45am

Cheers SteveS.
sjsepan wrote:
Monday 27th January 2020 8:33pm
Like the guide so far....
I'm not sure I have much more to add on the subject of OOP. I did a search yesterday for polymorphism & Gambas, but didn't find anything useful.

Maybe we can get the views of forum members on whether they use OOP in their programs, or whether they even like it (not everyone is a fan).
I like to create objects when I spot a suitable candidate in a project. I generally avoid using Inheritance between/within classes that I've written.

And I used to avoid putting code in a Form class that had nothing to do with the form (e.g. 'F to 'C conversion function would go in a Module, if it couldn't be part of an object) ...sadly, most of my recent stuff is just a hack (...a hack is where you just sit at your keyboard and type code, with little or no pre-planning!)
BTW, the syntax you may need probably looks like...
Thanks Steve, I have now updated the post with this syntax.
...because that's how I got my Dock and Anchor classes to inherit from DockAnchorBase in my DockAnchor library.
I tried to download your project but it is missing the library: GambasAppUtilityLib ...which I think you will find in your:
/usr/lib/Gambas3/stephensepan directory

User avatar
cogier
Site Admin
Posts: 648
Joined: Wednesday 21st September 2016 2:22pm
Location: Guernsey, Channel Islands

Re: A Dirty Guide to OOP #1

Post by cogier » Tuesday 28th January 2020 12:54pm

I will tell you a story. I was looking at the Spanish Gambas forum and it was asked if Gambas could do something like this video. (The tread is here.)

The challenge was set so I came up with:-
Image
Forms.tar.gz
(322.11 KiB) Downloaded 158 times
Now to the 'OOPS' part: -

I decided to try and make the 'Button' used into a class and with the help of my friend Matt came up with this. Once you get the hang of this it is extremely powerful as you are able to add Properties to the IDE. You can see 'EnterColor' and 'FlashColor', amongst others, in the image below. The Form below has been created entirely in the IDE and only 3 lines of code, outside ButtonPlus.class is used.
Image
ButtonPlus.tar.gz
(347.75 KiB) Downloaded 145 times
I have used 'ButtonPlus' in Remember in Project Showcase. if you want to try it just 'Import' the class into a new 'Graphical Application' and run the program. You will then find 'ButtonPlus' available in the Form toolbox, see image above. The 'ButtonPlus' is designed to work like RadioButtons. Click one and the others reset.

User avatar
sjsepan
Posts: 62
Joined: Saturday 12th October 2019 10:11pm
Location: Leeper, PA, USA
Contact:

Re: A Dirty Guide to OOP #1

Post by sjsepan » Tuesday 28th January 2020 1:12pm

BTW, the syntax you may need probably looks like...
Thanks Steve, I have now updated the post with this syntax.
I tried to download your project but it is missing the library: GambasAppUtilityLib ...which I think you will find in your:
/usr/lib/Gambas3/stephensepan directory
That project is also out on github (https://github.com/ssepan2/GambasAppUtilityLib); I've not uploaded the latest versions to the GambasFarm because I was getting errors uploading library projects.

Steve S

Post Reply