Recipes and Building
Building objects is one of the core purposes of MONDO and the building stage provides the functional definition of what Recipes are and how they relate to DomainObjects. This is where the concepts of Recipes and DomainObject become formalized in structures, interfaces and protocols.
Recipes and the building stage are conceptually very simple and natural. The first section of this chapter will cover the simpler concepts. But the building stage also has a great deal of flexibility and potential for sophistication. This is because it needs to be able to handle building complex domain models and interpreting complex recipes. Later sections of the chapter will begin to cover the sophistication and later chapters and other MONDO documents will cover the details.
Recipes for building objects
A recipe describes how to build a collection of associated objects. All the information that is placed into the Objectbase by MONDO is the result of building recipes. By formalizing recipes we separate the encoding of information (e.g. whether it is human readable and how to parse it) from what information is in the encoding. MONDO uses that information to construct the knowledge in a form we want to work with, the DomainObjects.
|
Each instruction of a recipe will build a single domain object, which can then be used as an ingredient to other instructions. Instead of being purely linear, recipes are hierarchical, with each instruction potentially containing other instructions. The hierarchy is important because an instruction uses only the ingredients from its sub-instructions.
|
Recipe
Instruction
Instruction
Instruction
Instruction
Instruction
Instruction |
|
To identify the ingredients needed for an instruction, they are named. When all the ingredients have been built, an instruction can be executed and build its DomainObject. This continues until the whole recipe has been built.
|
Recipe-For-Dinner
appetizer = Instruction
dessert = Instruction
icing = Instruction
crust = Instruction
entree = Instruction |
|
The separation of the term recipe and instruction can now be seen as one of role; Instructions are simply "sub-recipes" within the current recipe. So we can simplify our model to just having Recipes with ingredients that are built with other Recipes.
|
Recipe-1
appetizer = Recipe-2
dessert = Recipe-3
icing = Recipe-4
crust = Recipe-5
entree = Recipe-6 |
A Simple Recipe
The simplest recipe is one that requires no ingredients. An example would be creating a Date for today. We should only need to tell the builder to execute the recipe for a Date and it will build one for today without needing any other information.
Domain Side Effects
|
For each recipe the Builder will only return a single object, but multiple other objects could have been created and modified within our Objectbase as a side effect. For example, our Date object may be implemented to hold onto a day and year as an integer and a Month object to remember the month. Or the date object could store the date information in the original String that it was given. Or it could simply store a number of milliseconds from a specific date. A final possibility is that no object was created at all. The Builder simply returns a preexisting object from the Objectbase that satisfies the recipe. This will be one of the most important behaviors of the Builder to support complex information.
|
 | At the moment we do not care about which variation we have. Foremost we do not care because Objects encapsulate their representation inside the features they provide. As long as the Date acts like a Date (e.g. can tell us its month, the day of the week, how many days left until Christmas, etc.) we do not need to worry about its implementation. In all of these cases we are getting exactly the same Type of object back, only its implementation has changed.
The second reason we do not care is that we are currently focused on just the information that is needed to create an object (a piece of knowledge), not its subsequent responsibilities. These responsibilities depend on the DomainModel within the Objectbase. This is important to the applications that use the Objectbase but the recipe and the building process are identical.
Using Ingredients
After building a recipe without any ingredients, the next step is to add one. The following recipe has a Date with a parameter called iso that has a value of the recipe for a String.
To build the date, the client would first call the builder with the recipe for String, which would cause a domain object for the String to be constructed.
Now that there are no more ingredients to be built, we can send the Date recipe with its single parameter.
This results in the construction and return of a Date object to the Instructor (the client of the builder).
At this point, the recipe is complete and the Objectbase has (possibly) two new objects. But as you can see from the model, a Date can use a formatted String for construction but a Date does not need the String to remember its own value (e.g. it may simply use a number). So after this second construction is completed the string is "Garbage" and may be collected by the environments GarbageCollector. So we are left with a single object (that we know of) in the Objectbase, which is passed back to the instructor.
Multiple Levels of Ingredients
More complicated recipes have exactly the same process: each recipe will be built after the ingredients for it have been built. We can start with:
After four builds we will get:
And the final recipe building will produce:
The result of the complex recipe is the same: a single object is returned to the instructor and one or more objects are created within the Objectbase.
Types of Builders
All ObjectBuilders use exactly the same process as discussed above: they all provide the same interface, respond to the same protocol, and have the same goal (building the Objectbase). But the recipe building process provides an enormous amount of flexibility and levels of sophistication. Builders can be designed to do nothing, they can build simple data structure, or they can build very complex data structures that may require a recursive expansion of recipes. Most builders live in the middle ground of complexity: they are semi-customized by using Factories for the types of objects that need to be built. The next sections will describe the progression of builders from simple to more sophisticated.
Utility ObjectBuilders
At the simplest level we can have builders that do almost nothing: they either do absolutely nothing or they print tracing or timing information. These types of builders are mostly for utility. They can be useful for bringing a system up or for monitoring its performance.
|
UtilityBuilders rely on the complete flexibility of ObjectBuilders to determine where a DomainObject for a recipe comes from. Usually each recipe constructs one or more objects, but the UtilityBuilders choose the ultimate simplification: all recipes return a single object, the NoValue object. It does not matter what the recipe is or what ingredients it has, the resulting object will be a NoValue object.
|
 |
