Best practices for organizing objects

I wonder if how I call objects through different functions is the ‘best way’ of doing it (or if at least is in line with Julia’s best practices). A general example of what I am doing is below.

All objects needed in my program (e.g., variables or parameters) are collected in a specific module with different mutable structures. This module is saved in a separate .jl file:

module Globals

    export a, b, c

    mutable struct A
        ....
    end

    a = A()

    mutable struct B
        ....
    end

    b = B()

    mutable struct C
        ....
    end

    c = C()

end

In the main.jl file of my program, I import the module Globals and also call different functions that use the objects inside the module:

@everywhere include("globals.jl")                   
using .Globals
import .Globals: a, b, c

a.do_something = true        
foo(a, b)

c.something = foo2(b)

foo3(a, c)

Some objects defined in struct A, B, and C are empty until a particular function is called and defined in a very general way (e.g., x1::Any = Nothing()).

I am wondering if this way of structuring my program is, e.g., more efficient than other ways, like pre-defining my objects within each function instead of importing them from the module Globals? In general, the sketch above would be in line with Julia’s best practices? Any advice/thoughts on this is very welcome!

Thanks!

It’s generally better (for style, performance, maintainability) to avoid global variables entirely if you can. Global variables tend to be slow in Julia, but the overall idea that usage of global variables should be kept to a minimum is common throughout software engineering — google “avoid global variables” to find lots of commentary on this.

3 Likes

Would using function arguments to pass objects between functions be a way to avoid global variables?

In other words, instead of generating an instance of my A structure in the module Globals, I could allow each of my functions to create an instance of the A structure and thus manipulate a shared object (say, x1) within each function:

struct A
    x1::Any
end
function foo()
    a = A(0)
    
    foo2() = begin
        a.x1 += 1
    end
    
    foo3() = begin
        a.x1 = 0
    end
    
    return foo2, foo3
end

increment, reset = foo()

Yes.

(Though your posted code is not an example of this — your functions don’t have any arguments!)

1 Like