What is a component?

For the purpose of this project the term component is defined as
  • binary functional unit
  • with a separate contract

Specifically that means methods and classes are not components. They are functional units, but they are not binary. Binary functional units on the .NET platform are assemblies.

Assemblies are not components by themselves, though. They lack a separate contract. So what needs to be added is some kind of machine readable description of the services of an assembly to arrive at a component. One easy way to define such a contract is to put contractual types in a second assembly.

Example: A compiler translates a mathematical formula given as a string (e.g. "x^2 + 3*x - 7") into an executable function with signatur Func<double, double>.

A compiler component would then look like this: It would consist of a contract...

mathcompiler.contract.dll
public interface IMathCompiler
{
    Func<double, double> Compile(string mathExpression);
}

... and at least one implementation ...

mathcompiler.codedom.dll
public class CodeDomCompiler : IMathCompiler
{
    public Func<double, double> Compile(string mathExpression)
    {
        ...
    }
}

Separating contract and implementation like this has several advantages:
  • Once the contracts of all components in an application are defined (Contract-First Design) their implementation can be done in parallel. This increases productivity.
  • Tests can be written against contracts and be given to the developers implementing the components. This helps in distributed develpoment scenarios to ensure only high quality code is delivered back. Such tests define behavior (semantic contract) in addition to the syntactic definitions in the contract assembly.
  • Separate contracts make implementations independent of certain implementations. If component C depends on component S, then it´s statically bound only to the contract of S. This allows for different contract implementations, eg. for mock-ups during testing or for different technologies (eg. a CodeDom based expression compiler or a handcrafted one or an ANTLR based one).

Example of static dependency on contract only:

calculator.dll
// references mathcompiler.contract.dll

public class Calculator
{
    private IMathCompiler compiler;  // static dependency on contract type

    public Calculator(IMathCompiler compiler) // ctor dependency injection
    {
        this.compiler = compiler;
    }

   public IEnumerable<PointF> Calculate(string mathExpression, ...)
   {
       var f = this.compiler.Compile(mathExpression); // dynamic dependency
       ...
       for(double x = ...)
       {
           ...
           double y = f(x);
           ...
           yield return ...;
       }
   }
}

The contract of a component often consists of interfaces like above. But enums, structs, and classes are allowed in contracts, too. However, in order to be able to reap all the benefits from Contract-First Design you should keep contracts free of implementation details. As soon as you start to introduce statements into contracts so that you need to test contractual code, you´re making contract development slow. That should be avoided. Contracts need to be coded very quickly as to not hinder hinder their parallel implementation.

The specification of a component is the sum of all its contracts, ie. the contract it implements (exported contract) and the contracts it depends on (imported contracts).

Last edited Apr 4, 2010 at 8:52 AM by ralfw, version 4

Comments

No comments yet.