ACCU Buttons

Letters: Encapsulate Context

Encapsulate
Context

Mr. Griffiths,

When I read the title and abstract for the pattern, I thought it
might be really useful. Instead, this Pattern is simply a global
wrapped in a shroud. I would like to say, at the outset, that I do
believe there is a place for global variables (std::cout being an excellent example). I also like
the concept of contextually global variables - variables that are
global within a given context. To make my argument I offer the
following:

There are two ways a variable becomes global within a program:
1) it is intentionally declared global, or 2) it is passed to every
function as a parameter.

The ENCAPSULATE CONTEXT pattern suggests that there is a single
variable that should be passed to all functions within a given
context, effectively making the variable global (via A.).

The ENCAPSULATE CONTEXT pattern as written, does not build a
context into the variable, but rather provides a global that can be
provided within context. There is nothing to define or constrain
the context under which variables within the structure should be
accessed.

In short, the functionality provided by the ENCAPSULATE CONTEXT
pattern as written can be easily achieved by declaring some global
variables in a namespace that is shared by the functions that need
access to these variables. This "solution" is far cheaper, more
effective, and clearly delineates the level of coupling the
functions share, whereas the ENCAPSULATE CONTEXT solution as
written only serves to obfuscate the level of coupling, not
mitigate it.

Were I a sufficiently adept programmer, I would propose an
alternative to the ENCAPSULATE CONTEXT solution as written. Sadly,
my skills do not lie in this area and it would take me ages to
accomplish this task. At best I can offer some design
suggestions.

It strikes me that the container which "collects data together"
needs to do more than simply provide a pointer that can be passed
around on the parameter line. It should have a built in hierarchy
of contexts that allow functions to pass not simply the container,
but also information about the context under which the data should
be accessed. By providing the context (albeit dynamically) it is
possible to limit the scope of the variables that are accessible as
the context tree is traversed.

Taking the straw man stock exchange trading system that Allan
Kelly used, I would see the calls to the functions something like
this:

the constrain function from within
context should look something like:

MarketContext * MarketContext::constrain(
ContextLevel level);

In this case, the parameters to context.constrain() define the highest point in the
hierarchies of data stored within the context to which the function
should have access and the call returns a pointer to a variable
which has been so constrained (I can envision several objections to
this particular implementation, but I hope that the idea is clear).
The hierarchy within the context
container might look something like:

Within the hierarchy there might be variables associated with
that point in the hierarchy ... in run_time_data/log/error_log
there might be a lock that is used to lock the file while a set of
error messages is being written, the file pointer, etc. It is worth
noting that you shouldn't be able to pass a context above your
current context. Thus, if you had received the container with the
context level of log you would be able to
pass on log, error_log, or transaction_log, but not parms or run_time_data.
Maybe all of this could be done with templates somehow.

This would encapsulate the context and refine how generally
global variables might be accessed. A function with access to "log"
might not have access to "parms" and this would help with
decoupling. It strikes me that this is still a long-winded and
complicated way to achieve the same thing as globals within a
namespace. Given your concerns, however, that it might be good to
be able to recover from the state in which you find the application
rather than simply rewriting it so that such coupling is either not
needed or explicit, this may be a reasonable approach. Again, I
wish I had the necessary skills to adequately program such a
structure, but my "back of the envelope" efforts have not come to
fruition and I'm wondering if I have grasped the whole of the
problem or just a piece.

As a final note, I would like to say that the article was clear,
well written, and was on topic for Overload. I do feel that the
solution given represents a "bad programming practice" but I
readily concede to those more learned in this area then myself. I
also think that the solution given is a very complicated way to
ignore namespaces for no particular gain and some loss.

Sincerely,

William Fishburne

Allan's
Reply

Dear Editor,

I'm rather surprised by the amount of attention my little
pattern, ENCAPSULATE CONTEXT, in Overload 63 has generated.
However, I regard this as a good thing.

I'm a little disappointed that I was not given the opportunity
to respond to Phil Bass's points in the same issue of Overload as
his letter appeared. Phil's technical points are all valid, the
problem is: how do we balance all these forces? That is the problem
that ENCAPSULATE CONTEXT addresses.

