Wednesday 28 February 2007

Technical Notes - Rationale

I have spent about 50% of my working life (employed by the same, large, financial institution) involved in information technology. I started out as system analyst, but fairly quickly developed an interest in software development methods, and spent the bulk of my employment there becoming knowledgable about such methods,and their implementation of them in CASE (computer aided software engineering) tools. I became an instructor in two different software development methods; was involved in method and CASE tool selection; the specification of such tools, and amendments to existing tools; and consulting on methods within the organisation. Later I was involved in developing technology strategy and research, with particular reference to the software development environment.

Over a period of about 16 years I observed software development methods change and develop, and had exposure to virtually every development in the field.

One of my interests is Advanced Squad Leader (ASL) and I am trying to develop some computerised play aids for this game. As a software development this has some characteristics that make for complexity in the development process. The characteristics that give rise to this complexity are:
1 There are a very large number of rules within the game - the rule book is about 3" thick.
2 The rules have a very high level of interrelatedness - leading to sometimes arcane debates as to what the rules actually mean.
3 The rules are very situational dependant - some rules apply in some circumstances, and not in others. The way I believe is appropriate to handle this within a software development is to use reflexion - allow objects/classes/types to learn, from the system, about their own characteristics - to change the behaviour of objects in different circumstances.
4 The rules describe an inherently complex game - there are very large numbers of object types, relationship types and process types.

This interest has led to me try a personal system development project. This project has reignited my interest in ways of developing systems, and this in turn has forced me to clarify and codify my ideas about software development. As a result I am writing myself a series of notes about software development methods, which I am going to publish as a series of technical notes as I learn lessons from my development and codify my ideas on methods.

As a minor aside, there is a modern trend to use the word methodology rather than method in discussions of the software development lifecycle. Methodolgy to me is the study, understanding and comparison of different methods; a method is the way one goes about constructing a software system. I object to misusage of the word methodology, and believe that the software development is governed by a method (whether explicit, implicit, formal or informal).

Friday 16 February 2007

De-coupling inheritance dependencies in Delphi

I had been having problems with testing a Delphi unit that fired events off and had many subclasses, when I realised that a different approach would solve the problem. The new approach solved a long-term problem that I had with my design – too much coupling between classes because of the way Delphi implements inheritance.

The long-term problem is one that applies in many places in my system design. It could be regarded merely as a theoretical issue, but a more elegant design sorts it out in many places.

In the requirements for the system, the following structure comes up repeatedly:

Several object types need to share a common interface, but each has its own behaviour. My standard way of defining these requirements is to define a category (see note below) which defines the interface. In addition I define the individual object types that have the different behaviours. The category is defined as the intersection of the individual object types. My original method of implementing this structure was to follow the suggestions in the definition of a category, to define a superclass corresponding to the category, and descendant classes corresponding to the individual behaviours. This enables common behaviour to be isolated out into the superclass.

Example
The requirements for part of my system involve the definition of structure nodes and part of the requirements for structure node is:

Thus structure node is existence dependent on the three categorised node types.

This was originally implemented in Delphi as five units, one for each specialisation, one for the abstract parent, and one for the interface of the datatype, as follows:
**********************************************************************
unit Node;
interface
uses Classes, Constants, Operations;
type
IJSPNode = Interface
function AddChild(aJSPTYpe: TNodeType = jspSEQ): IJSPNode;
....
procedure AddOperation(const NewOperation: string);
procedure Assign(Source: IJSPNode);
procedure SetChild(i: integer; aNode: IJSPNode);
property ChildCount: integer read GetChildCount;
property Condition: string read GetCondition write SetCondition;
....
end;
implementation
end.
*****************************************************************

unit NodeImpl;
interface
uses
Classes, Constants, Node, Operations;
function NewJSPNode(ATNodeType: TNodeType): IJSPNode;
type
TJSPNode = class (TInterfacedPersistent, IJSPNode, Iinterface)
strict private
....
strict protected
....
public
function AddChild(aJSPTYpe: TNodeType = jspLEA): IJSPNode; virtual; abstract;
procedure AddOperation(const aOperation: string); virtual; final;
procedure Assign(Source: IJSPNode); reintroduce;
....
end;
implementation
uses
Math, NodeAdmImpl, .... NodeItrImpl, .... NodeSeqImpl,StrUtils, Sysutils;
function NewJSPNode(ATNodeType: TNodeType): IJSPNode;
begin
case ATNodeType of
jspADM: Result := NewPositAdmitNode;
jspITR: Result := NewIterationNode;
jspSEQ: Result := NewSequenceNode;
....
end;
end;
....
end.
***************************************************************************

