What are some design patterns found in prominent contributed modules?
One place to start is with core itself and the patterns exposed to contrib. - a great write-up by Larry Garfield (Crell) can be found in a Drupal Watchdog article.


  Observers, visitors, and peeping toms
  
  Drupal is actually built on the twin patterns of Observer and Visitor, although it doesn't call them by name. Instead, it calls them hooks. hook_node_load(), hook_user_login(), and so forth are, in essence, observers on nodes and users. hook_form_alter(), hook_node_view(), and so forth are, in essence, visitors. Because Drupal does not differentiate between the two, some hooks could arguably be considered both patterns but the principle is the same.
  
  Factories and commands
  
  There are several variations on the Factory pattern, but they all boil down to the same idea: One object, a client, asks another object, a factory, for an appropriate implementation of a piece of logic, but doesn't care which it is. That's up to the factory to decide. Consider the db_insert() function, the important bits of which are show below:

   function db_insert($table, array $options = array()) {
       // ...
       return Database::getConnection($options['target'])-&gt;insert($table, $options);
     }

  
  The Database::getConnection() method is a factory that returns a connection object appropriate for this site.
  
  Doctor Drupal's Dependency Injection
  
  In Drupal 6, the menu system was gutted and rewritten to be a multi-step process. We would now implement the same page callback like this:

   function example_menu() {
       $items['node/%node/example'] = array(
         'page callback' =&gt; 'example_page',
         'page arguments' =&gt; array(1),
         // ...
       );
       return $items;
     }

     function example_page($node) {
       // Do useful stuff here.
     }

  
  In this new setup the menu router gets more complex. In return, however, the page callback gets the node on which it depends passed to it, that is, injected into it. That gives us a number of benefits.
  
  Go for Broke(r)
  
  One common approach is some variation on the idea of a Broker or Mediator. In this design, an object doesn't request information from another object directly. Instead it asks an intermediary object, which may have been injected into it, to make such a request on its behalf. While it still means the first object has to actively request information, it is only tightly coupled to the mediator object and not to the myriad of other objects it may need to request information from. That means if those other systems change we need only update the mediator, not every system that touches it.