FRAMEWORK 101: Building XCF - Lesson 03 - Implementing Convention
November 5th, 2006
If you have looked around this blog you know I am a fan of Ruby on Rails. In fact, for most of you, it is how you found this blog. To a large degree version 6 of XCF, the one this series of articles is about, is based on applying my lessons learned from Ruby on Rails. Two things that framework does very is well is its support of DRY (don’t repeat yourself) and its philosophy of “Convention over Configuration”. Today you will add to XCF a class that will help make DRY the path of least resistance by modeling a set of conventions.
Course outline:
- Lesson 01: Building the core
- Lesson 02: Request Processing
- Lesson 03: Implementing Convention
- Lesson 04: Interpreting XML
- Lesson 05: Building the facade(Builder Design Pattern)
- Lesson 06: Rendering Responses
- Lesson 07: Writing a Native Listener
- Lesson 08: Adding Asynchronous Communication
- Lesson 09: Enhancing the Context Space
- Lesson 10: Adding a State Machine
- Lesson 11: Using the state machine to model a web application
- Lesson 12: Adding AJAX output renderers
NOTE: You can download the source code for today’s lesson here: Lesson 03 Source Code
Or, you can get the most up to date version of XCF available on sourceforge.
What you will be building today
Today you will build your first XCFService class. You use this class to create instances of classes that are singleton like classes: that is classes where there will only be one instance. The primary benifit this class provides is that the client will not need to know the package or class name of the object it requests. This will result in the client objects being highly decoupled from the objects created. The following shows the class, SERVICE_FlyweightFactory, in action:
XCFFacade facade = new XCFFacade();
SERVICE_FlyweightFactory factory = new SERVICE_FlyweightFactory();
facade.putService(SERVICE_FlyweightFactory.XCF_TAG, factory);
factory.addConvention("validator", ".request.parameter.VALIDATOR_");
factory.pushScope();
factory.addPackage("com.eternal.xcf.common");
// this call will return an instance to com.eternal.xcf.common.request.parameter.VALIDATOR_Required
XCFValidator required = (XCFValidator)factory.getInstance("validator", "required");
factory.pushScope();
factory.addPackage("com.eternal.mymodule");
// this call will return an instance to com.eternal.mymodule.request.parameter.VALIDATOR_Required
required = (XCFValidator)factory.getInstance("validator", "required");
The problem of xml based configuration
Like many java frameworks, version 5 of XCF uses XML for framework configuration. This will not change in the version you are writing (version 6). What will change, however, is the format of that XML configuration. The old XML configuration was light on convention. As a result, terms and classes had to be repeated. This, of course, led to bugs as it was easy to mistype. Many frameworks share this problem. In fact, as soon as you require a class specification in an XML file, you are violating DRY. The reason is that you first specify the package and class when you define it, and then you repeat that in your XML file. In this type of setup, the path of least resistence is to put the class anywhere you see fit. This yields code that is harder to maintain by others and one where it is easy to introduce bugs.
By supporting a simple package and class naming convention for each type of object, your new XCFService class will now make the path of least resistence one where the class is created and named such that it follows a simple convention that is documented AND specified in one place.
Flyweight Structure
You use flyweight structures when you need to support large numbers of fine-grained objects efficiently. From the first two articles, you now know that you will use XCF to model functionality using a facade composed of a multitude of objects such as processing instructions, validators, and setters. Additionally:
- The object state for each composite object in the facade is extrinsic. For all these objects we pass in the state (usually an XCFContext object)
- Once the extrinsic state is removed, many groups of objects can be replaced by one. For example, VALIDATOR_Required only requires one instance since the data it validates is passed in as an argument.
- Objects in the facade are decoupled from each other and do not rely on object identity. For example, a request processing instruction may need to contain an object that validates that a parameter exists. However, that class is not dependant upon the class com.eternal.xcf.common.request.parameter.VALIDATOR_Required. It is only requires that there is some class that performs the “required” functionality.
For these reasons, Flyweight is a good approach.
The workhorse class in flyweight is the flyweight factory. Client classes use this class to get a reference to a flyweight instance. The flyweight factory provides method that takes as input a string description of an object type. It then does the work of mapping that type to a concrete class and determining whether or not a new instance of that class needs to be created.
Flyweight Factory Design
To support this pattern, you will only need to create one class: SERVICE_FlyweightFactory. The actual flyweight objects will be all the other XCF classes such as XCFProcessingInstruction, XCFValidator, XCFSetter, etc. The idea behind this class is prior to construction of the facade, you define a set of conventions in the flyweight factory. These conventions are then used by the factory to convert the key lookup string to a concrete class during facade construction.
The other thing you will want to do is introduce the concept of scope. By having a boilerplate flyweight factory, we provide functionality that allows us to map the tag “required” to the class com.eternal.xcf.common.request.parameter.VALIDATOR_Required. However, down the road you may create modules that need to overide that class and provide a different implementation. By introducing scope and providing a mechanism for pushing and popping scope, you will support the ability to overide flyweights and add new types without adversly affecting convention or requiring configuration that violates DRY.
As you can see from the design, the factory contains a stack of scopes. Each scope contains a search path of packages and a cache of flyweight objects already created in that scope. To find a flyweight instance, the factory will ask each scope to return a flyweight. It will do this from top of the stack on down until a flyweight is returned. Each scope will first look in its cache and if one can’t be found, it will use the naming convention for the object type to generate class names. If the class exists, it will create a flyweight instance.
CONVENTION: The class name will be of the form [search path].[type-prefix][tag].
For example, if the typ prefix convention for “validator” is .request.parameter.VALIDATOR_ and in a particular scope, the search path is:
com.eternal.xcf.commoncom.eternal.xcf.myapp
Then for a request of getInstance("validator","required") the scope will look (and instantiate) the first of the following two classes it finds:
com.eternal.xcf.myapp.request.parameter.VALIDATOR_Requiredcom.eternal.xcf.common.request.parameter.VALIDATOR_Required
Flyweight Factory Implementation: SERVICE_FlyweightFactory
package com.eternal.xcf.common.service;
import java.util.HashMap;
import java.util.Stack;
import com.eternal.xcf.core.XCFException;
import com.eternal.xcf.core.XCFFacade;
import com.eternal.xcf.core.XCFService;
/**
* Client classes use this class to get a reference to a flyweight instance.
* The flyweight factory provides method that takes as input a string description
* of an object type. It then does the work of mapping that type to a concrete class
* and determining whether or not a new instance of that class needs to be created.
*
* @author sonjaya
*
*/
public final class SERVICE_FlyweightFactory implements XCFService {
public static String XCF_TAG = "flyweight-factory";
private String name = XCF_TAG;
private XCFFacade facade;
private Stack<Scope> scopeStack = new Stack<Scope>();
private HashMap<String, String>conventions = new HashMap<String, String>();
/**
* Returns services name
*/
public String getName() {
return name;
}
/**
* Set the services facade
*/
public void setFacade(XCFFacade facade) {
this.facade = facade;
}
/**
* Set the services name
*/
public void setName(String name) {
this.name = name;
}
/**
* No extra worked needed when the service starts
*/
public void start() throws XCFException {
}
/**
* No extra work needed when the service stops
*/
public void stop() throws XCFException {
}
/**
* Returns the instance associated w/the tag and type
* This method will first look to see if the instance exists in the current scope
* If a flyweight exists, it will return that, otherwise it will instantiate a new one.
* It repeats this process for each scope on the stack.
*
* @param type identifies the type of the flyweight -- e.g. validator, saxel
* @param tag identifies the flyweight in that type
* @return the flyweight object
* @throws XCFException thrown if it is unable to create a flyweight object
*/
public Object getInstance(String type, String tag) throws XCFException {
// get the sub-package and class name prefix for this type
String prefix = conventions.get(type);
if (prefix == null) throw new XCFException("No convention for type:" + type);
// for each scope started with the top of stack,
// find our create the flyweight
for (int i = scopeStack.size() - 1; i >= 0; i--) {
Scope scope = scopeStack.elementAt(i);
// first see if we have already created the flyweight
Object flyweight = scope.getFlyweight(type, tag);
// if flyweight is this object, then it meant we
// already looked and couldn't find it for this scope
// no need to look again, so move on to the next scope
if (flyweight == this) continue;
// if we got a non-null flyweight that was not this object
// we have what we need and can return it
if (flyweight != null) return flyweight;
// ok, we don't have a flyweight yet for this type,tag and we haven't
// looked yet. Search through all the packages to see if we can find
// the flyweight class
Stack<String> searchPath = scope.searchPath;
for (int j = searchPath.size()-1; j>=0; j--) {
// create the class name by appending [path][prefix][normalized name]
String path = searchPath.elementAt(j);
String name = normalizeName(tag);
String className = path + prefix + name;
facade.logDebug("Searching for: " + className);
// try to load it and create an instance
try {
Class aClass = Class.forName(className);
Object anInstance = aClass.newInstance();
scope.putFlyweight(type, tag, anInstance);
return anInstance;
} catch (ClassNotFoundException cnf) {
facade.logDebug(className + " not in classpath");
} catch (IllegalAccessException ia) {
facade.logDebug(className + " does not have a public constructor");
} catch (InstantiationException ie) {
facade.logDebug(className + " does not have a null constructor");
}
// if we got here, we didn't find it in this path, go on to the next one
}
// there is no flyweight for this context, mark it so
// we don't bother searcing on the next request
scope.putFlyweight(type, tag, this);
}
// if we get here, we can't find the flyweight class and that is a problem
throw new XCFException("Unable to find the class for type: " + type + " and tag: " + tag);
}
/**
* Convert the tag to a name that follows the conventions.
* Upercase word boundaries and remove -'s and _'s
* @param tag
* @return the converted name
*/
String normalizeName(String tag) {
StringBuffer buf = new StringBuffer();
boolean newWord = true;
// capitalize first letter of each word
// remove -'s and _'s
for (int i = 0; i < tag.length(); i++) {
char c = tag.charAt(i);
if (c == '-' || c == '_') {
newWord=true;
} else if (newWord) {
buf.append(Character.toUpperCase(c));
newWord = false;
} else {
buf.append(c);
newWord = false;
}
}
return buf.toString();
}
/**
* Push a new scope onto the scope stack
*
*/
public void pushScope() {
scopeStack.add(new Scope());
}
/**
* Pop a scope off of the scope stack
*
*/
public void popScope() {
scopeStack.pop();
}
/**
* Add a sub-package, prefix convention for the type
* @param type
* @param prefix this should be of the form .[package(s)].[prefix (in caps)]. e.g. .request.processors.VALIDATOR_
*/
public void addConvention(String type, String prefix) {
conventions.put(type, prefix);
}
/**
* Add a package to the search path stack for the current scope. The search path search starts from last first
* @param path
*/
public void addPackage(String path) {
scopeStack.peek().addPath(path);
}
/**
* A scope contains a stack of packages (called the search path)
* and a cache of flyweights bound to a type,tag pair
*
* @author sonjaya
*
*/
class Scope {
Stack<String> searchPath = new Stack<String>();
HashMap<String, Object>cache = new HashMap<String, Object>();
/**
* Add a path to search path stack
* @param path
*/
void addPath(String path) {
searchPath.add(path);
}
/**
* Lookup the flyweight object bound to type,tag return null
* if it doesn't exist
* @param type
* @param tag
* @return
*/
Object getFlyweight(String type, String tag) {
return cache.get(type + "." + tag);
}
/**
* Bind a flyweight object to a type,tag
* @param type
* @param tag
* @param flyweight
*/
void putFlyweight(String type, String tag, Object flyweight) {
cache.put(type + "." + tag, flyweight);
}
}
}
Testing it out
For this test to work, you will need to move the class VALIDATOR_Required that you created in the last lesson to the package com.eternal.xcf.common.request.parameter. You will also need to create the following classes:
com.eternal.stubs.request.parameter.SETTER_Custom(implements XCFSetter)com.eternal.scopeTwo.request.parameter.SETTER_Custom(implements XCFSetter)com.eternal.scopeThree.request.parameter.VALIDATOR_Custom(implements XCFValidator)com.eternal.scopeThree.request.parameter.VALIDATOR_Required(implements XCFValidator)com.eternal.scopeThree.request.parameter.SETTER_WithLongName(implements XCFSetter)
As we won’t actually be invoking these classes they can have empty implementations. Now write the following JUnit test class:
package com.eternal.xcf.common.service;
import com.eternal.xcf.common.service.SERVICE_FlyweightFactory;
import com.eternal.xcf.core.XCFException;
import com.eternal.xcf.core.XCFFacade;
import com.eternal.xcf.core.XCFLogger;
import com.eternal.xcf.request.parameter.XCFSetter;
import com.eternal.xcf.request.parameter.XCFValidator;
import junit.framework.TestCase;
public class TestFlywieghtFactory extends TestCase {
public void testFlywieghtFactoryScope() throws Exception {
XCFFacade facade = new XCFFacade();
SERVICE_FlyweightFactory factory = new SERVICE_FlyweightFactory();
facade.getLogManager().setLogger(XCFLogger.LogTypes.DEBUG, facade.getLogManager().getLogger(XCFLogger.LOG_TO_CONSOLE));
facade.putService(SERVICE_FlyweightFactory.XCF_TAG, factory);
XCFValidator requiredScope1 = null; // exists in common package
XCFValidator requiredScope2 = null; // same as scope 1
XCFValidator requiredScope3 = null; // exists in scope 3 package
XCFSetter customSetterScope1 = null; // exists in stub package
XCFSetter customSetterScope2 = null; // exists in scope 2 package
XCFSetter customSetterScope3 = null; // same as scope 2
XCFValidator customValidatorScope3 = null; // exists in scope 3 package
XCFSetter setterWithLongNameScope3 = null; // exists in scope 3 package
// add a rule that binds validator to .request.processors.VALIDATOR_
factory.addConvention("validator", ".request.parameter.VALIDATOR_");
// add a rule that binds setter to .request.processors.SETTER_
factory.addConvention("setter", ".request.parameter.SETTER_");
/////////////////////////////////////////////////////
// SCOPE 1 TEST
// push scope 1
factory.pushScope();
factory.addPackage("com.eternal.xcf.common");
factory.addPackage("com.eternal.stubs");
// ok, get the scope 1 flyweights
requiredScope1 = (XCFValidator)factory.getInstance("validator", "required");
customSetterScope1 = (XCFSetter)factory.getInstance("setter", "custom");
// these next two shouldn't be there
try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {}
try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {}
assertNotNull(requiredScope1);
assertNotNull(customSetterScope1);
assertTrue(requiredScope1 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
assertTrue(customSetterScope1 instanceof com.eternal.stubs.request.parameter.SETTER_Custom);
assertNull(customValidatorScope3);
assertNull(setterWithLongNameScope3);
/////////////////////////////////////////////////////
// SCOPE 2 TEST
// push scope 2
factory.pushScope();
factory.addPackage("com.eternal.scopeTwo");
// ok, get the scope 2 flyweights
requiredScope2 = (XCFValidator)factory.getInstance("validator", "required");
customSetterScope2 = (XCFSetter)factory.getInstance("setter", "custom");
// these next two shouldn't be there
try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {}
try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {}
assertEquals(requiredScope1, requiredScope2);
assertNotNull(customSetterScope2);
assertFalse(customSetterScope1 == customSetterScope2);
assertTrue(requiredScope2 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
assertTrue(customSetterScope2 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);
assertNull(customValidatorScope3);
assertNull(setterWithLongNameScope3);
/////////////////////////////////////////////////////
// SCOPE 3 TEST
// push scope 3
factory.pushScope();
factory.addPackage("com.eternal.scopeThree");
// ok, get the scope 3 flyweights
requiredScope3 = (XCFValidator)factory.getInstance("validator", "required");
customSetterScope3 = (XCFSetter)factory.getInstance("setter", "custom");
customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");
setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");
assertNotNull(requiredScope3);
assertFalse(requiredScope1 == requiredScope3);
assertEquals(customSetterScope2, customSetterScope3);
assertNotNull(customValidatorScope3);
assertNotNull(setterWithLongNameScope3);
assertTrue(requiredScope3 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Required);
assertTrue(customSetterScope3 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);
assertTrue(customValidatorScope3 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Custom);
assertTrue(setterWithLongNameScope3 instanceof com.eternal.scopeThree.request.parameter.SETTER_WithLongName);
/////////////////////////////////////////////////////
// SCOPE 2 TEST after pop of Scope 3
// pop scope 3
factory.popScope();
// ok, get the scope 2 flyweights
requiredScope2 = (XCFValidator)factory.getInstance("validator", "required");
customSetterScope2 = (XCFSetter)factory.getInstance("setter", "custom");
// these next two shouldn't be there
try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}
assertEquals(requiredScope1, requiredScope2);
assertNotNull(customSetterScope2);
assertFalse(customSetterScope1 == customSetterScope2);
assertTrue(requiredScope2 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
assertTrue(customSetterScope2 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);
assertNull(customValidatorScope3);
assertNull(setterWithLongNameScope3);
/////////////////////////////////////////////////////
// SCOPE 3 TEST with a bogus path
// push a new scope 3 with a non-existent package
factory.pushScope();
factory.addPackage("com.eternal.bogus");
requiredScope3 = (XCFValidator)factory.getInstance("validator", "required");
customSetterScope3 = (XCFSetter)factory.getInstance("setter", "custom");
// these next two shouldn't be there
try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}
assertEquals(requiredScope1, requiredScope3);
assertEquals(customSetterScope2, customSetterScope3);
assertTrue(requiredScope3 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
assertTrue(customSetterScope3 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);
assertNull(customValidatorScope3);
assertNull(setterWithLongNameScope3);
/////////////////////////////////////////////////////
// SCOPE 2 TEST after pop of Scope 3
// pop scope 3
factory.popScope();
// ok, get the scope 2 flyweights
requiredScope2 = (XCFValidator)factory.getInstance("validator", "required");
customSetterScope2 = (XCFSetter)factory.getInstance("setter", "custom");
// these next two shouldn't be there
try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}
assertEquals(requiredScope1, requiredScope2);
assertNotNull(customSetterScope2);
assertFalse(customSetterScope1 == customSetterScope2);
assertTrue(requiredScope2 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
assertTrue(customSetterScope2 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);
assertNull(customValidatorScope3);
assertNull(setterWithLongNameScope3);
/////////////////////////////////////////////////////
// SCOPE 3 TEST
// push scope 3 with old package
factory.pushScope();
factory.addPackage("com.eternal.scopeThree");
// ok, get the scope 3 flyweights
requiredScope3 = (XCFValidator)factory.getInstance("validator", "required");
customSetterScope3 = (XCFSetter)factory.getInstance("setter", "custom");
customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");
setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");
assertNotNull(requiredScope3);
assertFalse(requiredScope1 == requiredScope3);
assertEquals(customSetterScope2, customSetterScope3);
assertNotNull(customValidatorScope3);
assertNotNull(customValidatorScope3);
assertTrue(requiredScope3 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Required);
assertTrue(customSetterScope3 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);
assertTrue(customValidatorScope3 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Custom);
assertTrue(setterWithLongNameScope3 instanceof com.eternal.scopeThree.request.parameter.SETTER_WithLongName);
/////////////////////////////////////////////////////
// SCOPE 2 TEST after pop of Scope 3
// pop scope 3
factory.popScope();
requiredScope2 = (XCFValidator)factory.getInstance("validator", "required");
customSetterScope2 = (XCFSetter)factory.getInstance("setter", "custom");
// these next two shouldn't be there
try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}
assertEquals(requiredScope1, requiredScope2);
assertNotNull(customSetterScope2);
assertFalse(customSetterScope1 == customSetterScope2);
assertTrue(requiredScope2 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
assertTrue(customSetterScope2 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);
assertNull(customValidatorScope3);
assertNull(setterWithLongNameScope3);
/////////////////////////////////////////////////////
// SCOPE 1 TEST after pop of Scope 2
// pop scope 2
factory.popScope();
// ok, get the scope 1 flyweights
requiredScope1 = (XCFValidator)factory.getInstance("validator", "required");
customSetterScope1 = (XCFSetter)factory.getInstance("setter", "custom");
// these next two shouldn't be there
try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}
assertNotNull(requiredScope1);
assertNotNull(customSetterScope1);
assertTrue(requiredScope1 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
assertTrue(customSetterScope1 instanceof com.eternal.stubs.request.parameter.SETTER_Custom);
assertNull(customValidatorScope3);
assertNull(setterWithLongNameScope3);
/////////////////////////////////////////////////////
// NULL SCOPE TEST
// pop scope 1
factory.popScope();
try {requiredScope1 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {requiredScope1 = null;}
assertNull(requiredScope1);
}
public void testFlyweightFactorySearchPath() throws Exception {
XCFFacade facade = new XCFFacade();
SERVICE_FlyweightFactory factory = new SERVICE_FlyweightFactory();
facade.getLogManager().setLogger(XCFLogger.LogTypes.DEBUG, facade.getLogManager().getLogger(XCFLogger.LOG_TO_CONSOLE));
facade.putService(SERVICE_FlyweightFactory.XCF_TAG, factory);
// add a rule that binds validator to .request.processors.VALIDATOR_
factory.addConvention("validator", ".request.parameter.VALIDATOR_");
XCFValidator requiredScope1 = null; // exists in common package
factory.pushScope();
factory.addPackage("com.eternal.xcf.common");
factory.addPackage("com.eternal.scopeThree");
requiredScope1 = (XCFValidator)factory.getInstance("validator", "required");
assertTrue(requiredScope1 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Required);
}
}
Conclusion
In lesson one you created the concept of a facade. In lesson two you created the interfaces that will be used o model the request processing in that facade. Today, you created a class that will critical to the process you will use to build the facade. This class will greatly reduce configuration by modeling a convention that not only dictates how classes are names but also what sub-package they are placed. Because of this you have made the path of least resistence one where people will name classes in a manner where their function is easier to intuite and place them in a package future engineers will expect them. In the next lessons we will move on to the actual act of building the facade using an XML file as the director.
Entry Filed under: Software Development, XCF
4 Comments Add your own
1. Sonjaya Tandon » FR&hellip | November 5th, 2006 at 6:39 pm
[…] Lesson 03: Implementing Convention […]
2. Sonjaya Tandon » FR&hellip | November 5th, 2006 at 6:39 pm
[…] Lesson 03: Implementing Convention […]
3. Sonjaya Tandon » FR&hellip | January 7th, 2007 at 3:13 pm
[…] Lesson 03: Implementing Convention […]
4. Sonjaya Tandon » Fr&hellip | February 10th, 2007 at 2:17 pm
[…] Lesson 03: Implementing Convention […]
Leave a Comment
You must be logged in to post a comment.
Trackback this post | Subscribe to the comments via RSS Feed