unit NodeItrImpl;
interface
uses
Classes, Constants, Node, NodeImpl;
function NewIterationNode: IJSPNode;
implementation
type
TIterationNode = class(TJSPNode)
public
class function SetJSPType: TNodeType; override; final;
function AddChild(aJSPTYpe: TNodeType = jspLEA): IJSPNode; override; final;
function AddChildBefore(AChild: IJSPNode; aJSPType: TNodeType = jspLEA): IJSPNode; override; final;
end;
function NewIterationNode: IJSPNode;
begin
Result := TIterationNode.Create;
end;
.....
end.
.....
*************************************************************************

There are a number of characteristics of this code, some related to my style of coding, but some forced on me by the constraints of Delphi.

The declaration of the interface is fine, but, in order to provide a class from which I can inherit I need to expose the interface of the implementing class, so that other units can see it.

The implementation of the class implementing the interface needs to know about all its descendants, which will provide maintenance problems as the system evolves.

There is a massive case statement within the factory method which needs to know about all the subclasses. This is again fragile under change.

The visibility of the elements needed purely by the demands of the implementing language presents an opportunity for developer's of the system subsequently to misuse the implementation parts from misunderstanding or a desire to shortcut some of the “clutter”.

The solution to these problems was to realise that the inversion of control did not need to be implemented by inheritance, but rather could be implemented by delegation. The categorised items do not become specialisations, but rather classes in their own right, with an internal variable of type category interface. Any methods that are implemented in the category class are delegated from the “specialisation” class to the internal variable. Any methods that would be overriden in the first solution are implemented directly in the categorised item class. This leads to a unit for the interface which is unchanged from that above, but the class definitions become:
*****************************************************************************
unit NodeImpl;
interface
uses
Classes, Node;
function NewStructuredNode: IJSPNode;
implementation
type
TJSPNode = class (TInterfacedPersistent, IJSPNode, Iinterface)
strict private
....
strict protected
....
public
....
function AddChild(aJSPTYpe: TNodeType = jspLEA): IJSPNode; /* Do nothing */
procedure AddOperation(const aOperation: string);
procedure Assign(Source: IJSPNode); reintroduce;
....
end;
uses
Math, StrUtils, Sysutils, Constants, Operations;
function NewStructureNode: IJSPNode;
begin
Result := TJSPNode.Create;
end;
....
end.
*************************************************************************

unit NodeItrImpl;
interface
uses
Classes, Node;
function NewIterationNode: IJSPNode;
implementation
uses
Constants, NodeImpl;
type
TIterationNode = class(TInterfacedPersistent, IJSPNode, Iinterface)
strict private
aStructuredNode: IJSPNode;
public
class function SetJSPType: TNodeType;
function AddChild(aJSPTYpe: TNodeType = jspLEA): IJSPNode;
function AddChildBefore(AChild: IJSPNode; aJSPType: TNodeType = jspLEA): IJSPNode;
end;
function NewIterationNode: IJSPNode;
begin
Result := TIterationNode.Create;
end;
constructor Create(....);
begin
aStructuredNode := NewStructureType;
....
end;
function AddChildBefor(....)
begin
Result := aStructuredNode.AddChildBefore(....);
....
end;
.....
end.
.....
*************************************************************************
This approach does not quite get rid of all the problems of coupling. It is still necessary for something external to this part of the system to know what type of structured node is being demanded, but this is natural in this system, so not eliminating this lingering remnant of coupling is not significant.

Naming Rationale
I prefer this name to generalisation as the latter suggests that the system is to be implemented in a particular way. This construct arises in statements of requirements whereas generalisation arises in specifications. This term was used in the KISS method by Kristen[1] and was defined there as:
Category
A category takes care of the reuse of action types by different object types, weak object types or gerunds. For a category we always identify two or more parents. For transformation to the implementation environment, the category can incorporate the generic attribute types of its parents, so the attribute types have to be specified only once. In an object-orientated programming language the category can be implemented as an abstract superclass. The category allows us to model polymorphism.
[1] Object Orientation The KISS Method from Information Architecture to Information System.
ISBN 0-201-42299-9
Gerald Kristen 1994
Addison-Wesley Publishers Ltd.