12 — COMPOSITION

HAS-A beats IS-A on a plant floor — build skids from parts you can replace.

Estimated 24 minutes.

A skid is a parts list, not a special kind of frame

Walk up to a filling skid and you can name what is on it: a Grundfos pump, a Bray butterfly valve, a Rosemount mag meter, all bolted to a welded frame with a small panel at one end. Nobody would describe that frame as "a kind of pump". It HAS a pump. Software gets this wrong constantly, and the bill arrives on the third change request.

Build the skid and run it

Bolt parts onto the frame. Every part you add is one VAR line and one call inside FB_Skid — and the program below is rebuilt and executed on the real ST engine each time. Watch what the skid can and cannot do with parts missing.

One box on the rung, three boxes inside it

Nothing here is an object-oriented extension. Nested function-block instances are 2nd-edition IEC 61131-3: Siemens calls it a multi-instance, Rockwell nests one Add-On Instruction inside another, and it compiles everywhere.

Where does the pump run-time counter live?

FB_Pump keeps RunTime in its own VAR_OUTPUT. The skid holds one FB_Pump instance. Next year that pump is lifted off SKID-2100 and bolted onto SKID-2400, and the code follows it. What happens to the run-time count?

  • It stays with the skid — the frame is what has history
  • It moves with the pump, because the counter is part of the pump instance
  • It is lost — instance memory cannot be transferred
  • It has to be copied by hand from the old skid

Delegation: the frame decides, the parts do

FB_Skid contains no pump logic. It works out one thing — whether the machine may run — and then asks each part, in process order, through the pins the part publishes. The parts never call each other and never call back up. That one-way flow is what makes any part replaceable without opening the others.

Keep the outer block narrow

A composed block that republishes all of its parts has not encapsulated anything — it has just added a layer of typing between the caller and the same nineteen dependencies.

Give every value exactly one owner

Six values off one skid. Click the block that should write each one. Two writers is not a style opinion — it is a fault report waiting to be filed.

The same machine, modelled as a family tree

The other way to build this is derivation: a base machine with mode and fault handling, a pump station derived from it, a filler derived from that, and a vendor-specific filler derived from that. Every generation adds behaviour to the body it inherited. It reads beautifully on the first day and it is a single, rigid object by the second change request.

The inheritance version, in both languages

Compare the pin count with the composed FB_Skid four screens back. Every inherited detail is now part of the derived block's public surface.

Add a second outlet valve

The valve sequence lives in the body of FB_Filler, and FB_FillerHart derives from it. Process asks for a second outlet valve. In the composed design, FB_Skid gains one VAR line and one call. What does the derived design cost?

  • The same — one line, in FB_Filler
  • An edit to FB_Filler, plus a re-test of everything derived from it
  • Nothing — derived blocks pick up new behaviour automatically
  • A new derived block, FB_FillerTwoValve

Cost three change requests, both ways

Three real change requests off one filling line. For each one, mark the blocks you would have to open in each design, and read the ledger.

Favour composition over inheritance, in plant terms

The slogan is true for a boring reason. Inheritance is a permanent weld made at design time by someone who did not know what the plant would ask for; composition is a bolted joint you can undo with a spanner. A part on a frame can be swapped, borrowed by another machine, or bench-tested on its own. A generation in a family tree can do none of those things.

Where inheritance still earns its place

Inheritance is a fine answer to "all of these are genuinely the same kind of thing". It is a poor answer to "these two share some code".

How to lay out a real machine

One manager block per machine, holding an instance of each device it owns. Every device called exactly once per scan, from its owner and nowhere else. Every value written by exactly one block. The manager publishes what the machine does, not what its parts are. Follow those four and the program tends to survive its own maintenance.

Manager, skids, parts — three levels, one call

Adding a fourth skid to this line is a change to one array bound. Adding a fourth generation to an inheritance chain is a design meeting.

The reach-through

A colleague needs the pump run-time on the HMI. FB_Skid does not publish it, so they bind the faceplate straight to Line1.Skids[2].Pump.RunTime. It works. What have they actually done?

  • Nothing — reading a value never breaks anything
  • Made the pump un-replaceable: the HMI now depends on which part is on the frame
  • Created a double-writer conflict on RunTime
  • Broken the scan order, because the HMI reads mid-scan

What you learned

  • Composition is HAS-A: a block declares instances of other blocks in VAR and delegates to them.
  • Nested function-block instances are core IEC 61131-3 — they run everywhere, including in this browser.
  • The outer block decides; the parts do. Calls go one way, down the levels, once per scan.
  • Keep the composed block's interface narrow: publish what the machine does, not what its parts are.
  • A value belongs to whichever block it would follow if you unbolted the device and moved it.
  • Inheritance welds; composition bolts. Use derivation only for a stable IS-A, like device fault handling.
  • Reaching two levels down — Skid.Pump.RunTime from the HMI — is what turns a composed design back into a monolith.

COMPOSITION — done

  • You built a composed FB_Skid part by part and watched it execute on the real engine.
  • You can cost a change request against two designs and say which one the plant should pay for.
  • You can assign single ownership to every value in a machine and spot a double writer before it ships.