|
 | This is obviously not a very useful ObjectBase for an application to work with, but the ObjectBuilder can provide useful maintenance information. We may want to time the parsing stage as a whole or examine the series of recipe calls that are made.
MONDO: Building a Date {iso} [time = 0.001]
MONDO: Building a Date {iso} [time = 0.002]
MONDO: Building a Period {start,end} [time = 0.004]
The NoValue Object
The NoValue Object is useful for many purposes other than utility builders. It can be used with any recipe to indicate that there is no resulting value. The recipe may have changed the state of the ObjectBase but there is no result that should be used within the containing recipe.
|
For example, this would apply to the List recipe to the right assuming the "<NoValue>" recipe generates a NoValue object. The resulting List would only have two objects in it: The first and last <Date> recipe. The middle ingredient to the List is ignored when building the List
The actual object used for the NoValue object is up to the DomainModel and because it need have no behavior it can be almost anything. The important part is that it has a unique identity. Some DomainModels may choose to use a standard null value indicator for the language ("null", "nil", "Void", etc.) but usually the NoValue object should be different from the null value so the two roles are seperated.
|
 |
Simple, Static ObjectBuilders
The next step up in building sophistication is to build just one or two types of objects from any recipe and to have a builder that will always generate the same type of ObjectBase DomainModel. All recipes are viewed as building uniformly simple data structures.
Recipe Builder
The simplest interpretation of a Recipe is that it builds a Recipe: a data structure representing the information in the recipe. This is the transformation of a virtual recipe that is being described through protocol between the BuilderClient and the ObjectBuilder into an actual Recipe object that resides in the ObjectBase.
The Recipe Structure
|
The structure of a recipe is very simple: it has a name and it has a collection of zero or more parameters. Each parameter has a name and a recipe for how to build that ingredient. The core recipe structure is represented by the DomainModel to the right. The DomainModel implementation would implement the general Recipe and probably also include special Classes to make storing the "primitive" String recipes and the List recipes easier.
Taking our Period example we would get a recipe ObjectBase of:
|
 |
Uses
Building a Recipe ObjectBase could be useful as a utility program. You can verify whether the structure is as you expect from the recipe and you can easily view (report on) the hierarchy. The Recipe structure is also potentially useful for very simple applications. For example, recipe objects could support representing HTML or other simple document structures and doing simple textual transformations on them. Recipes could even support more complex transformations but the application would have to determine what to do based solely on data (the name of the recipe and its parameters). Using a Visitor or Walker & Strategy might be sufficient in spite of having a very simple DomainModel.
But the most interesting and powerful application of recipe objects is to allow the ObjectBase and the Builder to hold onto recipes that can be used with future building. This is usually because you want to build a recipe in a context different from where you are first encountering it. That might be just slightly later in the current "RecipeStream" or it could be in a completely different stage or time. We will wait to discuss the details of this until we get to more advanced builders.
SimpleObject ObjectBuilders
|
A slightly more sophisticated DomainModel than the recipe model would be to keep the basic data structure as a recipe but to have more capabilities to interact with that data. This might include knowing that a particular parameter (or parameters) is special and is the main structure of the recipe that Visitors should walk along. It could also be that a particular parameter is always a List [we will cover validation later] and can be treated specially because of that.
Referring back to the different modeling approaches, this would be like adding functions on top of a U-ADT. We have not really built a more sophisticated model but we have added intelligence to it that is application independent for certain uses. We have increased our abilities to interact with the model at a higher level.
|
 |
Static SGML Element and Grove Builders
SGML/XMLs Element model and the Grove model are also very simple models if you do not consider the DTD metadata and you do not want to treat elements differently (e.g. based on their GI). To discuss an SGML Builder in the context of simple builders we will ignore the DTD aspects and just concentrate on elements and attributes that describe the document itself. Later sections will show how to produce a richer model out of the different elements and also to bring the DTD information into the ObjectBase in multiple different ways.
A standard SGML document has two primary types of Recipes. Recipes for Elements and recipes for Attributes. Elements will form the main core of the Recipe hierarchy. Attributes can only be leaves off of an Element. Attributes are also "simple". They will only be Strings and Literals (and possibly Entities that resolve to strings and literals). Elements, on the other hand, can have two different types of "parameters". One type of parameter has only a single member called "content". The "content" parameter will always contain a List of sub-recipes, and this list can contain zero or more Strings (character data) and Elements. The other type of parameter is an Attribute parameter and can have any name. This is a close representation of the SGML Element structure onto MONDO Recipes.
If we want to produce a DomainModel similar to the SGML Element structure we will need to identify the different types of Recipes and Parameters, build the right types of objects, and collapse certain objects into the structures of others (e.g. the content List into the Element). This is certainly far less simple than our previous examples, so this ObjectBuilder will be the most sophisticated version of the Simple Builders. The ObjectBuilder still produces very few types of objects in the ObjectBase and it is still completely static: the Element Builder will always generate the same Types/Classes of objects as it was initially programmed to do.
|
Switching to a Grove model and a GroveBuilder does not change the sophistication or structure of the builder itself. It still needs to recognize the different types of recipes and parameters. The difference is the type of data it generates. The GroveModel has the information modeled more uniformly and somewhat cryptically. A simple version of the GroveModel might look like the information model to the right.
|
 |
