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.
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.
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.
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