Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Re: Best practices - if any?

by afoken (Chancellor)
on Feb 21, 2010 at 12:27 UTC ( [id://824490]=note: print w/replies, xml ) Need Help??


in reply to Best practices - if any?

If you have that much code that you feel the need to spread it over several files, think about modularising it.

In C, you would better NOT simply #include "second_part.c", but instead split your code into smaller parts, compile them separately into object files, and use the linker to create a single executable. You would perhaps end with something like main.c, inputreader.c, logger.c, smoothify.c, prettyprint.c, and perhaps utils.c and globals.c. For most of the files, there would be a corresponding *.h file containing the "public" interface, i.e. those functions that are called by one of the other files (logger.h would perhaps contain something like extern int initlogger(const char * logfile); and extern void log(int level, const char * message);, globals.h would instead define the few needed global variables, e.g. extern int verbose; extern char frobnicate;). All functions (and global variables) not needed outside one of the source files would be declared as static, so that the linker does not try to resolve those names.

In Perl, you would do pretty much the same: Put groups of functions into modules, have a public interface for each module (i.e. use Exporter for non-OOP code), and have a short main program that delegates to the modules.

Because Perl already has a lot of modules, use a unique prefix for your module names. If you have no better idea, use the application name and / or your last name or your company's name. You would end with AriSoft::Frobnicate for the main routines, AriSoft::Frobnicate::InputReader, AriSoft::Frobnicate::Logger, AriSoft::Frobnicate::Smoothify, AriSoft::Frobnicate::PrettyPrint, AriSoft::Frobnicate::Utils, and perhaps AriSoft::Frobnicate::Globals.

Thinking about "big subs":

Some big things are a pleasure to the eye, but "big" subs spanning more than one or two screens (i.e. more than 50 lines) are a sure sign of wrong design. You will become confused when you need to change the code, you will pile up status variables and obscure if-then-else constructs, and perhaps you even will abuse goto. Split them into smaller, specialised functions. This is pretty independant from Perl, you have exactly the same problem in nearly every other language.

I'm currently earning my money by refactoring C-like code written by an ungifted amateur, full of bugs, copy-and-paste, gotos and cargo cult, without any proper indenting, with functions spanning literally thousands of lines, with loops and ifs nested more than 10 levels deep, and of course without any useful documentation. I've removed more than 30% of the code without any loss, and I will remove about another 30% before the will go back to the production machines. During the process, the number of functions will at least double. That code is a real nightmare, no one has a clue about what it does, and just deleting the crap and starting from scratch is not an option. All we can do is to cleanup every piece of code we need to touch, and hope for a slow improvement over time.

Learn from that, start writing clean, structured, and documented code NOW.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Replies are listed 'Best First'.
Re^2: Best practices - if any?
by AriSoft (Sexton) on Feb 21, 2010 at 19:24 UTC

    Lets test this theory. Here is one authentic sub from my project. Why should I break it pieces and how this helps to keep it with other subs in the same file? I still prefer to keep this in a separate file like a module.

      I am not going to get into the details of this, but just a couple of quick stylistic comments:

      • instead of including anonymous subs in your hashes, why don't you name them? Instead of row => sub  { # complex code here that ASAIK creates a record }, write row => \&create_record, and then define create_record a little further. This way you're giving a name to that sub, and it becomes easier to see the data structure, without the big, often irrelevant, blob of code right in there.
      • $$flight{Reg}; can be written $flight->{Reg};, which is especially handy when writing $cols->[0]</<c> instead of <c>@{$cols}[0]
      • the commented-out code does not belong in there, if you have to remove code just do it, the source-control system will keep the old version.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-03-19 03:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found