ID:2043466
 
Hello there. This is a small lesson on how to design your projects in a more organized manner using modules. Modules are portable batches of source code which - in theory - are self-sustained and can be used for any project developed in the same environment. You can think of libraries that are created here as modules as well.

There are numerous advantages to developing your projects in a modular-oriented way. Each module can be re-used in future projects, and are also easier to maintain because they require little or no dependencies from other sources. They are usually describable by their covers, and are simple enough in that they handle one or more specific tasks.

Let's say you'd like to include a module for your project which keeps track of some images for you, or maybe a group of icons you might need to refer to at a later point. There's endless ways to do this, but to keep it simple, I'll be demonstrating this using datums.

/////////////
// Globals //
/////////////

// The global IconGroup datum which can be referred to by other
// modules.
var/list/icongroups = new()

//////////
// Type //
//////////

icongroup
var/list/icons

New()
..()
icons = new()

proc/AddIcon(s,i)
icons["[s]"] = i

proc/RemoveIcon(s)
icons -= s

proc/GetIcon(s)
if (icons["[s]"] <> null)
return icons["[s]"]
else
return "null"

///////////////
// Functions //
///////////////

proc
icongroups_add(s)
var/icongroup/a = new()
icongroups["[s]"] = a

icongroups_remove(s)
icongroups -= s

icongroups_get(s)
return icongroups["[s]"]


This is just something silly I whipped up for this example. What this module does is define a global list of icongroups which can be communicated with using the three commands "icongroups_add" "icongroups_remove" and "icongroups_get". This module serves to be simple, as you'd be communicating with it using strings to refer to icongroups (a la associative lists).

//////////
// Main //
//////////

client/New()
..()
icongroups_add("thing")
var/icongroup/i = icongroups_get("thing")

i.AddIcon("1",'icon1.dmi')
i.AddIcon("2",'icon2.dmi')

src << i.GetIcon("1")


In the code above (really it's just a "main" procedure that we're using to debug the module) what we're doing is communicating with the global icongroup list by adding a new entry called "thing" and then grabbing it via its name, which is "thing". An icongroup contains the methods "AddIcon" "RemoveIcon" and "GetIcon" which work a lot like the global icongroups list. You don't need to use icons either, but that's besides the point.

Given that this module is self-sustainable, it can be applied to any project and function wherever it's needed. You'd be communicating with it using solely the commands and methods, not you're not really supposed to worry about what goes on inside.

There might be times when a module relies on another module, which is okay given that a lot of the time it's a lot easier or more efficient that way. Module B could be dependent on Module A, but Module A could be self-sufficient on its own. This is known as one-directional dependency or one-to-many relationships. If two modules happen to rely on each other, it makes more sense to combine the two together to form a single module, or a module which contains two "submodules".

I think that's about it. See ya.
if (icons["[s]"] <> null)


You actually use the <> Not operator over the != Not operator?

I'm less interested in the article as I already know the wonders of modular design (it *IS* a good article though), but I'm very interested in your preference of not operator :P
Two immediate things I feel I'd do differently:

    proc/GetIcon(s)
if (icons["[s]"] <> null)
return icons["[s]"]
else
return "null"


would be

proc/GetIcon(s)
return icons[s]


And additionally

proc/icongroups_add(s)
. = new/icongroup
icongroups[s] = .

client/New()
..()
var icongroup/new_icongroup = icongroups_add("thing")
// other stuff here...
In response to CrimsonVision
CrimsonVision wrote:
if (icons["[s]"] <> null)

You actually use the <> Not operator over the != Not operator?

I'm less interested in the article as I already know the wonders of modular design (it *IS* a good article though), but I'm very interested in your preference of not operator :P

I come from derivatives of BASIC. It's very commonplace to see <> or even "Not" as the NOT operator. Even more so, usually comparisons and assignments both use the = symbol (without a second equals to signify comparing).

Furthermore, most variants of BASIC use "sigils" for determining a data type:

integer = %
float = #
string = $

some_int% (no symbol is also valid)
some_float#
some_string$

I used to use != but since I work in both BlitzMax (BASIC roots) and DM, I can use <> for both. I also sort of like the way it looks.

--

@FKI:
The reason why I had done it that way is because I wanted to force s to be a string. I guess I could have done something like s = "[s]" to instantly convert it before using the conversion twice in the proc.