ID:1720215
 
Introduction to Action Orientated Programming In DM

Full Demo Here: http://www.byond.com/developer/Zecronious/ ActionOrientationExample

I would like to introduce to you all what I believe to be a new style of programming in DM. This style has many advantages over the built in way of programming. As a lot of you will know DM does not support multiple inheritance. This is my work around.

To demonstrate I have written a comparison below of a typical approach and an action orientated AO approach.

----------------------------------------------------------------

The Problem

You are tasked to build two items. One is TNT and one is a stopwatch. The TNT is special because it will explode when it's time limit has expired. The stopwatch is special because it will beep when it's time has expired.

----------------------------------------------------------------

A Typical Approach

First creating an object that keeps track of time.
obj/Timed_Object

var/running = 0
var/time = 0
var/max_time = 10

proc
start()
running = 1
keepRunning()

stop()
running = 0

keepRunning()
spawn()
while(running)

if(time >= max_time)
finished()
return

time += 1
sleep(10)


finished()

getTime()
return time

getMaxTime()
return max_time

Making the stopwatch child of Timed_Object.
obj/Timed_Object/Stopwatch

getTime() // Counts downward
return max_time - time

finished()
world << "BEEP BEEP"

Making the TNT child of Timed_Object.
obj/Timed_Object/TNT

finished()
world << "BOOOOM"

What's Wrong With This Approach?

TNT and stopwatches are very different items. Explosives will share different functionality to a stopwatch. The only way to now group explosives together is to extend our object tree even further creating a horribly large and verbose object tree.

Do you really want to type this out? Of course you don't. It's ridiculous.
var/obj/Timed_Object/Explosive/TNT/myTNT = new/obj/Timed_Object/Explosive/TNT()

And what if the explosive is a remotely detonated explosive? Now it has to be a Timed_Object? That would be crazy.

----------------------------------------------------------------

An Action Orientated Approach

We start by implementing these methods. Datum is the parent to all objects. So we give the method to all objects.

It has error handling that I've made.
datum
proc/performAction(ACTION)
actionError(ACTION)

proc/actionError(ACTION)
world.log << "Runtime Error:\n\tsrc:[src] [src.type]\n\tusr:[usr] [usr.type]\n\taction:[ACTION]\nThis action was not found to exist in src."

Creating an independent Timer object.
obj/Timer

var/running = 0
var/time = 0
var/max_time = 10

var/atom/parent

New(PARENT) // Keep track of the owner of this object
parent = PARENT

proc
start()
running = 1
keepRunning()

stop()
running = 0

keepRunning()
spawn()
while(running)

if(time >= max_time)
parent.performAction("timer finished") // Tell the parent it's done.
return

time += 1
sleep(10)

getTime()
return time

getMaxTime()
return max_time

Creating an instance of the Timer object inside our stopwatch.
obj/Stopwatch

var/obj/Timer/timer

New()
timer = new/obj/Timer(src)

proc
soundAlarm()
world << "BEEP BEEP"

performAction(ACTION)
switch(ACTION)
if("timer finished") soundAlarm()
else
..() // Error

Creating an instance of the Timer object inside our TNT.
obj/TNT

var/obj/Timer/timer

New()
timer = new/obj/Timer(src)

proc
explode()
world << "BOOM"

performAction(ACTION)
switch(ACTION)
if("timer finished") explode()
else
..() // Error

What's Better About This Approach?

Now we have don't have to build such a huge tree of objects. We have our Timer objects for remembering the time, our explosives for exploding and our stopwatch for counting down for us. Everything does what it's supposed to do.

----------------------------------------------------------------
Make a proc called StartTime() to start the timer, for explosives that are have a time set.
You may want to look at this library: http://www.byond.com/developer/Stephen001/EventScheduling

In response to ANiChowy
Same thing I thought after seeing this topic.
You also can just do

var/obj/Timed_Object/Explosive/TNT/myTNT = new


This seems to be an okay approach, it even solves a few problems I used to have with the standard inheritance. Good job.
Why does no one ever use parent_type to cut down lengthy new statements. Like maybe you can justify having Explosive/TNT but Explosive should have a parent_type of Timed_Object.
I think parent_type should be used more often at the top of the hierarchy moreso than at the bottom. The reason for this is because as the hierarchy tree branches off, to keep things consistent, you would need to use parent_type for all of your children. It would make more sense to use traditional type branching with / if several child types are being created.
Lugia319 wrote:
Why does no one ever use parent_type to cut down lengthy new statements.
Because it annoyingly obscures the inheritance structure, reduces code clarity, introduces unnecessary path collisions, misuses the initial slash to denote an absolute path, and misleads new programmers.

It's only useful in very specific cases.
For example, i'm using it in a graphics library for a purely-graphical class with parent_type = atom/movable: this helps since only one line will need to be changed if/when the related parts of the type tree are improved in the future (ie if/when BYOND becomes open-source), and it can quite easily be swapped for /image with compile options in certain cases (eg to benchmark performance) with minimal change to the code overall (using an absolute type would require changing every line in which it's mentioned).

However, that need only exists because we need our code to interface with the BYOND hardcode, which is non-modifiable (for now) and saddles us with an... unoptimal inheritance structure (a downright awful one when it comes to graphical objects).

If your type paths are too long, consider using shorter names. For example, thingThatHappensToDetonateViolently/ theKindThatDoesSoAfterASetTime ---> explosive/timed, and even shorter for things intended to be at the root of the type tree (for example, /graphicalElement --> /gem)

Wild Bill Bartok wrote:
Because it annoyingly obscures the inheritance structure, reduces code clarity, introduces unnecessary path collisions, misuses the initial slash to denote an absolute path, and misleads new programmers.

None of this is an actual issue if you know the code you wrote.

Lugia319 wrote:
Why does no one ever use parent_type to cut down lengthy new statements.

I do. I find it pretty handy.