Coding Best Practices

Hi Codeans,

(Sorry, this turned into a bit of a monster post!)

Having just tried stuff out in Codea for a couple of months, looking for the killer idea to actually get a concrete project off the ground :wink: a friend of mine just proposed a great game idea, so we are full steam ahead!

I am working on the UI side inwards, e.g. Intro screens, graphics etc. ā†’ core game engine, AI etc., and he is working from the other endā€¦

I have now setup the entire infrastructure and completed the Intro screen and am moving on from there. Having worked in object-oriented programming for more years than I care to admit to, Iā€™ve been trying to apply OO principles and coding best practice in the Codea/Lua world.

I donā€™t expect a lot of this stuff is new to many of you, but Iā€™ve found what I think are some quite nice ways of doing certain things, and Iā€™d be interested in other peoples take on this as Iā€™m new to Lua/Codea, maybe this would ultimately make a good Wiki pageā€¦

Caveat: Some of this stuff is just personal preference, especially code style opinions, so donā€™t beat me too hard ;))
Caveat2: Code examples are pseudo-code-ish, they may not be exactly correct as Iā€™m not writing this on the iPad.

Anyway, to kick off:

Code Style

For ease of readability for people who donā€™t know your code, itā€™s a good idea to make bracketing and indenting clear, e.g. I always use brackets in ā€˜ifā€™ statements and arithmetic statements, and indent and break out long statements e.g.:

if(obj ~= nil) then
end

result=((var1/100)*var2) + ((var3-var4)/var5)

tween(4, obj,
    { width = WIDTH/2, height = HEIGHT/2 },
    tween.easing.linear,
    function ()
        -- do stuff
    end
)

```

If you download the free version of Intellij IDEA (a Java development environment for Windows and Mac) then you can get a Lua plug-in. You can do a bit of prototyping for non-Codea specific stuff (you need Lua installed) but I mainly use it for code reformatting. Using AirCode I copy-paste my code into it, reformat (Ctrl-Alt-L) then copy back. sorts out all indenting and spacing which is nice ;).

Constants

Avoid 'magic numbers/strings' in code, they are EVIL! Anywhere you find yourself using the same number or string for the same thing then make it a constant. The easiest way to do this is to declare a 'namespace' Lua file as, e.g.:
Constants = {}
Constants.HALFHEIGHT = HEIGHT/2
Constants.GAMENAME = "That's It I'm Rich"

```

You can create sets of constants in this way depending on requirements...

However, as we know, anyone can change these constants on the fly by just reassigning to them:
Constants.GAMENAME = "This Sucks"

```


...so we lead into:

Namespaces and Classes and Data 'Protection'

This is the biggest area of interest in my view, and in the forum, discussions about 'what the heck are Classes and why do I care?' are fairly frequent...My take is:

Any object you need which holds specific data AND requires specific operations on that data AND you need to create MORE THAN ONE of those objects, all with their own data 'state' SHOULD be a class

Any object you need only ONE of should be a table 'namespace':
Constants = {}

local var = nil

Constants.HALFHEIGHT = HEIGHT/2
Constants.GAMENAME = "That's It I'm Rich"
Constants.doThing = function(thing) var = thing end

```

This is a global object, has data, has functions and can have local variables which are not accessible from outside, as above. This is looking nice, but there is a slight problem. If you want (as I did!) to have a tweening utility method in my global class, I couldn't tween the local variables, because they don't belong to a table. If they DO belong to the main table then they are directly accessible externally...

So to protect data, and to organise it internally, you can create an internal 'implementation' table object, e.g.:
Utility = {}

local impl = {}
impl.var = 255
impl.tweenId = nil

Utility.tweenMeUp = function(time, callback)
    impl.tweenId = tween(time, impl, {var=0}, tween.easing.linear, callback)
end

Utility.stop = function()
    if(impl.tweenId ~= nil) then
        tween.stop(impl.tweenId)
    end
end