Factory-Based ObjectBuilders
In all the previous ObjectBuilders, the ObjectBuilder did exactly what it was originally programmed to do. It might be possible to create a new ObjectBuilder from an old one (e.g. by inheriting from it) but there was no explicit flexibility described in the ObjectBuilder itself. Although these are useful for certain tasks that have well defined and generally useful goals (e.g. the Recipe Builder), most applications will want an ObjectBuilder that can be easily configured to their particular ObjectBase and needs. Their are several ways to support customization but one of the most flexible is to associate Recipes with Factories.
Factories
A Factory is simply an object that can build other objects. That makes it similar to the Builder itself, but a Factory has a more restricted focus. Factories only need to build a limited number of types of objects and they only need to support the Builder-to-Factory interface instead of the full Builder interface. The limited focus of factories makes implementing them easier.
More importantly, it encourages reusing previously built factories because a previous factory may do everything you want for a particular type of Recipe and DomainModel. Factories can be viewed as providing Builder-oriented interfaces onto an ObjectBases construction services. The Builder is simply configured to use whichever factories are needed to build the appropriate domain objects.
If your DomainModel is mostly like the SGML Element model then use an SGML Element Factory as the default factory and use a custom factory for the Recipes of particular interest. Or you could ignore (filter) recipes by associating them with NoValue factories.
Determining the Factory
The simplest and most common way to determine a Factory for a Recipe is to have a map of recipe names to Factories. This approach provides enough flexibility to hand most recipes and information models. When a builder is set up you can determine what recipes like to which factories and completely configure the behavior from very general (just a few simple factories) to very specific.
This approach is the primary approach assumed within the Functionality Levels of MONDO and the core recipes (i.e. MONDO:RecipeName). There are times when the selected factory will depend on information other than just the full (namespace resolved) recipe name, but these are rare.
Advanced Factories
Some factories may be extremely sophisticated about how they produce their objects, but from the outside they will be just as easy to use. As a conceptually simple example, consider a factory that builds objects by starting a new building flow (Parser->Builder->ObjectBase) for the same ObjectBase. The only ingredient the factory needs is an appropriate TextStream to the document and the result is just an Object and the modifications to the ObjectBase.
With this very simple factory we get a tremendous amount of functionality. We can partition recipes into different files without any semantic effect on the building process. Whether a recipe comes in through the main read or needs to be fetched from a URL during the recipe construction makes no difference.
Importance of Factories
Factories are a core element to the full functionality of the MONDO architecture. They make it easy to create very powerful ObjectBuilders that can be tuned to a particular task. Using Factories with the building process provides exceptional encapsulation, reuse, and flexibility.
Still, the MONDO architecture is flexible enough not to require using Factories when they are not appropriate. The builder interface makes no mention of factories, so no component outside of the builder is coupled to them. And some builders simply do not need a lot of tools that they can piece together (e.g. the Static and Utility builders).
The factory concept is so useful that they play an integral part in the specifications of the Functional Levels of MONDO beyond the core architecture. Most of the higher level ObjectBuilders are designed to work with factories and very few recipes need different behavior. The specification of the factories provides a specification of the possible behavior during building. The actual syntax of mapping recipes to the factories is always left up to the application and to the recipe itself (metadata).
Chapter Unfinished
Standard Builder Functionality
Recipe-Info (associating parameters with recipes)
<!RecipeAnnotation
name="recipeName"
parameterName = "scheme"
parameterValue = <OmlParser source = <File url="http://www.chimu.com">>
>
<!RecipeAnnotation
name="recipeName"
parameters=(<Parameter name="scheme" value=<
see OmlParser
> >)
>
name spaces (recipe name expansion accomplished by annotation),
symbolic recipe expansion (define),
<!Define name="&MONDO" value="MONDO: A General Architecture
">
delayed recipe expansion
<!Recipe{<ARecipe that=<Will> not=<Be{Expanded}> ARecipe>}>
Foreign subrecipes (i.e. building an object from a different file, url, stream, etc.)
<OmlParser source = <File url="http://www.chimu.com/"> >
["!" expands to "MONDO:" as a "namespace"]
More advanced builders
[Metadata returns to DomainModel]
Validation
|