Saturday, April 30, 2005

Contexts #2

One more iteration on the subject of contexts. But first, let me recap what I said so far: we have a certain type of variable marked as "contextual". When someone tries to read such a variable, the runtime checks to see if the variable's value has been set. If not, it finds the closest contextual parent that has this variable set, and uses it.

There could be two basic types of contextual parents: structural (i.e. the component has a pointer to a contextual parent) or caller (the calling method is the contextual parent). I've stated that we have to declare each contextual variable as a separate type, but it wasn't too clear what the declaration's scope would be. Let's clear this out and modify the philosphy a bit.

I tried to come up with a simple real-life example for contexts, but was unable to find one. While the usage of contexts could be broad (and there are already many concepts that are similar or equal to them - like ambient properties in windows controls, for example), they are mostly applicable in more complex situations. And complex situations don't make good examples.

One interesting application for contexts could be the Visual Studio .Net's design-time functionality. For those who aren't familiar with it, let's say that in Visual Studio's development environment the controls and components get instantiated the same way as in the runtime, but need to - and do - behave somewhat differently. Let's say we are developing a multi-tier application with such functionality in mind: we have a number of windows controls that communicate with the business logic layer, and the business logic communicates with the database-access layer. But, in design-time we want the database layer to behave like a stub component, i.e. do nothing. We signal that we're in design mode by setting to "true" the DesignTime contextual property on the windows form we're working on. (I know this works differently in Visual Studio, but let's pretend we're making an alternative system the way we want it).

Now, a database layer method needs to access this contextual property, and we definitely don't want to carry it around through method arguments. Furthermore, we don't want the value to be static because we want to turn on the database access when we need it (for example, we want to access the database from a list control to be able to automatically generate its columns).

It could work like this: Database layer classes would declare the DesignTime contextual variable as a calling-context type variable. Which means that they would inherit its value from the calling methods. The business logic layer classes would do likewise, and inherit the value from the controls that call them. Now, the controls have a hierarchical structure and their variable should be of the structural-context type. Thusly, a control would inherit the value from its parent controls, which would inherit the value from the form. Graphically, it could look something like this:

Note that the green arrows represent calling-context parent relations and the red represent structural parents. Comp1 and Comp2 are data-layer components.

So it looks like this is the solution: let each component declare its contextual variable and decide whether it will implement it using structural or calling hierarchy. Note that calling-context variables should probably behave like static properties, because they are not bound to objects.

Having all this in mind, we have several questions to answer:
  • Should we declare calling-context variables inside methods? They are obviously not bound to objects, but nevertheless there could exist an option to declare them at class level.
  • Could one class support both a structural hierarchy (at class level) and caller hierarchy (in some of its methods)?
  • Could a structural context be declared to use a static variable for parent context reference?
  • How do we know contextual variables on different classes have the same meaning? Only if their name is the same? Probably a separate contextual type needs to be declared?
  • Is there really no use here for .Net attributes, they look so cool?

Let's try to answer "yes" to all these questions, including the last (yes, there really is no use for attributes). Like this:

// declare a contextual variable type
public context bool DesignTime;

// a data layer class supporting this variable in a caller-
// context fashion
public class Comp2
protected DesignTime(Context.Caller);

public void AccessDatabase()
// access the database...
// just pretend you did

// a class with structural support
public class MyUserControl : UserControl
protected Control Parent;

// structural context - the structure is
// traced via the Parent property
protected DesignTime(Context.Structural,

// a class with mixed caller, structural and
// static support
public class SomeClass
protected static object ContextualParent;

protected static DesignTime(Context.Structural,

public void SomeMethod()
DesignTime DesignTime(Context.Caller);

// ...

I haven't yet figured out a really elegant way to declare and consume contextual variables. Maybe in a later post...

Ok, now how about a more complex scenario? What happens if components in the data layer have a structural hierarchy, so there's more than one hierarchical path to choose from? Like this:

In this case, is it really good not to be able to dynamically determine whether structural or calling context would be used at one time? I think it probably is. If we need to have the same contextual property work differently at different times, I'm not sure but it may be a sign of bad design. I think it's cleaner to have the context variable's behavior decided statically. It seems simpler anyway.

Some additional notes:

  • These two context types (structural and caller) are probably not all there is, so there should be a way to declare one's own logic for them. The philosophy could possibly be borrowed from .Net's delegates (or whatever their equivalent in Java is).
  • What about components that don't declare support for the contextual variable? When traversing the hierarchy, they should simply be skipped. If a component acts as a crossroad from structural to calling hierarchy (like the DbControl in the illustration), it has to have the context variable declared. And to bring the clutter to a minimum, AOP should probably be called for help.
  • Could there be contextual methods? Possibly, yes, but they would need to behave differently than variables. While reading of a contextual variable triggers the search through hierarchy to the point where the variable's value has been set, accessing a contextual method would probably trigger a search to the point where the method has been implemented. Which probably also means the method would have to be a part of an interface.

No comments: