Novell is now a part of Micro Focus

iManager Plugin Design idiom - Private Class Data

Novell Cool Solutions: Feature
By R Sathish

Digg This - Slashdot This

Posted: 18 Jan 2005
 

The Generic Design Generator Tools use a Design idiom: Private Class Data. This Article describes this useful pattern.

Design Pattern - Private Class Data

Problem: A class has numerous member variables/properties, allowing all methods to manipulate them.
Desired Solution: Reduce visibility of attributes to methods.

Problem: A class has one-time mutable variables, which cannot be declared 'final'
Desired Solution: Allow one time setting without setters.

Design Pattern can:

  • Reduce class variables
  • Remove write privilege of class variables, which are set once
  • Mandate getters,setters over direct access
  • Separate logic from data

How Design Pattern succeeds:

  • It solves these problems by extracting out a DATA class for every CLASS and CLASS has a DATA object.
  • Getters are provided for all variables
  • Setters are provided for variables which need to change after construction.

Objectives:

  • Visibility should be minimum
  • Class data should be protected
  • Class data should be minimum

Detailed Description

Existing OOPL does not put a limit on visibility of class attributes on methods.

Class CLASS
{
int int_x;
Map map_y;
String string_s;
method1()
{
x = y.size();
}
method2()
{
x = s.length();
}
method3()
{
}
}

There are lot of global variables. Least desired in an Object oriented system. All variables can be made private to the class, but all methods can read and write to them. Many of these class variables are constants, so they can be made final, but many are set once only. They are final after constructor call.

How to reduce visibility of variables which are highly coupled across all methods:

Make a class DATA{
private int int_x;
final private Map map_y = new Map();
final private String string_s = "const";
DATA(int y)
{
x = y ^ 2;
}

int getX(){
return x;
}
Map getY(){
return y;
}
String getS(){
return s;
}
}
class CLASS{
private DATA data;
CLASS(Object o)
{
data = new DATA(o.something());
}
....
}

Now there is one Class variable DATA which encapsulates all other global variables. No one can set any variable. All can get all variables.

Summary

This design pattern provides

  • Encapsulation of all class variables to single DATA object.
  • Provides write access optionally to a non-final class variable.
  • Separation of data and methods that use that data.
  • Enclosure of data and methods that initialize, process that data.
  • Encapsulates all class variables to one.
  • Provides new type of final - Final after constructor
  • Pattern is very generic - for any CLASS in any language.
  • Although it uses pseudo JAVA - it is applicable in all OOPL like C++.

Reduces global variables, Final after constructor, Improved design

Usage in Generic Design Generator Tools

HandlerGenerator.java
You can see how the global variables in HandlerGenerator is reduced to one "data" variable which is initialized during construction and used by all methods.

/*
 * Created on Nov 2, 2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.novell.admin.generic;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.TreeSet;

/**
 * @author rsathish
 * 
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class HandlerGenerator implements HandlerGeneratorConstants {

    public static void main(String args[]) {
        new HandlerGenerator(args[0]).generate();
    }

    private HandlerGeneratorData data;

    public HandlerGenerator(String args) {

        try {

            data = new HandlerGeneratorData(args);

        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void generate() {
        processKeys();

        HashMap pageBufs = generateActionHandlers();

        StringBuffer taskBuf = generatePageHandlers(pageBufs);

        generateTaskHandler(taskBuf);
    }

    private HashMap generateActionHandlers() {
        final HashMap pageBufs = new HashMap();

        GenerateActionHandler aH = new GenerateActionHandler(data);
        for (Iterator iter = data.getActionNames().keySet().iterator(); iter
                .hasNext();) {
            aH.execute(new Object[] { iter.next() }, pageBufs);
        }
        return pageBufs;
    }

    private StringBuffer generatePageHandlers(HashMap pageBufs) {
        final StringBuffer taskBuf = new StringBuffer("");
        GeneratePageHandler pH = new GeneratePageHandler(data);

        for (Iterator iter = data.getPageNames().values().iterator(); iter
                .hasNext();) {
            pH.execute(new Object[] { iter.next(), pageBufs }, taskBuf);
        }
        return taskBuf;
    }

    private void generateTaskHandler(StringBuffer taskBuf) {

        GenerateTaskHandler tH = new GenerateTaskHandler(data);
        tH.execute(new Object[] { taskBuf }, null);
    }

    /**
     * @return
     */
    private Iterator getSortedKeys() {
        Enumeration e = data.getRb().getKeys();
        TreeSet tSet = new TreeSet();
        while (e.hasMoreElements()) {
            tSet.add(e.nextElement());
        }

        Iterator itr = tSet.iterator();
        return itr;
    }

    /**
     * @param itr
     */
    private void processKeys() {
        Iterator itr = getSortedKeys();

        while (itr.hasNext()) {
            //key of format P1 = PageName, P1_P2=ActionName
            String key = (String) itr.next();
            //value is a string literal, preferably in Title Case.
            String value = data.getRb().getString(key);

            if (key.startsWith("P") == false)
                continue;

            StringTokenizer tkns = new StringTokenizer(key, "_");

            int tokens = tkns.countTokens();

            switch (tokens) {
            case 1:

                data.getPageNames().put(key, value);
                break;
            case 2:
                data.getActionNames().put(
                        key,
                        data.getPageNames().get(tkns.nextToken()).toString()
                                + value);
                break;
            default:
                break;
}

        }
    }

}

HandlerGeneratorData.java
See the number of setters and getters.
Also notice how protected data constructor achieves one time settability in constants.

/*
 * Created on Nov 8, 2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.novell.admin.generic;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.PropertyResourceBundle;

import org.antlr.stringtemplate.StringTemplate;

/**
 * @author rsathish
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
class HandlerGeneratorData implements HandlerGeneratorConstants{
    public HashMap getActionNames() {
        return actionNames;
    }

    public String getAuthor() {
        return Author;
    }

    public String getDate() {
        return Date;
    }

    public String getOutputPath() {
        return OutputPath;
    }

    public void setOutputPath(String outputPath) {
        OutputPath = outputPath;
    }

    public HashMap getPageNames() {
        return pageNames;
    }

    public String getPluginName() {
        return PluginName;
    }

    public PropertyResourceBundle getRb() {
        return rb;
    }

    public String getTemplatePathPrefix() {
        return TemplatePathPrefix;
    }

    protected HandlerGeneratorData(String args) throws IOException {
        rb = new PropertyResourceBundle(new BufferedInputStream(
                new FileInputStream(args)));
        TemplatePathPrefix = rb.getString("TEMPLATE_PATH_PREFIX");
        PluginName = rb.getString("TASK_NAME").toUpperCase();
        OutputPath = rb.getString("OUTPUT_PATH_PREFIX") + OutputPath
                + PluginName.toLowerCase() + File.separator;
        Author = rb.getString(HandlerGeneratorConstants.AUTHOR);
        Date = new Date().toLocaleString();
        actionHandlerTemplate = new
StringTemplate(HandlerGeneratorUtils.contentOfFile(TemplatePathPrefix
                + ActionHandlerPath));
        pageHandlerTemplate = new
StringTemplate(HandlerGeneratorUtils.contentOfFile(TemplatePathPrefix
                + PageHandlerPath));
        taskHandlerTemplate = new
StringTemplate(HandlerGeneratorUtils.contentOfFile(TemplatePathPrefix
                + TaskHandlerPath));
    }

    private String Author;

    private String Date;

    private String OutputPath = "/src/com/novell/admin/";

    private String PluginName;

    private PropertyResourceBundle rb;

    private String TemplatePathPrefix;

    private final HashMap actionNames = new HashMap();

    private final HashMap pageNames = new HashMap();
    
    private StringTemplate actionHandlerTemplate;
    
    private StringTemplate pageHandlerTemplate;
    
    private StringTemplate taskHandlerTemplate;
    
    public StringTemplate getActionHandlerTemplate() {
        return actionHandlerTemplate;
    }
    public StringTemplate getPageHandlerTemplate() {
        return pageHandlerTemplate;
    }
    public StringTemplate getTaskHandlerTemplate() {
        return taskHandlerTemplate;
    }
}


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© Copyright Micro Focus or one of its affiliates