network programming without goto (refactoring)

Roy Smith roy at panix.com
Thu Nov 25 12:22:52 EST 2004


In article <mailman.6783.1101394437.5135.python-list at python.org>,
 kent sin <kentsin at yahoo.com> wrote:

> Please help:
> 
> I was really blocked here. without goto I really do
> not known how to do it.
> 
> The problem is to use PyZ3950 to consult a lists of
> hosts and for each of them to search for a list of
> targets. Since the network is undetermined, there were
> always some exceptions: I would like to allow it to
> retry for 3 times. Moreover, during the query process,
> the conn will timeout (set by the remote server).
> Reconnect is preferred before fail the current search,
> but the reconnect may fail even if the first try is
> succeed.


[spaghetti code deleted for brevity]

The real problem you've got is that you're trying to smash too much 
stuff into one function.  You've got several things going on here at 
once.  First, you're iterating over several hosts to try.  Next, you're 
making several attempts to connect to each host.  Lastly, you've got 
some buildquery()/search() stuff going on (whatever that may be).  I'm 
not at all sure what you're trying to do with "for t in targets", but 
I'm going to assume it's somehow related to iterating over the various 
hosts.

What you want to do is refactor this into a number of smaller functions, 
each one encapsulating one piece of the puzzle.  To start, I'd build a 
function which handled the multiple connection attempts to a given host.  
Maybe something like this:

#
# Untested code -- this is just to give you some ideas
# and get you thinking in the right direction
#
def connectWithRetries (host, port, retryLimit):
   attempts = 0
   while attempts < retryLimit:
      try:
         connection = zoom.Connection (host, port)
         return connection
      except ConnectionError:
         attempts += 1
   throw ConnectionError

Then, I'd handle the iteration over multiple hosts:

def connectToSomeHost (hostlist, port):
   for host in hostlist:
      try:
         connection = connectWithRetries (host, port)
         return connection
      except ConnectionError:
         # ignore
   throw ConnectionError

Finally, It's time to implement your query logic, which I'll leave as an 
exercise for the reader (mostly because I don't really understand what 
you're trying to do with that).

None of this is network-specfic or even Python-specific.  The idea of 
breaking up a complex operation into smaller steps and implementing each 
one in its own function is pretty much a universal idea.  Each function 
can be easily designed and tested on its own (and, later, understood by 
a future reader).

The question then becomes, how to decide what stuff to put in what 
function?  There's no hard and fast rules, but the general idea is that 
each function should do one thing, or a small set of very closely 
related things.  For example, I could imagine possibly combining 
connectWithRetries() and connectToSomeHost into a single larger function 
that did both tasks, because they're both part of the basic "get a 
connection to a host" concept.  But, getting the connection and using it 
to perform a query definitely don't belong together.

Here's some rules of thumb:

Several smaller functions are usually better than one larger one.

If you can't see the entire function code without scrolling in your 
editor, it's too big.  With todays windowing systems, that probably 
means about 40 lines.

If it's got more than a couple of loops, or more than a couple of try 
blocks, you're probably doing too much in a single function.

If you can't explain to somebody in a single sentence what your function 
is doing, it's doing too much.

The take-home assignment is to DAGS for "refactoring".  There's been a 
lot written on the subject in the last 5-10 years.  A lot of what's been 
written assumes an object oriented environment (in a C++ or Java like 
language), but the basic concepts hold for all languages, and for 
procedural as well as OO styles.



More information about the Python-list mailing list