Blueprint (Object Creational)

Chad Austin (aegis@aegisknight.org) - 2001.11.29

Intent

Allow for future expansion to the set of parameters used to create an object and provide reasonable default values for them.

Also Known As

Motivation

Often functions that create objects take a list of parameters used to specify characteristics of the new object. Unfortunately, once you initially create a list of arguments, you cannot add new parameters or remove obsolete ones without breaking binary and source compatibility. Also, users don't always want to specify every parameter if they can get away with just using sane defaults.

Applicability

The Blueprint pattern is applicable in any situation where there are a lot of parameters or when binary or source compatibility must be maintained. Some languages provide support for this pattern natively (Python's keyword arguments, for example).

Structure

<insert class diagram here>

Participants

Attributes Object

A simple object with only setters and getters. Immediately after creation, all attributes should contain reasonable default values.

Creation Function

The function responsible for creating the object takes one extra attributes object parameter. It queries this object for all attributes required to instantiate the object. If an attributes object isn't passed in, the factory operates as if a default attributes object is used.

Collaborations

The user creates the attributes object, filling it with necessary values, and the factory queries them in order to determine how to create the resulting object.

Consequences

Since the attributes are being passed in via a containing object, an additional level of indirection exists in the system. The attributes object can actually be defined in terms of an interface with QueryInterface- or dynamic_cast-like functionality. This way, not only is it easy to add new attributes to the system, it's easy to specify new implementations of the interface. For example, in a filesystem-like factory like the following:

class IFile {
public:
  // whatever
};

class IFileAttributes {
public:
  virtual string getName() = 0;
  virtual void setName(const string& name) = 0;
  
  // UCS-2
  virtual wstring getWideName() = 0;
  virtual void setWideName(const wstring& name) = 0;
};

class IFileSystem {
public:
  virtual IFile* openFile(IFileAttributes* attr) = 0;
};

Obviously, we can implement these interfaces in terms of standard C or C++ file functions. Additionally, we can implement an FTP filesystem. If we need to specify FTP-specific parameters in the openFile call, we can create an IFTPFileAttributes interface and have the FTPFileSystem implementation do a dynamic_cast to see if the file attributes object implements this extra interface.

Implementation

In Python, no extra work is necessary, as the language itself provides keyword parameters:

class File:
  def __init__(self, filename, mode):
    # do whatever
    pass

def openFile(filename, **kw):
  if kw.has_key('mode'):
    mode = kw['mode']
  else:
    mode = some_default_mode
  return File(filename, mode);
  
file1 = openFile('filename1');
file2 = openFile('filename2', mode='rb');

Sample Code and Usage

class IAttributes {
public:
  std::string getName();
  void setName(std::string name);
};

class Factory {
public:
  IObject createObject(IAttributes* attr) {
    return new Object(attr ? "" : attr->getName());
  }
};

Known Uses

Related Patterns

Combined with abstract factory, Blueprints can provide a mechanism for specifying attributes of an object with as-yet unknown type.

Notes

This is an initial draft, and I'm not all that happy with it. Primarily, I don't know if the name fits. Also, I'd like some feedback on the pattern description and example code. And whether the pattern is useful at all. :)