Monday, June 30, 2008

Interfaces in Python.

Defining interfaces that your classes will provide is an essential programming practice if you plan to develop extensible code. That is, when you define interfaces, you are defining your internal architecture. The interfaces you define will give you a starting point in which you can discover more meaningful abstractions in your application domain.

Interfaces are not a replacement for object-oriented analysis. You can't just put together a set of interfaces that you think might work and hope for the best. Rather, your interfaces should be derived from concepts created by doing analysis and development. That being said, you will discover how your interfaces need to be refined and expanded upon as you are implementing classes that realize your interfaces. Don't get stuck in the waterfall approach to software development.

Zope provides an interface package which allows Python developers to define interfaces and implement classes that realize them. For example, say I want to define a blog entry interface. Using the zope.interface package, I might do something similar to:

import zope.interface

class IBlogEntry(zope.interface.Interface):
"""A simple blog account interface."""
title = zope.interface.Attribute("""The title of the blog entry.""")
content = zope.interface.Attribute("""The main content of the blog entry.""")
date = zope.interface.Attribute("""The publish date of the blog entry.""")

def setTitle(self, title):
"""Set the title of the blog entry."""

def setContent(self, content):
"""Set the content of the blog entry."""

def setDate(self, date):
"""Set the date of the blog entry."""

def getTitle(self):
"""Return the title of the blog entry."""

def getContent(self):
"""Return the content of the blog entry."""

def getDate(self):
"""Return the date of the blog entry."""

def publish(self):
"""Publish the blog entry."""

class BlogEntry:
zope.interface.implements(IBlogEntry)
And of course we would then have to implement all the attributes and methods defined in IBlogEntry. We can then test if IBlogEntry is implemented by any given BlogEntry instance as follows:

blog_entry_obj=BlogEntry()
IBlogEntry.providedBy(blog_entry_object)

Of course, this interface gives us a good idea of what an blog entry implementation might look like. We can easily implement a different blog entry class because the interface provides us with an interchangeable template. As long as these methods an attributes are implemented, our class may be used where the IBlogEntry interface is required.

Is there a way we could re-factor our interface to get more use out of it? I think so. In a system which incorporates blog entry abstractions, it is most probable that there will be similar abstractions. Similar in the sense that these other abstractions will have a title, content, and date. Lets take a look at an alternative interface implementation of interfaces provided by BlogEntry.


import zope.interface

class ITitle(zope.interface.Interface):
"""A simple title interface for abstractions with a title."""
title = zope.interface.Attribute("""The title of the abstraction.""")

def setTitle(self, title):
"""Set the title of the abstraction."""

def getTitle(self):
"""Return the title of the abstraction."""

class IContent(zope.interface.Interface):
"""A simple content interface for abstractions with content."""
content = zope.interface.Attribute("""The content of the abstraction.""")

def setContent(self, content):
"""Set the content of the blog entry."""

def getContent(self):
"""Return the content of the blog entry."""

class IDate(zope.interface.Interface):
"""A simple date interface for abstractions with dates."""
date = zope.interface.Attribute("""The date of the abstraction.""")

def setDate(self, date):
"""Set the date of the blog entry."""

def getDate(self):
"""Return the date of the blog entry."""

class IPublish(zope.interface.Interface):
"""A simple publishing interface for publishing abstractions."""

def publish(self):
"""Publish the abstractions."""

class BlogEntry:
zope.interface.implements(ITitle)
zope.interface.implements(IContent)
zope.interface.implements(IDate)
zope.interface.implements(IPublish)

In this implementation, we have removed all references to blog entry in the interfaces we have defined. This is a good practice when there are several non-trivial abstractions involved in the system being implemented. We can then reuse our interfaces for several class implementations rather than keeping them specific to blog entry.

Finally, keep your interfaces simple. The goal in using interfaces is to keep the implementation of abstractions consistent. This is not accomplished be further complicating the code by adding interfaces that are more complex than necessary.

Friday, June 27, 2008

Meaningful names in code.

Ever have to read someone else's code only to find out that it is unreadable? The biggest issue I find when reading other people's code is the names they have chosen. To have trouble understanding the reasoning behind a given piece of code does not require you to sift through the entire project, tracing every invocation back to the code you are looking at. All it takes is a few poorly chosen names.

It actually takes a lot of effort to create bad names in code. Say, for instance, you create a variable named x in a method you are currently implementing. This works well at first because it is easy to type and you don't need to spend any time thinking about what this value represents. However, further down the line, you are going to need to use x in some way. So, now what? What am I going to do with x? I can't possibly guess what the value of x is. It could be anything. So now go into retreat mode and find where x was initialized to check what the value could be. But it does not end there.

Chances are that if a developer is willing to use a name such as x for a variable name, the names chosen for classes, methods, and functions are not likely to be any better. So, what if x was initialized with y(). What if this same coding practice is being implemented throughout the entire project. It becomes a perpetually unreadable monstrosity.

All this could have been avoided simply be replacing x=y() with page_result=getPages(). This will allow other developers and, most importantly, yourself to infer the contents of software objects and put that saved time toward higher-level architectural objectives.

Thursday, June 26, 2008

A simple TurboGears i18n principle.

I've been doing a lot of internationalization improvements in Enomalism lately and I've noticed a generally good practice to use when building i18n applications.

