Plugin Methods and the Call Mechanism

We now have all the plug-ins tagged, and, in theory, we should know which methods are available on which plug-in objects. However, this approach is not very flexible. We've added the tags so the functions are optimized and the plug-ins are not called unnecessarily. There still might be situations when the plug-in announces its interest in some type of call, but does not implement the functions that the host application associates with that set of keywords.

Since the host application and plug-in software are very loosely coupled and quite often developed by completely different organizations, it is practically impossible to synchronize the development progress of the two. For example, suppose that a host application is designed to call the function_A() method on all plug-ins that announce their interest in the keyword foobar. Then the host application is modified so that it calls the two methods function_A and function_B on all plugins marked with the same keyword. However, some of the plug-ins may not be maintained, or they simply may not be interested in implementing the new function—it's sufficient to implement just the single function for their purposes.

This may seem to be a problem, but it actually isn't. The host application is going to call the method without checking whether it's available. If the plug-in implements that method, it will execute it. If the method is not implemented and not defined, that's fine—we simply ignore the exception. This technique is called duck typing.

We'll give the manager class the following new method, which will be responsible for calling the plug-in methods. The main application will call this method with the name of the function that it wants the plug-ins to run. Optionally, it can also pass the list of arguments and keywords. If the keywords are defined, the call will be dispatched only to the plug-ins that are marked with one or more keywords from that list:

def call_method(self, method, args={}, keywords=[]): for plugin in self.plugins:

if not keywords or (set(keywords) & set(self.plugins[plugin])): try:

getattr(plugin, method)(**args) except: pass

Now we can finish writing our host application. Let's replace the print statement that prints the log line structure with the actual call to the plug-in manager call dispatcher method. We'll call the process() method in the main loop and pass in the log line structure as an argument. All plug-ins that implement this method will receive the function call along with the keyword arguments. At the end of the loop, we'll call the report () function. The plug-ins that have anything to report now have an opportunity to do so. If the plug-in is not designed to produce any reports, it will simply ignore the call.

plugin_manager = PluginManager()

log_generator = LogLineGenerator()

for log_line in log_generator.get_loglines():

plugin_manager.call_method('process', args=log_line) plugin_manager.call_method('report')

WHAT IS DUCK TYPING?

The term duck typing comes from James Whitcomb Riley's quote, "When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."

In object-oriented programming languages, duck typing means that the behavior of an object is determined by the set of its available methods and properties, not its inheritance. In other words, we're not worried about the type of the object class, as long as the methods and properties we're interested in are present and available. Therefore, duck typing does not rely on object type tests.

When you need something from the object, you simply ask for it. If the object doesn't know what you want from it, an exception will be raised. This means that the object doesn't know how to "quack" and therefore it is not a "duck." This method of "test and see what happens" is sometimes referred to as the Easier to Ask for Forgiveness Than Permission (EAFP) principle. It's best illustrated in the following sample code:

Was this article helpful?

0 0

Post a comment