Phil is also concerned about the resulting coupling. I too am
concerned about this and would draw his attention to the Solution
and Consequences sections. These deal with some of the problems
which can arise when this pattern is used - or misused. This paper
does not claim to be a solution to every programming problem. Like
any other pattern this one may be useful sometimes and not others.
If a system can be partitioned to avoid this problem then great, if
not, then this pattern has a use. Unlike many other patterns this
one knows its limitations and highlights these to the reader.

Readers must decide whether this pattern stands or falls.
However I am more concerned about a non-technical point Phil makes
in his letter. He states "mention of Kevlin Henney, Frank Buschmann
and EuroPLoP gives the article an unwarranted air of authority."
Let me say that these people are mentioned not to aggrandise the
pattern or myself but to acknowledge their assistance in creating
the pattern. I am most grateful to these people - and the others
mentioned - for all their help. It is in the culture of the
patterns community that such assistance is acknowledged.

In no way did I hide anything from these people, there was no
attempt to pull the wool over their eyes and slip a dodgy pattern
past them. The final choice of words may have been mine but this
pattern wouldn't be what it is without the review and comments of
others.

These names were not mentioned to create a false authority for
the pattern. If the pattern has authority it comes not from mention
of these names but form the fact that others have reviewed it on
multiple occasions. (In fact, I find it hard to think of any other
piece of writing in Overload that has been reviewed as much as this
- and that was before the Overload editorial board got to see
it.)

Moving onto the comments from William Fishburne. First, let me
encourage William to write up his thoughts. I'm sure his skills are
up to the job.

Reading William's comments I take two main points. Firstly his
suggestion to use a namespace as part of the solution. What he
describes sounds like the MONOSTATE pattern - otherwise known as
THE BORG. This pattern has its uses but - as the alternative name
suggests - it too has problems.

The second solution is to constrain the parameters passed to a
function. This may well be a solution in some contexts, but in the
context taken by ENCAPSULATE CONTEXT it is not. In ENCAPSULATE
CONTEXT we wish the caller to remain ignorant of what is being
passed down. This is a deliberate selective ignorance. While this
approach introduces compile time coupling the coupling is less than
would be introduced by either global variables or an extra
parameter in the function signature.

We could remove this coupling were we to use dynamic members
within the context, this is the approach taken by Patow and Lyaret
in PARAMETER BLOCK pattern (another pattern presented at EuroPLoP
2003.)

Both Phil and William describe my example as a straw man. To
some degree this is true, like any example it is an abstraction
which ignores some elements, however, I can assure them it is not a
straw man example.

The example given comes from a very real system. Two
explanations are therefore possible:

Option #1:

A better design was possible that would have avoided this design
situation. Undoubtedly other designs where possible, would other
designs have avoided this problem? I don't know. I do know that
this solution troubled me as it emerged; this was the trigger for
deeper investigation and ultimately writing the pattern.

More importantly, like any design this evolved, knowing what one
knows at the end of a design one might not always chose the same
design again. However, I am convinced that given the constraints at
the time (knowledge of domain, time to market, programming
interfaces, experience, etc.) this was the best design
possible.

Option #2:

Simply that I am a poor designer. I will let readers decide this
for themselves but I will note that others have come to similar
conclusions in similar circumstances.

Finally, I welcome this debate, it's good to hear other views
and it demonstrates that software development is not black and
white, different opinions exists and always will. I look forward to
hearing more comments and opinions.

Alan's reply
to Allan

Allan is right: if a "letter to the editor" had expressed the
views that Phil propounded then I would have sought a response from
the author. For that I have to apologise, both to Allan and to the
readership. Sorry.

Phil's comments caused discussion within the editorial team -
not so much because of the views expressed, but how to deal with
them. Given Phil's position as one of Overload's advisors his
comments needed different treatment to those of most
correspondents. Especially as the main point was regarding
editorial policy: should material that an advisor has concerns
about be published without warning?

