December 29, 1997 - Jack Harich - happyjac@mindspring.com
As we architect and build the Bean Assembler (BA), is has become obvious we need to review the high level requirements of what a good BA would try to accomplish. Attempting to identify the principles of assembling a good system from parts seems the right place to start. It certainly appears that something as radical as the BA needs a fresh foundation.
Just as Structured Programming greatly improved individual programs, Structured Assembly (SA) improves systems assembled from parts. The goal of SA is "good system structure". A system having good structure will exhibit these traits:
- Usefulness, to all types of users, including developers
- High understandability
- Consistency, including no runtime dependency failures
- Continuous change is supported as a routine system feature
- Architectural soundness and automatic enforcement
It follows that a good SA tool will provide "good system structure" at all times, plus it should make the process efficient and enjoyable.
It also follows that SA principles can be derived from the pursuit of the above goals. To put this in perspective, lets review the principles of Structured Programming, originated by Edsger Dijkstra in 1969:
- Sequence - A set of statements executed in order
- Selection - A control structure causing statements to be executed selectively
- Iteration - A control structure causing statements to be executed multiple times
Another similar contribution was Structured Design, first described in 1974, and made up of these practices:
- System organization - Systems are organized into black boxes, routines that have well-defined, narrow interfaces and whose implementation details are hidden. This has come to be associated with a preference for encapsulation, high cohesion and low coupling.
- Strategies for developing designs, such as top-down decomposition and bottom-up composition.
- Criteria for evaluating designs.
- A clear statement of the problem to guide the solution.
- Graphical and verbal tools for expressing designs, including using certain diagram types.
As you can see, Structured Design was not nearly as focused on principles as Structured Programming, but attempted to provide a process or smorgasbord of best practices. It made valuable contributions but failed to take root, since no one process has yet proved to be superior. Thus as we develop the theory behind Structured Assembly we should be careful to stick to principles, and eschew sneaking in process or best practices. If these are needed then that should be a separate topic, since they are highly subject to change and appropriateness. Principles, on the other hand, tend to be stable and highly appropriate.
"System" here means a discrete collection of objects that perform a particular mission. We exclude large, complex systems such as enteprise IS systems, the WWW, operating systems, etc. We are more concerned with applications and collections of applications.
Principles
Organization
- A system is best composed of only 2 types of items - containers and workers.
- A system is best organized as a hierarchy of containers.
- A container contains items, which are other containers and/or workers.
- Larger systems may be composed as a hierarchy of subsystems.
Collaboration
- Items collaborate via events, not methods.
- Workers can only reference other items in their container.
- Privileged workers also have access to their container's services. Minimize these.
- Systems collaborate via events handled by their root containers.
User Expression
- A system is assembled by:
- Adding, removing, copying, pasting items to/from a container
- Configuring items in a container
- A system can be assembled while running or not running.
- A friendly system helps the user understand and assemble it.
Corollaries and Observations
- Organization + Collaboration principles = a suitable Control Structure.
- Each system, container and worker should be designed for reuse.
- An empty system has a single, empty root container.
- Items must be designed to support live assembly from the start.
- A consistent system enforces its own integrity at all times, particularly during assembly.
- Item names are unique per container.
- There will be legitimate needs to collaborate using methods. Implement this by letting one worker be a property of another. This is tighter coupling than events, so minimize it.
- Workers will need to share workers in other containers. Examples are styles, data sources, configuration data and ORBs. Implement this with a mechanism that only allows sharing a worker by looking upward in the heirarchy, and placing a reference to a shared worker in the container. If downward sharing is needed use an event. This will preserve vertical layered architecture. Upward sharing in a hierarchy is also known as inheritance.
- Mechanisms such as events and sharing are best implemented by privileged workers, and should not be built into a container's behavior.
- Containers must allow dynamic assembly by workers. This allows a system to be self modifying.
This is so crucial that we must get into detail. A reasonable approach is:
Provide a EventService worker for each container. Workers needing event notification from workers in their container can subscribe directly to those workers. Workers needing event notification from containers in their container or their container would use the EventService.
The EventService would be a privileged worker. Using their container's services, they would communicate with other containers to implement the event service.
Again, this is so crucial that we must get into detail. A reasonable approach is:
Each worker has a "ShareType" property equal to:
- Normal - Most workers will be Normal.
- Shareable - Containers below may share that worker.
- Shared - A proxy worker that is a reference to a worker above.
Note that sharing occurs explicitly. There is no equivilent of declaring a field as "protected" and then letting all subclasses access it automatically, since this reduces understandability and encourages bugs. Remember, we must follow the principle "Workers can only reference other items in their container."
A shared proxy worker could use a name alias to resolve a name collision. Minimize this since it reduces understandability. It cannot be avoided in old, large systems undergoing evolution.