Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Re^3: What are the core points of good procedural software design? (functions, code structuring)

by zby (Vicar)
on Jun 23, 2008 at 06:57 UTC ( [id://693454]=note: print w/replies, xml ) Need Help??


in reply to Re^2: What are the core points of good procedural software design? (functions, code structuring)
in thread What are the core points of good procedural software design? (functions, code structuring)

1. Should my functions be returning something instead of setting values of globals? (er.. FSL: file-scope lexicals, that is)

...

(1) If you say "yes" to the first one, then what does that buy me? Instead of setting an FSL somewhere, I'm returning values, and so need to create an FSL to hold the value anyway!

At least you don't have the globals in the body of functions definitions. You can now analyze the functions in separation from the rest of the code - this is a clear win. The next step is to move more code into the functions - and you'll get mostly global free code.
  • Comment on Re^3: What are the core points of good procedural software design? (functions, code structuring)

Replies are listed 'Best First'.
Re^4: What are the core points of good procedural software design? (functions, code structuring)
by doom (Deacon) on Jun 23, 2008 at 18:18 UTC

    Once upon a time, it was realized that human beings couldn't reliably hold an entire program inside their heads at once, so it was necessary to subdivide tasks into subtasks small enough to grasp.

    The trouble with globals to maintain state is that it "breaks encapsulation", you get "action-at-a-distance" between your subtasks, i.e. you don't have something small enough to hold in your head.

    So from there, you get to the idea that we should write "pure functions" where all arguments are explicitly passed in to the routine, and all returns are explicity passed out of it, and there are no "side-effects".

    The trouble is that if you actually try to write large software projects that way it often gets pretty awkward: if you end up passing in a few dozen pieces of information and returning a dozen, that straight-jacket starts feeling pretty tight.

    So, is there some sort of compromise solution? OOP design, with multiple classes is one of them -- but another, very similar one, is proceedural design with multiple modules. These days I have a slight preference for OOP code, but as far as scoping concerns go, the two are close to identical. The main advantage of OOP (in my opinion) is that the namespaces of the things you're using are always labeled by a short alias, i.e. the names of the objects: consider $Some::Hairy::Name::Space::important_variable, vs the Exporter style of important_variable, vs the OOP style of $shns->get_important_variable.

    As to what do do to refactor your hairy code, I'd suggest:

    • If it's all in a script, you need to move it's guts to a module, any kind of module...
    • Now write tests that verify the behavior of the module (it's better to do this on a module rather than a script, especially if you use the perl debugger).
    • Now do a re-write of the module, making the argument passing explicit. I have an idea on how you might do that (see below).
    • As you isolate each sub from accessing globals directly, you should then write additional tests ("unit tests") that verify the behavior of each of them.

    Okay, now here's a possible strategy for getting the globals under control:

    • Move them all into a hash named %global
    • Rewrite all subs so they accept a hashref to %global as a single argument: some_sub( \%global );.
    • Each sub will then need to unpack each value it needs explicitly: my $global = shift; my $some_value = $global->{ some value };. Don't forget the my.
    • Any changes you need to make to %global need to be organized somehow -- to start with maybe just gather them to the bottom of the routines.
    • Now, look at which values each sub needs to unpack. Do you see any patterns? Can you organize the subs into groups that use (and effect) a particular subset of values?
    • Start moving values out of %global into new hashes oriented toward particular purposes.
    • Re-write the effected routines so that they take the new hashes as arguments, rather than %global.
    • A goal here is to minimize the number of hashes you need, but it's okay for a routine to need more than one as an argument.
    After all that, with any luck you'll be in a position to subdivide this module into multiple modules, one centered around each of the hashes. You could even, you know, bless those hashes, and then you've got perl object classes.

    How do you deal with routines that need to work on more than one of the hashes? In the OOP style, you can do things like pass objects into a method as arguments. That just changes the way you unpack the values a little (if you're being good and using accessors).

    All of this work might not be worth it to you of course, but the early stages of what I'm talking about here (moving the code to a module and writing tests) is bound to be helpful even if you don't complete the program.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://693454]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (5)
As of 2024-04-23 21:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found