My experience with the solution Allan proposes is that a number
of problems experienced on the project that I employed it on were
resolved without unexpected effects. Hence, I didn't feel it
appropriate to give any warning beyond those Allan himself
provides.

I trust that Overload readers are sufficiently sophisticated to
make up their own minds about both the validity of Allan's pattern
paper and also about Phil's concerns regarding publishing it.

Alan Griffiths (editor)

Developing a
Pattern

Dear Editor,

I was a little surprised and more than a little worried by the
comments made by Phil Bass (included in the editorial of Overload
64) on the ENCAPSULATE CONTEXT pattern by Allan Kelly (published in
Overload 63). They were quite strongly against Allan's article,
which is a personal preference anyone is allowed to express, but
for reasons that I felt were ungrounded, and which undermined the
value of those comments as a review.

The first concern I had was with Phil's question over the
validity of the pattern. Given Phil's experience and the way that
he has approached design in his articles, this is surprising. The
solution outlined in the pattern is both valid and good for the
problem it addresses, and a standard tool in the toolkit of
experienced OO developers. For the record, and to assuage Phil's
concerns, it can be found in a number of well-designed systems; it
can also be found missing in several systems that are not so well
designed, where instead single, fixed points of contact (globals,
SINGLETONS, MONOSTATES, etc) are employed or long, unstable and
tedious argument lists are passed around.

Unifying sets of distinct items, such as all or part of an
argument list, that are bound by common use or an invariant is one
of the diverse range of techniques OO developers use to identify
object types. Phil considers such a use to be trivial, which
appears to go against much of the accumulated wisdom on identifying
stable elements in a design. Such normalisation is a common and
accepted practice, whether carried out explicitly or
intuitively.

The second concern I had is concerned with a fundamental part of
the pattern concept. A pattern is not an unconditional piece of
design advice, a blanket recommendation that covers all designs.
Its applicability is very much dependent on context and the
acceptability of trade-offs involved in applying it. A robustly
written pattern will make clear that it is not a panacea,
publicising its benefits, liabilities and alternatives quite
openly. This is a point that Mark Radford made clearly and well in
his editorial of Overload 63. Hence the reason for my surprise:
Phil's comments suggest that he views the pattern as unconditional
design advice that has neither a discussion of consequences nor a
discussion of techniques involved in applying it, such as
partitioning the context. I don't believe that this is the way that
Phil actually views patterns, but that is the message that comes
across in this instance.

Allan's pattern is quite clear in its caution, making explicit
the many design decisions and alternative paths that would lead to
or away from encapsulating the execution context of an object. Phil
does not seem to have picked up on some of the other points made in
the paper, such as partitioning the execution context. He proceeds
to misapply the pattern in his discussion, and then claim that it
is the pattern that is broken and not his reading of it. It is
important to recognise that the pattern does not unconditionally
propose that there be only one context type or context object (or,
to pick up a previous point a conditional application, that context
objects are needed in all system designs). To claim that the
system's coupling will rise if Encapsulated Context is applied both
misses and makes the point: employ the pattern to reduce the
coupling rather than increase it; if that doesn't work, do it
differently or do something else. If Phil does not wish to apply
the pattern, I have no problem with that; if he has been unable to
evaluate it on its own terms and is cautioning others that it
should not be used at all, I question that.

The first two concerns are technical in character, and are the
stuff of lively technical debate, whatever views we hold. However,
the third concern I had was more ad hominem in nature. Phil's claim
that "the mention of Kevlin Henney, Frank Buschmann and EuroPLoP
gives the article an unwarranted air of authority" is an
inappropriate claim that potentially insults all those involved -
Allan, Frank, me and the EuroPLoP workshop participants who offered
Allan feedback on his paper.

In his prologue and his acknowledgements section Allan offers an
insight into the history and evolution of the paper. In recognising
that it has evolved, and the journey taken so far, he mentions
those who have contributed in some way to the paper. That is part
of telling the story of the paper, but it is also why
"acknowledgements" sections are so called. Our names were not
picked out of thin air to aggrandise Allan's work: we offered Allan
feedback in one form or another. It is generally considered polite
to acknowledge such contributions. Such acknowledgements are also
part of the cultural expectation for pattern papers.

