4.4.1.9–4.4.1.11 - Decomposition, composition and automation (C#)

4.4.1.9–4.4.1.11 - Decomposition, composition and automation (C#)

Break a problem into identifiable tasks, combine procedures and data, then put an abstract model into action through an algorithm. This route uses written C# tasks and is incomplete: runnable support pending.

Procedural decomposition

Procedural decomposition divides a problem into sub-problems, each accomplishing an identifiable task that can be subdivided again.

A car park needs a display showing how many spaces are available. “Build the parking system” is too large to implement or check as one instruction. A useful decomposition is: collect zone readings, reject inconsistent readings, calculate free spaces, decide the display message, and present it. “Write the first half of the code” is a poor decomposition because it identifies no meaningful responsibility.

Procedural decomposition

Breaking a problem into identifiable sub-tasks, with further decomposition where a sub-task is still too complex.

Each sub-task needs a clear interface: its inputs, outputs and assumptions. A validation procedure can accept capacity and occupied count and return a Boolean. A free-space calculation can assume valid counts and return an integer. Separating these responsibilities makes a failure easier to locate and lets each component be checked against its own contract.

C#
static bool ReadingIsValid(int capacity, int occupied)
{
    return capacity >= 0 && occupied >= 0 && occupied <= capacity;
}

static int FreeSpaces(int capacity, int occupied)
{
    // Caller guarantees ReadingIsValid(capacity, occupied).
    return capacity - occupied;
}

With capacity 40 and occupied 31, validation returns true and the calculation returns 9. With occupied 45, validation returns false: subtracting anyway would produce −5, a result that violates the physical model. A procedure is not correct in isolation from its preconditions. Decide whether callers guarantee them or whether the procedure must detect violations.

Further decomposition might split collecting readings into obtaining a sensor value and converting it into the chosen data representation. Avoid unnecessary fragmentation: a one-line arithmetic calculation may already be a sufficiently clear task. The aim is manageable responsibilities rather than a large procedure count.

Procedure and data composition

Composition builds a larger abstraction by combining components whose interfaces fit together.

Procedure composition combines procedures to make a compound procedure. Here DescribeZone uses the result of FreeSpaces as the input to DisplayMessage; callers need not repeat the subtraction or message rule.

C#
static string DisplayMessage(int free)
{
    return free == 0 ? "Full" : free + " spaces free";
}

static string DescribeZone(int capacity, int occupied)
{
    int free = FreeSpaces(capacity, occupied);
    return DisplayMessage(free);
}

DescribeZone(40, 31) returns "9 spaces free"; DescribeZone(40, 40) returns "Full". It retains the valid-reading precondition of the calculation it calls. Composition differs from decomposition: decomposition identifies smaller responsibilities; composition assembles their implementations into useful larger behaviour. They work together in the same design.

Data composition combines data objects into compound data. A zone has a name, a capacity and an occupied count. Keeping these values together prevents confusing one zone's capacity with another's reading.

C#
class Zone
{
    public string Name;
    public int Capacity;
    public int Occupied;

    public Zone(string name, int capacity, int occupied)
    {
        Name = name;
        Capacity = capacity;
        Occupied = occupied;
    }
}

Continue this lesson.

You’re previewing NovaLearn without an account. Create a free account to read the rest.

Create free account