Tutorial: How to change several Functions functions at once

The place for any Hollywood tutorials
Post Reply
Bugala
Posts: 690
Joined: Sun Feb 14, 2010 8:11 pm

Tutorial: How to change several Functions functions at once

Post by Bugala » Sun Jul 08, 2018 3:00 pm

This is continuation to this tutorial: https://forums.hollywood-mal.com/viewto ... =11&t=1936
And I originally meant them to be just one tutorial, but after reading through, I came to conclusion that they are different enough that while first might not interest some, some might be interested in the second part.

However, you might benefit from reading the first one first since I am continuing directly from that one (as i said, i originally wrote this all in row as one tutorial)


Next I am going to show two different ways to solve the problem of being in need to change several buttons functions at once. To start, I will first complete the original button example into one where functions can be replaced:

Code: Select all

EnableLayers

Function button1func()
	button1realfunc()
EndFunction

Function button1realfunc()
	DebugPrint("button 1 pressed")
EndFunction

Function button2func()
	button2realfunc()
EndFunction


Function button2realfunc()
	DebugPrint("button 2 pressed")
EndFunction

Function button3func()
	button3realfunc()
EndFunction

Function button3realfunc()
	DebugPrint("button 3 pressed")
EndFunction

SetFillStyle(#FILLCOLOR)
Box(50, 100, 100, 100, #RED, {name="button1box"})
Box(200, 100, 100, 100, #GREEN, {name="button2box"})
Box(350, 100, 100, 100, #BLUE, {name="button3box"})
 
MakeButton(1, #LAYERBUTTON, "button1box", True, False, {OnMouseDown = button1func})
MakeButton(2, #LAYERBUTTON, "button2box", True, False, {OnMouseDown = button2func})
MakeButton(3, #LAYERBUTTON, "button3box", True, False, {OnMouseDown = button3func})

Repeat
	WaitEvent()
Forever
This one now works exactly like the first example did, except there are more lines and this now has the flexibility of being able to change the functions associated.

I am first going to show you a way to show several functions at once using STATE variable (this variable can be called anything, but state or something similar is quite usual naming practice for it).

Code: Select all

EnableLayers

Function button1func()
	Switch state
	   Case "number":
		button1realfuncnumber()
	   Case "color":
		button1realfunccolor()
	   Case "mix":
		button1realfuncnumber()
	EndSwitch	
EndFunction

Function button1realfuncnumber()
	DebugPrint("button 1 pressed")
EndFunction

Function button1realfunccolor()
	DebugPrint("Red Button pressed")
EndFunction

Function button2func()
	Switch state
	   Case "number":
		button2realfuncnumber()
	   Case "color":
		button2realfunccolor()
	   Case "mix":
		button2realfunccolor()
	EndSwitch
EndFunction


Function button2realfuncnumber()
	DebugPrint("button 2 pressed")
EndFunction

Function button2realfunccolor()
	DebugPrint("Green Button pressed")
EndFunction

Function button3func()
	Switch state
	   Case "number":
		button3realfuncnumber()
	   Case "color":
		button3realfunccolor()
	   Case "mix":
		button3realfuncnumber()
	EndSwitch
EndFunction

Function button3realfuncnumber()
	DebugPrint("button 3 pressed")
EndFunction

Function button3realfunccolor()
	DebugPrint("Blue Button pressed")
EndFunction


SetFillStyle(#FILLCOLOR)
Box(50, 100, 100, 100, #RED, {name="button1box"})
Box(200, 100, 100, 100, #GREEN, {name="button2box"})
Box(350, 100, 100, 100, #BLUE, {name="button3box"})
 
MakeButton(1, #LAYERBUTTON, "button1box", True, False, {OnMouseDown = button1func})
MakeButton(2, #LAYERBUTTON, "button2box", True, False, {OnMouseDown = button2func})
MakeButton(3, #LAYERBUTTON, "button3box", True, False, {OnMouseDown = button3func})

state="color"

Repeat
	WaitEvent()
Forever
I did following changes to the previous code.

First of all i added "Button1realfunccolor" and changed "Button1realfunc" to "Button1realfuncnumber" for each button, so they will have two different functions that can be used and names tell something about their behavior. One debugprints number of button, while other prints color of button.

I also added this variable called "state". There are now three different states supported in this program "state=number", "state=color" and "state=mix".

right before repeat-forever loop this state is chosen, if you replace this word with one of the other two, buttons behavior will change.

last change was that i changed the function that is associated with "OnMouseDown"-event. Instead of directly going to another function, i am now using switch command for it to choose which function it should pick. This Switch is used in connection with STATE variable.

Notice that this switch part could have been put to "realbutton1func" instead, in witch case I could have afterward decided to change the switch part on fly. Right now that switch part is cemented on a rock and cant be changed anymore during the program, unless you recreate the button itself, but to keep this example simpler, i decided to do it that way.

This system I used in this one, has its Pros and Cons. One of the good things is that it is very easy to see what all one button can do. You just go and check the associated buttons function, and you can see all them options in one switch place.
It is also very easy to change the behavior of even several buttons at once, since all you have to do is change the "STATE" variable in to something else.

The Con part however is, that you actually have to remember what all those "STATE"s are doing, since it is not obvious what happens when your state changes, unless name is very clear, like in this case the "number" and "color" are enough telling names that as long as you have any idea of the code, you have very good idea what is going to happen.

But lets suppose you read a code and you suddenly see "mix" as the state. Now what happened now? Only way you can know, is by checking through every button to see which buttons are affected which way.
Similarly, what if some new function became available somewhere completely elsewhere? It is very hard to know, unless you check through every piece of code to find out where this change happened.


Therefore there is also another way to do it that I am showing now:

Code: Select all

EnableLayers

Function button1func()
   button1realfunc()
EndFunction

Function button1realfunc()
EndFunction

Function button1realfuncnumber()
	DebugPrint("button 1 pressed")
EndFunction

Function button1realfunccolor()
	DebugPrint("Red Button pressed")
EndFunction

Function button2func()
	button2realfunc()
EndFunction

Function button2realfunc()
EndFunction

Function button2realfuncnumber()
	DebugPrint("button 2 pressed")
EndFunction

Function button2realfunccolor()
	DebugPrint("Green Button pressed")
EndFunction

Function button3func()
	button3realfunc()
EndFunction

Function button3realfuncnumber()
	DebugPrint("button 3 pressed")
EndFunction

Function button3realfunccolor()
	DebugPrint("Blue Button pressed")
EndFunction

Function ChangeState(state)

Switch state
Case "number":
	button1realfunc = button1realfuncnumber
	button2realfunc = button2realfuncnumber
	button3realfunc = button3realfuncnumber
Case "color"
	button1realfunc = button1realfunccolor
	button2realfunc = button2realfunccolor
	button3realfunc = button3realfunccolor
Case "mix"
	button1realfunc = button1realfuncnumber
	button2realfunc = button2realfunccolor
	button3realfunc = button3realfuncnumber
EndSwitch 

EndFunction


SetFillStyle(#FILLCOLOR)
Box(50, 100, 100, 100, #RED, {name="button1box"})
Box(200, 100, 100, 100, #GREEN, {name="button2box"})
Box(350, 100, 100, 100, #BLUE, {name="button3box"})
 
MakeButton(1, #LAYERBUTTON, "button1box", True, False, {OnMouseDown = button1func})
MakeButton(2, #LAYERBUTTON, "button2box", True, False, {OnMouseDown = button2func})
MakeButton(3, #LAYERBUTTON, "button3box", True, False, {OnMouseDown = button3func})

ChangeState("color")


Repeat
	WaitEvent()
Forever
Differences to the STATE variable version are following:

Buttons now have 4 different functions in total.

1. The function that is associated with OnMouseDown and which is simply referring to realfunc function.
2. RealFunc function which at beginning does nothing.
3. Function for number version.
4. and Function for color version.

Previous version had all but number 2 function. And Function number 1 had switch-case in them, while this is now going back to simple "realnumber1func()" execution.

Instead of having variable called STATE, there is now a function that handles the state changes. You dont necessarily need a ChangeState function, but you could simply have function called "ChangeToColors", which would then be doing same as ChangeState function is doing now, except it wouldnt be using switch-case at all, but simply be doing the part that switch-case is now doing:

Code: Select all

ChangeToColors()
button1realfunc = realnumber1funccolor
button2realfunc = realnumber2funccolor
button3realfunc = realnumber3funccolor
endfunction
or you can also make customised states very easily too, suppose you dont for some reason want red button to react for a while:

Code: Select all

function DisableRedButton()
button1realfunc = DoNothingFunc
endfunction


And if you then at some point come to conlusion you dont need that red disable anymore, simply remove the "DisableRedbutton()" function, and nothing else in your code needs to be changed.


As with using previous STATE variable, this one has its Pros and Cons too.

One Clear Con, in small code like this, is the necessarity to have so many functions. The state version was doable with only 3 functions per button (with having 2 different functions for this button), while this one required you to have 4 functions per button to have same two functions.

Hence if program is small, like this one, STATE variable would be better way to go, since it is not that hard to find out what is happening with each state change.

However, if program gets more complex, then it is most of time much better using this second version, since main problem is the previously mentioned problem that when you change a state from "menu" to "ingame", someone who doesnt know about your code, or even you yourself after revisiting your old code, it is hard to figure out what all does that "state=ingame" affect.
While in this version you know exactly what all are being changed, since you simply look at where the state change (what ever way it is done in practice) is done, and you can instantly see each button that is being changed this way.

This also helps you bug hunting, since if you have disabled some button by using "DoNothingFunc()" which is simply:

Code: Select all

Function DoNothingFunc()
endfunction
Then you know right away that the problem cant be there.
If you however are using STATE to fix things, then you cant rule out the possibility that you forgot to add a line to actually disable that button, or maybe you even misspelled that states name, and hence when you meant if "state=ingame", you wrote "state=ingme" and since state is "ingame" but not "ingme" it is being executed instead of returned as you meant, with func=newfun the good thing is that if i miswrite func=nwfunc, then hollywood tells me there is no such func as nwfunc, and i know instantly where the problem lies if wrong spelling was the problem.

This state=something then return problem can multiply greatly in more complex programs when using STATE variable:

Code: Select all

function MybuttonFunc()
if state="main" then return
if state="ingame" then return
if state="choose" then return
if state is several more thing, then return

if state = "finallyonethaticanuse"
do stuff
if state = "anotherthingtouse"
do this stuff instead
and several more states
do them too.
endfunction
Now in this case the maintenance starts to be quite terrible, especially because just to see what it does requires you to look through 50 lines of code to find out that in this current state, it is simply debugprinting "you could have done this different way", because all that stuff is cluttered in same place.

Also, suppose you suddenly decide to remove "choose" from states. Naturally you can simply remove the "state=choose", and it works fine. However, this "if state=choose then return" is not going anywhere, unless you first remove that line from your code.
It maybe doesnt do anything, but for that specific reason it also shouldnt be there, or otherwise you might start hunting down what does this one state here do, and not find it at all, and regardless, readabiity of your code suffers when there are that same if state=choose line in several other buttons too when it is not even in use.

Compare this to

Code: Select all

Function ChangeToChoose()
button1 = DoNothingFunc
button2 = DoNothingFunc
button3 = Choosefunc
button4 = Choosefunc
button5 = DoNothing Func
endfunction
And then if you decide you dont need ChangeToChoose() state anymore, you simply remove both "ChangToChoose()" function, as well as "Choosefunc()" and there is not a trace of that function left anymore.

So all in all, STATE variable is sometimes more useful and practical that this second system, but often times when things get even a bit more complex, it is already better to change in to this second system instead, but sometimes it is good even to use them both, that for some stuff use STATE variable, while to some use the statechange function style.

Post Reply