It is perhaps worth understanding a little more about the
process that surrounds the reviewing of many pattern papers. A
shepherd is someone who offers comments structured as iterative
feedback with dialogue, with the goal of giving the author a
different insight into their paper and the means to improve it.
This is the role that I took on voluntarily when the pattern
originally came up in discussion in 2002. At this point Allan was
working on the paper without a distinct publication goal in mind.
Allan decided to submit it to EuroPLoP 2003, and at this point
Frank Buschmann took on the role of the shepherd. Papers are not
accepted for the PLoP family of conferences without some amount of
shepherding and acceptance by the shepherd. The shepherding is
intended to improve the paper to a point where it can be opened to
further structured feedback at the conference in the form of a
workshop, which is where Allan received further suggestions for
improvement. Publication in the EuroPLoP proceedings is not a mark
of perfection, but is a visible outcome of the reviewing process,
and to reference it is a statement of fact rather than an appeal to
authority.

Given the depth and breadth of the review process, it seems only
good manners to include a list of acknowledgements. And, given the
common characterisation of a pattern paper as a work that is always
in progress, it is possible that Allan will take Phil's comments on
board and address them in some way. In such a case, it is also
likely that Phil's name will appear in the list of
acknowledgements, because in one way or another he will have
contributed to improving the paper.

Phil's
Response

Dear Editor,

The only comment I really wish to make is that I apologise
unreservedly to Allan, Kevlin and anyone else who feels insulted by
my remarks. When I said that "the mention of Kevlin Henney, Frank
Buschmann and EuroPLoP gives the article an unwarranted air of
authority" I chose my words badly. I still believe Allan's article
over-states the value of this "pattern", but I never intended to
question anyone's integrity.

The real technical issue, here, lies with Allan's initial
premise that "A system contains data, which must be generally
available to divergent parts of the system". That is a description
of a problem. ENCAPSULATE CONTEXT is a sticking plaster that can be
applied if you wish, but it doesn't begin to tackle the problem
itself. What we should be doing is analysing the system's design
with a view to removing (as far as possible) the need for such
data.

C++ Lookup
Mysteries

Dear Editor,

Sven Rosvall's "C++ Lookup Mysteries" in Overload 63 couldn't
have been better timed as it provided a solution to a problem I had
been struggling with - a test harness that failed to compile after
a new feature was added to the main product because of C++'s
non-intuitive name lookup rules.

The problematic code, trimmed to the minimum to illustrate the
problem, was:

This works fine for standard containers containing either
built-in types or user defined types that define an output operator
in the same namespace as the type is defined. The code that broke
the test harness was a standard container of std::pair. The obvious solution, defining
operator<<(std::pair) in the test
harness namespace, didn't work because the compiler cannot "see"
this definition. The problem is that operator<< is already defined (for the
built-in types) in namespace std and masks
my definition, as Sven explains "Firstly, the nearest enclosing
namespace is searched for 'entities' with the same name.
Note that as soon as a name is found the
search stops" (my italics). C++ name lookup says that
the only place that the compiler will look for operator<<(std::pair) is in std.

except that adding declarations or definitions to namespace std is undefined behaviour according to
the standard (Clause 17.4.3.1).

Of course the name lookup will find operator<<(pair) in the test harness
namespace for a pair also in that
namespace. The standard fully defines std::pair (Clause 20.2.2) so I can copy the source
code to define my own pair (in my
workspace) and expect identical behaviour. Although legal, this has
a number of problems:

It breaks the rule of least surprise. A future maintainer may
wonder why there are two identical pairs
in different namespaces.

It requires the main product source code to use this new
pair, and thus adds a dependency on the
test harness code.

Although the two pairs are binary compatible, they are not
interchangeable in source code without considerable scaffolding,
and even then not fully.

To force the name lookup to find my operator<<(std::pair) without duplicating
std::pair, I took Sven's wrapper class,
PrintSpannerNameAndGap, and made it into
a template class and output function: