Selection Factory

Summary

Purpose

Generates query selections (i.e., WHERE clauses) based on identity object attributes.

Scenario

A key task for any domain object mapping framework is to identify the set of relational data on which to operate. Applications indicate this set using identity objectsAn identity object often corresponds directly to a table's primary key and as such refers to exactly one row of data. An identity can also define criteria that match several rows across multiple tables.

The relational concept that is analogous to an identity object is the WHERE clause in SQL statements. To illustrate the mapping from identity objects to  selections (i.e., WHERE clauses), reconsider the Product table example given in the introduction section earlier: An application uses domain objects to access the [Product] table which contains all products available for sale. The application also uses two types of identity objects. The first is a simple ProductCode that is unique for every product. The second is a ProductCriteria object which defines various optional attributes that are useful for inventory searches. The following figure illustrates:

The Selection Factory pattern encapsulates the details of translating from identity objects to WHERE clauses using a factory object. Since these details are likely to vary among identity objects and target database entities, you can define factory objects for each translation variation

Structure & UML

The following figure illustrates the static structure for the Selection Factory pattern :

The ISelection interface defines the selection representation for your system and ConcreteSelection implements it. If the domain object mapping sits on top of a data accessor, you can define a selection representation that is independent of any database platform. In most cases, you simply use strings to represent SQL WHERE clauses. The ISelectionFactory interface defines a single method for generating a new ISelection-derived object based on an identity object. ConcreteSelectionFactory implements this interface for a specific identity object type or target database entity. The overall domain object mapping is likely to require multiple ISelectionFactory implementations each with customized mapping details encapsulated within each of them. Client code and any other object mapping infrastructure code refers only to ISelection and ISelectionFactory interfaces, keeping them decoupled from any specific mapping details.

The following figure illustrates the sequence diagram for the Selection Factory pattern:

When a client requires a new Selection object, it creates an identity object and passes it the ConcreteSelectionFactory.NewSelection method which in turn creates and returns a ConcreteSelection object. The client uses this ConcreteSelection object to issue database read and update operations.

Example

The following code implements the ISelectionFactory for the example given in the Scenario section:

public interface ISelectionFactory
{
    string NewSelection ( object obIdentityObject );
}

// ISelectionFactory implementation that generates a selection clause
// based on a given ProductCode
public class ProductSelectionFactory : ISelectionFactory
{
    // ISelectionFactory implementation
    public string NewSelection( object obIdentityObject )
    {
        string strIdentity = (string)obIdentityObject;
        return "ProductCode = '" + strIdentity + "'";
    }
}

// ISelectionFactory implementation that generates a selection clause
// based on a ProductCriteria object
public class ProductCriteriaSelectionFactory : ISelectionFactory
{
    // ISelectionFactory implementation
   
public string NewSelection( object obIdentityObject )
    {
        ProductCriteria criteria = (ProductCriteria)obIdentityObject;
        StringBuilder sb = new StringBuilder();

        // Criteria
        sb.AppendFormat( "Criteria = {0} and Price = {1} and Origin = '{2}'",
        criteria.Category,
        criteria.Price,
        criteria.Origin );
        return sb.ToString(); 
    }
}

public class ProductCriteria
{
    private int nCategory;
    private double dPrice;
    private string strOrigin = "";

    public int Category
    {
        get { return nCategory; }
        set { nCategory= value; }
    }
    public double Price
    {
        get { return dPrice; }
        set { dPrice = value; }
    }
    public string Origin
    {
        get { return strOrigin; }
        set { strOrigin = value; }
    }
}

static void Main(string[] args)
{
    /* Product selection factory */
   
// Use selection factory to obtain a selection (WHERE) clause
    ISelectionFactory sfProduct = new ProductSelectionFactory();
    string strProdWhere = sfProduct.NewSelection( "1234" );

    // Use DAL to execute query supplying the above selection object
   
string strSQL = "select * from Product " + strProdWhere;

    /* Product criteria selection factory */
   
// First construct a ProductCriteria object to be used by the ProductCriteriaSelectionFactory
    // to return a Selection object
    ProductCriteria criteria = new ProductCriteria();
    criteria.Category = 10;
    criteria.Origin = "US";
    criteria.Price = 0.50;

    ISelectionFactory sfProductCriteria = new ProductCriteriaSelectionFactory();
    string strProdCriteriaWhere = sfProductCriteria.NewSelection( criteria );

    // Use DAL to execute query supplying the above selection object
   
string strSearchSQL = "select * from Product " + strProdCriteriaWhere;
}

Applicability

Use this pattern when:

Strategies / Variants

Consider these strategies when designing a selection factory:

Benefits

Related Patterns