Intro to OO

BlitzMax Forums/BlitzMax Tutorials/Intro to OO

marksibly(Posted 2004) [#1]
Here's something I knocked up a while back and plan to 'extend' (hur, hur) one day:


Crash course in OO programming for Blitz coders

Welcome to a crash course in OO programming for Blitz coders!

OO stands for 'Object Oriented'. OO is a programming paradigm or 'way of thinking' that has taken on almost mythical proportions over the years yet, somewhat unusually for the world of computing, is actually quite useful!

*Disclaimer* - OO coding means different things to different people. This article is strongly swayed by my own experience with OO coding and what I like and find useful about it. The actual 'meaning' of OO is almost as fun to argue about as whether the universe is infinite or not.

First up, OO coding has spawned a bunch of initially confusing but actually quite sensible buzzwords, including such gems as:

Inheritance,
Multiple Inheritance,
Polymorphism,
Virtual,
Abstract,
Concrete,
Type,
Class,
Object,
Instance,
Base Class,
Derived Class,
Super Class,
Sub Class,

Scary stuff!

Well, not really. Never forget that behind the scenes its all just a bunch of bits and bytes and Ifs and Adds and Gotos.

As always, its easier than the guys who get paid megabucks to do it would have us think.


Part 1 : Inheritance.
--------------------------

Inheritance is a way of 'composing' objects from other objects.

This is already possible in procedural Blitz. Consider the following code...

Type Part1
Field x,y,z
End Type

Type Part2
Field p1.Part1
Field p,q,r
End Type

Function UpdatePart1( t:Part1 )
'Do something clever with a Part1.
End Function

This is one way of composing objects. Part2 'contains' a Part1, via the p1 field.

If you've got a Part2 object and want to call UpdatePart1(), you can grab the p1 field and use it in the function call, for example:

Local t:Part2=New Part2
t.p1=New Part1 'need to create it!
UpdatePart1 t.p1

OO inheritance provides a different (not always better!) way to do this:

Type Base
Field x,y,z
End Type

Type Derived Extends Base
Field p,q,r
End Type

Function UpdateBase( t:Base )
'Do something clever with a Base object.
End Function

Ok, we've already hit our first OO buzzwords - Base and Derived. These are not special keywords, they are just names I chose for these types. Bear with me and hopefully you'll get some idea of why I chose them.

First up, The Base type is just the same as the procedural Part1 type - no problem there.

The Derived type, however, introduces us to the Extends keyword, which is used to indicate inheritance.

Extends always appears after 'Type whatever', and means that the type you are describing extends a different, existing type. So the type you are describing is composed not only of the fields and stuff you declare, but also the fields and stuff of the type you are extending!

This means that the Derived type above actually has the additional fields x,y,z - these have been 'inherited' from the Base type. It is then perfectly legal for you to code up something like:

Local t:Derived=New Derived

t.x=100 'x inherited from Base - thanks Base, you rock!

Another way to think of it is the Derived type 'is a' Base type, but with some extra stuff. In fact, in OO terminology 'IsA' is often used to indicate inheritance or inheritance-like properties.

Now it gets a bit interesting. Since our Derived type 'is a' Base type, its perfectly legal to use it where ever a Base type is expected - for example, the UpdateBase() function:

Local MyDerived:Derived=New Derived 'fine...
UpdateBase MyDerived 'What?!?

Initially, this looks *wrong* - 'Type mismatch error' looms! But in OO code its fine. Since Derived Extends Base, its OK to use a Derived where ever a Base is expected - even in assignments:

Local MyBase:Base=New Derived 'Okay, because Derived Extends (or 'Is a') Base.

In addition, there could be many types extending Base, and each of these could be used interchangeably with Base.

Hopefully the terms 'Base' and 'Derived' now mean something. The Derived type is derived from, or 'comes from', the Base type.

Remember that Base and Derived are just names I chose for this example. In real world coding, you'd use useful names, for example:

Type Actor
Field x,y,z
End Type

Type Player Extends Actor
Field lives,score
End Type

But still, you can think of Actor as the base type, and Player as the derived type - a Player is derived from an Actor.

Its also perfectly legal (and very useful!) to extend extended types, for example:

Type Actor
Field x,y,z
End Type

Type Player Extends Actor
Field lives,score
End Type

Type Enemy Extends Actor
Field mood
End Type

Type Troll Extends Enemy
Field grudge_quotient
End Type

Type Ghoul Extends Enemy
Field lust_for_life
End Type

So, if you've got a function like UpdateActor( t:Actor ), you can pass it any of the above types as they are all ultimately derived from Actor.

Similarly, a RethinkMood( t:Enemy ) function could be used with any of the bad guys (Enemy, Troll or Ghoul) - but not with players, as Player is not derived from Enemy.

This is what is known as a 'class hierarchy' - a 'tree' of types.




Hotcakes(Posted 2004) [#2]
A good start, Mark. Thanks!


Caff(Posted 2004) [#3]
Nice tutorial so far.

Anyone who's interested in working seriously with the 'object orientated' mindset should have a look at UML (Unified Modelling Language) - this is a great way of expressing a system (game, application, business-wide system) in a graphical form. It helped me to move from procedural/BASIC languages to OO. I don't know how helpful it is for games developers, but might be worth a look.

If you are trying to design something complex, it can be a good way to show how the objects in a system relate to one another, how everything interacts, what extends from what and so on.

It's widely used throughout the IT industry (for analysis and design) and looks great on a CV if you understand it!

Have a look at this -> http://odl-skopje.etf.ukim.edu.mk/uml-help/


Amon_old(Posted 2004) [#4]
Nice Tutorial Mark. Just what i was looking for.

Thanks :)

Checking out your link aswell caff. :)


flying willy(Posted 2004) [#5]
Interesting tutorial, and a good explanation.

But why have base anyway? isn't it more to remember when you extend? To me, it seems as though I have to work harder and leads to sphagetti coding practise.

In my opinion:

Type Player
Field x,y,z,score,health
End Type

Would be so much simpler and to the point? Is OO overkill?


Tibit(Posted 2004) [#6]
It is overkill if you use 5 Variables ;)

But if you have a type let's say Fighter which has alot of fields required for fighters, everything from x,y,z to Sheilds,Textures and so on. Then you want a Carrier which has the same base structure but has Turrets,Wrapengines, crew, Carrier_Bitmaps and tons of extra stuff. In this case you have two options if you don't want to use OO: 1. Create a Carriers type and a Fighter type both which happen to have alot of fields in common including alot of indentical methods. 2. Use the Carrier-Type for both fighters and carriers. This will lead to alot of unused fields and unused methods for each fighter you create, that is unneccecery use of memory. If you where to extend the fighter into a Carrier. Then the carriers would have everything the fighter has + the extra stuff needed. The fighter would still be just a fighter. That's basically it. It simplifies programming some in certain cases. In big project it can really sort things up! This got a little messy but I hope you got the point =)
//Wave~


PetBom(Posted 2004) [#7]
Thanks Mark!

@Caff
I agree that UML is a great metod to do OO design and modelling. But the whole UML set is massive so I'd advice anyone intrested in exploring it to pick the bits that are useful to your specific task. I use UML professionally, and I've been in to many projects where we have 'modelled ourselves to death'. (Actually I'm in one right now...) In my expereience the _really_ useful parts of UML for OO projects is Class (Static-Model) Diagrams and State Diagrams.

For personal projects I use ArgoUML, a nice java-based freeware tool, for UML modelling. Professionally I use Visio (Which sucks for UML!) or XDE (Which is ok but really expensive)

Sorry for drifting off topic!

//PetBom


Tibit(Posted 2004) [#8]
Is there a way to inherit from two types? It this a good feature request, have it been planned? Is it impossible to implend perhaps? I know Java don't have it.

Type House
...
End Type

Type Boat
...
End Type

Type HouseBoat Extends House and Boat
End type


Warren(Posted 2004) [#9]
No, I don't think Max supports multiple inheritence. Most modern languages are doing away with it. C# has dropped it, for example. Too complicated and just not necessary in the majority of the cases where it gets (ab)used.


bradford6(Posted 2004) [#10]
Great Tutorial Mark.
here is a little snippet that uses SELF and Methods.

let me know if it makes sense (or is good/bad form)...

Rem
bbdoc: Blitz UDT OOP (User Defined Type) demo
returns: type stuff
about: this is a type example

End Rem

GH = 800 ; GW = 600 ; GD = 0
Graphics( GH,GH,GD )

Type blitzmonkey
	Field xpos,ypos			' screen position
	Field name:String 
	
	Method define(n$ = "DEFAULT",xp = 200,yp = 200)
		Self.xpos = xp
		Self.ypos = yp
		Self.name = n$
	End Method
	
	
	Method display()
		DrawText("My Name is "+Self.name+". I am at "+Self.xpos+" , "+Self.ypos,Self.xpos,Self.ypos)
	End Method

	Function Create:blitzmonkey()
		Return New blitzmonkey
	End Function
End Type

Local b:blitzmonkey = blitzmonkey.Create()

b.define("harry",20,20)
b.display()

b.define("bob",100,100)
b.display()

b.define()
b.display()

Flip
WaitMouse



BlitzSupport(Posted 2004) [#11]
Bill: looks fine to me. Not sure if you know, but you don't need to use Self. when accessing the type fields from a method -- you could just refer directly to xpos, ypos and name. Personally, I only use Self to refer to the actual calling object directly.


ImaginaryHuman(Posted 2004) [#12]
Ahh, finally some clarity. Looking forward to the next installment.


bradford6(Posted 2004) [#13]
thanks James,

Could you add a little more clarity to what you said.

I only use Self to refer to the actual calling object directly.



in what situation(s) could I use the self. in the following code

same code as above with "Self." removed
Rem
bbdoc: Blitz UDT OOP (User Defined Type) demo
returns: type stuff
about: this is a type example

End Rem

GH = 800 ; GW = 600 ; GD = 0
Graphics( GH,GH,GD )

Type blitzmonkey
	Field xpos,ypos			' screen position
	Field name:String 
	
	Method define(n$ = "DEFAULT",xp = 200,yp = 200)
		xpos = xp
		ypos = yp
		name = n$
	End Method
	
	
	Method display()
		DrawText("My Name is "+name+". I am at "+xpos+" , "+ypos,xpos,ypos)
	End Method

	Function Create:blitzmonkey()
		Return New blitzmonkey
	End Function
End Type

Local b:blitzmonkey = blitzmonkey.Create()

b.define("harry",20,20)
b.display()

b.define("bob",100,100)
b.display()

b.define()
b.display()


Flip
WaitMouse



Jeroen(Posted 2004) [#14]
BlitzSupport: aaaah, thanks. I was wondering for some days, why Self is used, and sometimes not. You cleared it up. Nice one for the docs!


BlitzSupport(Posted 2004) [#15]
Well, this is probably a really bad example, but maybe you'll see what I mean when I say that I use Self to refer to the calling object (as opposed to using Self to then access the object's fields) -- in this case, passing the calling object to a function, which might otherwise work on any Rocket object passed to it:

Type Rocket

	Field fuel = 10

	Method FuelCheck ()
		If fuel < 1 Then RefuelRocket (Self) Else fuel = fuel - 1
	End Method
	
	Function RefuelRocket (r:Rocket)
		Print "Refueling!"
		r.fuel = r.fuel + 10
	End Function

End Type

r:Rocket = New Rocket

Print "Fuel: " + r.fuel

For a = 1 To 20

	r.FuelCheck
	Print "Fuel: " + r.fuel

	r.FuelCheck
	Print "Fuel: " + r.fuel

Next


I also use it to remove objects from lists when they're no longer needed (and to add them to lists) -- see the GravityItem.Destroy method in rockout.bmx, which is inherited by all of the other types that extend it (hence I can call Destroy from a Shot object when it goes offscreen, for example). That does it the OO way, but you could do "ListRemove GravityItemList, Self" instead (wasn't in earlier betas).

(I am hardly the world's acknowledged expert on OO coding, though, so this is just my way of doing things, not 'the' correct way!)


FlameDuck(Posted 2004) [#16]
In my expereience the _really_ useful parts of UML for OO projects is Class (Static-Model) Diagrams and State Diagrams.
And deployment diagrams for distributed/cross platform systems.

For personal projects I use ArgoUML, a nice java-based freeware tool, for UML modelling. Professionally I use Visio (Which sucks for UML!) or XDE (Which is ok but really expensive)
In my experience ArgoUML is okay, if a bit resource heavy. Personally I use [a https://www.esm.jp/jude-web/en/index.html]JUDE[/quote], which apart from being rediculously small, also doubles as a CASE and reverse engineering tool (at least for Java).

I agree Visio is useless.
(I am hardly the world's acknowledged expert on OO coding, though, so this is just my way of doing things, not 'the' correct way!)
And yet by some strange coincidence, it just happens to be the 'correct' way aswell. :o>


eni(Posted 2004) [#17]
An additional thing with Self that my quick skimming didn't see (I'm busy) is it lets you access fields of a type that have the same name.

Eg

Type Rocket

	Field fuel = 10

	Method setFuel(fuel)
		Self.fuel=fuel
	End Method

End Type


Or in bradford6's example, the method define could be rewritten as

Method define(name$ = "DEFAULT",xpos = 200,ypos = 200)
	Self.xpos = xpos
	Self.ypos = ypos
	Self.name = name
End Method


Greatly aids in clarity of constructors and simple assignment in my opinion.


Jeroen(Posted 2004) [#18]
Enidox, I believe with methods you don't have to use Self that way, you can leave it out. "fuel" is known in the method.


eni(Posted 2004) [#19]
I believe you misunderstand me. My point was that you can use Self to access the public variables of a type which are otherwise inaccessible if you have a local variable of a method with the same name.

It aids clarity of assignment as it prevents you having to declare parameter names such as fuel_new or xp in bradford6's example holding the new value for xpos where using xpos as the parameter and accessing the public variable xpos by using Self.xpos would suffice.


Sweenie(Posted 2004) [#20]
[Oops, you beat me too it Enidox] :)

But if you look at Enidox example the setFuel parameter has the same name as the type field.
I guess the fuel parameter "overrides" the default fuel field in the type and therefore you'll need to use self to clarify for the method which one you refer to.


bradford6(Posted 2004) [#21]
In this example, leaving out the "self" from the self.fuel is fine:

GH = 800 ; GW = 600 ; GD = 0 ; Graphics( GH,GH,GD )


Type rocket

	Field fuel:Float

	Method addFuel(amount = 10.0)
		 fuel = fuel + amount ' SELF omitted ********
	  	
	End Method
	
	Method BurnFuel(amount = 1)
		If fuel > 0.0
			fuel = fuel - amount
		endif
	End method
	
	Method showFuel()
		SetColor 200,200,255
		DrawText "<SPACE> to add fuel----->FUEL: "+Int(fuel),100,100
		SetColor 0,255,0
		DrawRect 10,10,Int(fuel),15 
	End method
End Type

myship:rocket = New rocket

myship.addfuel(100.0) ' adds the default which is 10

repeat
cls
	If KeyHit(KEY_SPACE)
		myship.addfuel(20) ' tells 'addfuel' to add 20 units of fuel
	endif
	
	myship.burnfuel()
	myship.showfuel()
	
	
flip
Delay 50
Until KeyHit(KEY_ESCAPE)



eni(Posted 2004) [#22]
Yes, self can be omitted in normal cases like that.

However look at what this example does:
Type Rocket

	Field fuel = 10

	Method setFuel(fuel)
		Self.fuel=fuel ' (the fuel field of this instance of Rocket) = (the fuel parameter passed to the setFuel method)
	End Method

End Type



Kanati(Posted 2004) [#23]
Ack... That's a good way to confuse the issue. Change that parameter variable. :)


bradford6(Posted 2004) [#24]
It is much more readable if you change the parameter variable to something other than the type-field variable.

why would you want to use the same variable name? am i missing something?

Method setFuel(gas)
		fuel=gas  
End Method


these do the same thing

Method setFuel(fuel)
	Self.fuel = fuel  
End Method



bradford6(Posted 2004) [#25]
here is some more oop stuff.



Global GH = 640 ; Global GW = 480 ; Global GD = 0
Graphics( GH,GH,GD )

Type actor
	Field px#,py#
	Field x# = GW/2
	Field y# = GH/2
	Field color
	Field name:String
	Field life
	Field entity
	'
	Field angle#,velocity#
	Field angular_velocity# = 0.00
	Field friction# = 0.99
	
	Method applyImpulse#(angular#,thrust#=0.0)
		angular_velocity# = angular_velocity# + angular#
		velocity# = velocity# + thrust#
			
	
	End Method
	
	Method update()
			angular_velocity# = angular_velocity# * friction#
			velocity# = velocity# * friction
			
			x=x+(velocity*Cos(angle-90))
			y=y+(velocity*Sin(angle-90))
						
			angle#:+angular_velocity
				If angle#<0.0 Then angle#=360.0
				If angle#>360.0 Then angle#= 0.00
			SetRotation(angle)
		
		
	End Method
	
	Method show()
		
		DrawImage(entity,x,y)
		SetRotation(0)
		DrawText name,x,y
	End Method

End Type 


Type player Extends actor
	Field inventory:String[]
End Type

Type enemy Extends actor
	Field anger
	
End Type

me:player = New player
me.name = "Bob"

me.entity = CreateImage(64,64,DYNAMICIMAGE)
	SetColor 255,255,0
	SetLineWidth 3
	DrawLine 0,64,32,0
	DrawLine 32,0,64,64
	GrabImage me.entity,0,0
	MidHandleImage(me.entity)


Repeat
	Cls
		
	
	If KeyDown(KEY_LEFT) Then me.applyImpulse(-0.1)
	If KeyDown(KEY_RIGHT) Then me.applyImpulse(0.1)
	If KeyDown(KEY_UP) Then me.applyImpulse(0,0.04)
	me.update()
	me.show()
	
	Flip
Until KeyHit(KEY_ESCAPE)



MRaven(Posted 2004) [#26]
One question to base and derived types. Let's say I create a base type called "window" with alle the fields I need (x,y,image etc.), now I extend this by the type "button". Is it possible to create a "button" for a specific window type? Let's say I created score:window and now I want to create a button type for exactly THAT base object.

How can I do that? :)


bradford6(Posted 2004) [#27]
This may be WAAYYY off, but I am not sure wat you are trying to do?
do you want to have a SPECIFIC button type for each window type?

this creates a window type with a list of buttons in each instance of window. you could make a field in the button type to identify the type of window it can "work" in




MRaven(Posted 2004) [#28]
Thanks Bill, this IS a good idea. I played around with types and methods and stuff and have to say this improves Blitz big time. Well done, Mark.