TurboGears adds a built-in _() Python function that serves as a basic message translator. During run-time, if the label is found in the message catalog for the current locale, the label is replaced. Simple enough.

Things tend to get messy when there are %s string formatters in the label. I find that this severely limits the opportunity for language label re-use.

Wednesday, June 25, 2008

Building scalable software objects.

With the computing resources available today, practicing software developers need to build an architecture that can easily take advantage of these resources. Does this mean that every new application built needs to be a globally distributed grid computing architecture? No. However, your code can be built with scalability in mind even when this is not a requirement.

The leading cloud computing (buzzword) resource at the moment is Amazon EC2. The elastic compute cloud offering from Amazon provides a consistent, stable API that allows developers to manage virtual machines on Amazon's network. This is an extremely powerful tool that provides computing on demand. You only pay for the computing resources that you need. When you are finished with your machine, you terminate it. This essentially eliminates the need for a massive server setup in which most of the time, machines could be sitting idle.

So how does this all come together? Do you hire someone that manages these machines? Someone to launch new machines when the need arises and terminate them when they are no longer needed? This job is much better suited for the actual software that requires the resources. Ideally, the architecture should be built in such a way that it can determine when these distributed resources are needed and when they can be negated.

This is simply a concept of resource management and is already implemented in operating systems and many applications in existence today. The main difference being that these resources may not necessarily live on the same physical hardware. When designing modern software, the potential use of remote resources should not be neglected.

Enomalism uses this approach for much of the machine control functionality. Put simply, an attempt to perform some action on a machine in Enomalism will result in a simple redirect to the hypervisor that could potentially be on some remote machine. The key word here is potentially; the same functionality is still realized if the hypervisor is on the same machine in which the request originated.

This approach applies to almost component you may build. Allowing for the potential to use remote resources is critical for scaling. Even if the need may not be now. It will someday.

Sunday, June 22, 2008

Use cases from the perspective of software objects.

Use cases from the perspective of software objects? What does that mean exactly. Well, it is misleading in the context of contemporary use case modeling. When I first encountered use cases, I have to say I was less than impressed. There are actors and uses cases. Big deal. Why can't we just hear what needs to be built, go out and build it, and iteratively evolve the software accordingly. Boy was I wrong.

The importance of understanding what is being built is so important that it cannot be underestimated. Especially as software grows ever more complex.

Typically, actors are the users of the system being modeled. For example, you may have a user actor that represents a generic user of the system. You may also have an administrator actor that inherits from user. This works well for modeling the actions the users of the system can perform as well as actions only administrators can perform. Actors also represent external systems in use case diagrams. This provides a new perspective of how your system uses external systems or how your system is used by external systems.

But what about modeling software objects as actors? This is completely valid and a very powerful abstraction tool. Modeling what your system will do in a standard use case diagram is a challenging task on its own. The next step is to realize this functionality. Once some software objects have been conceived, we can then conceptualize how these objects will behave by modeling them as actors in a use case diagrams.

Alternatively, software objects can be modeled in use case diagrams as the system boundary, or system context. Your software object essentially becomes the system you are modeling.

Modeling software objects as in uses cases can help discover interfaces that your various objects will both require and provide.

Friday, June 20, 2008

Enomalism2 First Look

Enomalism2 First Look

This is a quick introductory video to the new Enomalism. Things are looking pretty good for a stable release in the not too distant future. I'm sure I'll be writing plenty about Enomalism as development progresses.

New-Style Python Classes

Well, I guess you can't really call them new anymore but they are nonetheless an important feature of the Python programming language. It seems that a lot of Python code I look at these days does not use new-style classes. But then again, a lot of it does.

The basic idea of new-style classes is to bridge the gap between primitive solution space and the abstract problem space. As a quick analogy, picture yourself trying to make a bowl of homemade soup. There are several steps involved in the process such as cutting up vegetables, measuring ingredients, mixing ingredients, etc. The common theme here is ingredient. Those with an object-oriented mentality are going to say something like "Ingredient would be a great candidate for a base class for all ingredients in the soup". And I would have to agree.

Now, imagine stove-top cooking as an environment we are working in. While building the soup, we can't conceptualize anything other than what is on the stove-top. In this environment, each ingredient would be considered primitive. The soup inherits all the attributes and behavior from these basic, primitive ingredients. That is the basic idea behind new-style Python classes.

An example of this powerful Python concept:

>>> class book(list):
... pass
...
>>> class page(object):
... name=""
...
>>> my_book=book()
>>> my_book.append(page())
>>> for i in my_book:
... print i
...
<__main__.page>

Here we have two new-style class definitions. The first class, book, inherits from the Python primitive type list. The second class, page, inherits from the Python primitive type object. We then add a page to out book instance, and display all the page objects in out book instance. Pretty simple.

So, why bother generalizing a primitive type when we could just instantiate and use the type directly. That is, why not just create a new Python list and be done with it?

The first reason is one of abstraction. Our book class is just a Python list. It has no real purpose other than to instantiate the Python list object. However, there are several taxonomic list forms like book, bookshelf, the line at the door. They are all lists and Python makes bringing more elegant meaning to our lists an easy task.

The second reason is flexibility and functionality. We can now add our own methods to our lists as well as override the existing list methods.

The possibilities given by new-style Python classes is almost endless.