```

Your object now has internal data which it can manipulate and hold state in the 'impl' object, but none of these values are externally accessible... To make them accessible, just provide functions like set/getTweenId() to set or return the 'Impl' variable. This way YOU control what is exposed and what is not...

You can do the same thing in classes also if you want per-class constants (similar to static variables in other languages).

One caveat is that if one of your impl variables is itself a table and you return that from a getter function then that can be changed underneath you...

You can also implement the classic Singleton pattern if you want to enforce the creation of only one instance of a class, shout if you want details, or Google ;).

The above may seem like labouring the point a bit BUT I had nightmares debugging stuff until I protected my data in this way as variables were changing underneath me in tweens etc., and I couldn't tell where things were being accessed...

Making an effort to encapsulate your data in classes and global tables really leads you towards better design decisions and more elegant code...

Code Duplication

Avoid like the plague! Any time you find yourself copying and pasting code, make it a utility function in a global object!

Object Creation Minimisation

I'm on less certain ground here, but as a general good practice, minimize the number of objects you create, e.g. store an image once using readImage(), check for object creation in loops that could be moved outside etc.

Its easy to write a Cache class that does on-demand loading and caching for different objects so you only ever create a named 'thing' once...Shout if you want the implementation...

Be interested in all your thoughts,

Brookesi

Thanks for this post, I plan to start implimenting best practices more frequently, because when my project code increases it gets so sloppy sometimes I cant even figure out whatā€™s going on, and have had to restart more than once because of it. The Intellij + AirCode reformat trick is really cool.

Oh God yesā€¦ Donā€™t Repeat Yourself and No Magic Numbers are big ā€œdontā€™sā€. I have to fight that all the time. :frowning:

I recently refactored over 1000 lines of code that ended up being distilled down to 8 lines. The previous programmer had written two different functions, each of which handled roughly 50 cases each - one for positive and one for negative numbers.

I distilled the whole thing down to one function that basically just copied the input values to the output table and dealt with the 3 special cases using a select/case block.

Itā€™s one thing to miss a chance to optimize and leave a duplicated line in an if/then blockā€¦ but when you copy and paste the same code 100 times, changing a single letter in each paste, you probably missed a chance to do clean something upā€¦

Speaking of: I am a big fan of switch or case statements. Lua doesnā€™t actually have a switch syntax, but you can emulate it with a table filled with function assignments. I think the syntax would look like this (someone correct me if Iā€™m doing this wrong):

switch["case1"]=function1
switch["case2"]=function2

...

switch[testvalue]()

speaking of cleaning up code: as I work on Tanks!, Iā€™m adding more controls to the screen (angle, power, fire button, menu button, etc)

I started out with syntax that added each button in the setup() function, then called each buttonā€™s touched event in the global touched() function, then called each buttonā€™s draw() method in the draw() functionā€¦ you get the idea.

Thereā€™s another great place to use tables. Adding a new control becomes as simple as adding a new value to the table.

setup()
... stuff ...

  fireButton=new ButtonControl()
  ... set up values ...  
  controls["fireButton"]=fireButton
end

touched(touch)
    for k,v in pairs(controls) do
        v:touched(touch)
    end
end

draw()
    for k,v in pairs(controls) do
       v:draw()
    end
end

I am thinking of writing a little global function that executes a single method call on all of the objects in a table, so the syntax could be reduced to this:
tableExec(controls,ā€œdrawā€)

Itā€™s not no magic numbers, itā€™s consolidate your magic numbers in one place. The best thing about this is if your magic numbers are for the right things, you can play with them to tune your playability easily.

The term ā€œmagic numbersā€ really means ā€œsome arbitrary data value that affects the programā€™s execution.ā€ Once you turn that value into a constant by defining it at the top of your module, itā€™s no longer a magic number any more. Itā€™s now code.

A constant like END_OF_FILE makes a lot more sense than the literal ā€œ26ā€ somewhere in your code. Itā€™s not so much about consolidation as it is giving a contextual meaning to something.

Although, yes, consolidating the values is helpful, the term ā€œmagic numbersā€ specifically refers to numbers that have a special meaning and affect how your program works.