Interfaces, Abstract Classes and Polymorphism

13.1 Interfaces

interface Shape
{
    double area();
    final string describe() { import std.conv; return "Area: " ~ area().to!string; }   // final method with a body
    static Shape unit() { return new Square(1); }
}

interface Named { string name(); }

class Square : Shape, Named
{
    double side;
    this(double s) { side = s; }
    double area() { return side * side; }
    string name() { return "square"; }
}

Shape[] shapes = [new Square(2), Shape.unit()];
foreach (s; shapes) writeln(s.describe);

13.2 Abstract Classes

abstract class Vehicle
{
    abstract void drive();                       // must be implemented
    void honk() { writeln("Beep"); }
}
class Car : Vehicle { override void drive() { writeln("Driving"); } }

13.3 Static (Compile-Time) Polymorphism

Prefer templates/ranges for performance-critical polymorphism (no virtual calls). Use interfaces for runtime polymorphism and plugins.

// Duck typing with templates: any type with area() works
double totalArea(T)(T[] items) { double t = 0; foreach (i; items) t += i.area; return t; }

13.4 Design Patterns in D

Singleton (static this / lazy thread-safe init), Factory (Object.factory("module.ClassName")), Visitor (via templates and overloads), Observer (delegates/signals: std.signals), Strategy (delegates), RAII (structs with destructors).