Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / UML

Building Pluggable Parser Components - Builder Pattern (C#)

1.67/5 (8 votes)
20 Jul 2006CPOL3 min read 1   160  
A Design Pattern that is used to implement pluggable parser components and that exhibits the following GRASP Patterns - Creator, Low Coupling, High Cohesion, Polymorphism, Protected Variations.

Introduction

Design Patterns belong to any of the three breeds:

  • Creational
  • Structural
  • Behavioral

A Creational Pattern handles the process of object creation. It basically makes a system independent of how objects are created, composed, and represented. This configuration can be compile time or run time. Hence they are further classifieds as Class or Object Creational Patterns.

Class Patterns deal with subclass relationships that are established through inheritance. The relationships are static, i.e., fixed at compile-time. Object Patterns deal with object relationships that can be changed at run-time, and are more dynamic. Class Creational class patterns defer the responsibility of object creation to subclasses, while Object Creational object patterns defer it to another object. A Class Creational pattern uses inheritance to change the class to be instantiated, while an Object Creational pattern delegates instantiation to another object.

Builder Pattern is an Object Creational Pattern.

Problem

Middle tier components have to be built that support complex parsing logic. The data object (typed dataset) is updated by UI components. Addition and removal of data elements from the mainframe transaction should minimize and isolate the impact in the middle tier components. Hence forth, a Design Pattern needs to be used that:

  • Decouples the process of building a complex object from the parts that make up the object.
  • Defines a construction process that must allow different representations for the object.
  • Provides an extensible framework for new mainframe transactions by constructing a concrete builder, and should be pluggable.

Solution

The Builder pattern is used to enable the creation of a variety of complex objects from one source object (Typed Dataset). The builder object holds a typed dataset that contributes individually to the creation of each complex object (DfhCommArea) through a set of common interface calls of the Abstract Builder class (UpdateBufferBuilder).

A director object is needed to select the builder class. The abstract builder class would be a list of interface calls - such as BuildDfhCommArea() - that the director will use. Each concrete version of the builder class could implement a method for these calls.

C#
/// <summary>
/// Director Class, Director Object 
// </summary>
public class UpdateBufferDirector
{
    public DfhCommArea Construct(
           UpdateBufferBuilder updateBufferBuilder )
    {
        return updateBufferBuilder.BuildDfhCommArea();
    }
}

/// <summary>
/// Abstract Builder Class 
/// </summary>
public abstract class UpdateBufferBuilder
{
    public abstract DfhCommArea BuildDfhCommArea();
}

/// <summary>
/// Concrete Builder Class 
/// </summary>
public class UpdateBuffer220Builder : UpdateBufferBuilder
{
    //Source Object : Typed Dataset
    private Buffer220 _buffer220;

    public UpdateBuffer220Builder()
    {
        _buffer220 = new Buffer220();
    }

    public void FetchSchema(ref DataSet pbuffer220)
    {
        pbuffer220 = _buffer220;
    }

    public override DfhCommArea BuildDfhCommArea()
    {
        //Parse the data in TypedDataSet
        string BufferData = string.Empty;
    
        //Iterate through the Tables 
        //of the Typed dataset and form the BufferData 
    
        //Construct the Product
        DfhCommArea dfhCommArea = new DfhCommArea();
        dfhCommArea._functionCode = 220;
        dfhCommArea._dfhCommAreaLayout = BufferData ;
        return dfhCommArea;
    }
}

/// <summary>
/// Concrete Builder Class 
/// </summary>
public class UpdateBuffer250Builder : UpdateBufferBuilder
{
    //Source Object : Typed Dataset
    private Buffer250 _buffer250;

    public UpdateBuffer250Builder()
    {
        _buffer250 = new Buffer250();
    }

    public void FetchSchema(ref DataSet pbuffer250)
    {
        pbuffer250 = _buffer250;
    }

    public override DfhCommArea BuildDfhCommArea()
    {
        //Parse the data in TypedDataSet
        string BufferData = string.Empty;

        //Iterate through the Tables 
        //of the Typed dataset and form the BufferData

        //Construct the Product
        DfhCommArea dfhCommArea = new DfhCommArea();
        dfhCommArea._functionCode = 250;
        dfhCommArea._dfhCommAreaLayout = BufferData ;
        return dfhCommArea;
    }
}
/// <summary>
/// Product 
/// </summary>
public class DfhCommArea
{
    public int _functionCode;
    public string _dfhCommAreaLayout;

    public DfhCommArea GetDfhCommArea()
    {
    return this;
    }
}

Example

Typed datasets are filled by the UI. Business rules can be applied to this data by the business objects, in which case the client depicted in the sequence diagram would be part of the business object. The construction process would be the same, but would be different based on the function codes. Each function code attributes to a different mainframe transaction. So any new transactions should be pluggable by defining another builder class.

Static View

The Containment relationship composition is shown between:

  • the Director and the Abstract Builder class
  • the Typed Dataset and the Concrete Builder class

Image 1

Dynamic View

  • Creates represents constructors.
  • Buffer220 is a typed dataset that holds data.
  • UpdateBuffer220Builder is the concrete builder class which holds an instance of Buffer220.
  • dfhCommArea is a complex object.

Image 2

GRASP Patterns

Supports the following GRASP Patterns:

  • Creator: the builder classes are responsible for the creation and update of DFHCommArea objects, since they hold knowledge on what is the function code to be assigned and the dfhCommArea layout.
  • Low Coupling: by assigning responsibilities to the right classes. Typed datasets are a part of the builder classes instead of the director classes since it is responsible to read the schema elements and data and form the dfhCommArea.
  • High Cohesion: by distributing responsibilities to the appropriate classes. Parsing logic is the responsibility of the builder class; director responsibility is to select the appropriate builder class.
  • Information Expert: assigns responsibilities to the classes that have information needed to fulfill it.
  • Polymorphism: by exhibiting pluggable software components. Supporting new mainframe transactions would mean building new builder classes. These can be integrated without changing the source or the director object.
  • Protected Variations: since variations in dfhCommArea due to changes in the mainframe would impact only the specific builder classes, which take care of the parsing logic, and not the Director or the dfhCommArea object.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)