This post is part of a four part series on prototypal inheritance using PowerShell.

If you didn’t read Part 1 and Part 2, I would recommend reading them as I build off of their functionality and theory.

With mixins, each derivation/extension has the chance to replace override previously declared functionality. Since we aren’t dealing with classical inheritance, we can have multiple mixins applied to a single prototypal object.

I have designed the mixins to operate as filters that return nothing; this choice is simply for syntax reasons.

filter Mixin-Math {
  $_ | Add-Property PI ([Math]::PI) Constant
  $_ | Add-Property E ([Math]::E) Constant
  $_ | Add-Function Get-Sqrt {param($value)[Math]::Sqrt($value)}
  $_ | Add-Function Get-Hypotenuse {param($a,$b) $this.Get-Sqrt($a*$a+$b*$b)}
}

filter Mixin-Circular {
  param($radius = 3.0)
  $_ | Mixin-Math
  $_ | Add-Property Radius $radius
  $_ | Add-ScriptProperty Diameter {$this.Radius * 2.0}
  $_ | Add-ScriptProperty Circumference {$this.Diameter * $this.PI}
  $_ | Add-ScriptProperty BaseArea {$this.Radius * $this.Radius * $this.PI}
}

filter Mixin-Cylindrical {
  param($radius = 3.0, $height = 4.0)
  $_ | Mixin-Circular $radius
  $_ | Add-Property Height $height
  $_ | Add-ScriptProperty LateralHeight {$this.Height}
  $_ | Add-ScriptProperty LateralArea {$this.Circumference * $this.LateralHeight}
  $_ | Add-ScriptProperty SurfaceArea {$this.BaseArea + $this.LateralArea}
  # override/replace
  $_ | Add-ScriptProperty BaseArea {2 * $this.Radius * $this.Radius * $this.PI}
}

The mixins describe groups of functionality that we wish to bestow upon other objects effectively apply a bulk monkey patch. Since we only have the single level of delegation, we replace any functions/properties instead of overriding.

To create a new cylinder, we just need to create the prototype and mix in the cylindrical behavior.

function New-Cylinder {
  param($radius = 3.0, $height = 4.0)
  $prototype = new-prototype
  $prototype | Update-TypeName
  $prototype | Mixin-Cylindrical $radius $height
  $prototype
}

$cylinder = New-Cylinder -radius 1 -height 1

# Let's have a look at our new cylinder object:
$cylinder | Get-Member -View Extended

   TypeName: Prototype#Cylinder

Name          MemberType     Definition
----          ----------     ----------
E             NoteProperty   System.Double E=2.71828182845905
Height        NoteProperty   System.Int32 Height=1
PI            NoteProperty   System.Double PI=3.14159265358979
Radius        NoteProperty   System.Int32 Radius=1
Hypotenuse    ScriptMethod   System.Object Hypotenuse();
Sqrt          ScriptMethod   System.Object Sqrt();
BaseArea      ScriptProperty System.Object BaseArea {get=2 * $this.Radius * $this.Radius * $this.PI;}
Circumference ScriptProperty System.Object Circumference {get=$this.Diameter * $this.PI;}
Diameter      ScriptProperty System.Object Diameter {get=$this.Radius * 2.0;}
LateralArea   ScriptProperty System.Object LateralArea {get=$this.Circumference * $this.LateralHeight;}
LateralHeight ScriptProperty System.Object LateralHeight {get=$this.Height;}
SurfaceArea   ScriptProperty System.Object SurfaceArea {get=$this.BaseArea + $this.LateralArea;}

# And its values
$cylinder | fl

PI            : 3.14159265358979
E             : 2.71828182845905
Radius        : 1
Diameter      : 2
Circumference : 6.28318530717959
Height        : 1
LateralHeight : 1
LateralArea   : 6.28318530717959
SurfaceArea   : 12.5663706143592
BaseArea      : 6.28318530717959

We can take an object that has cylindrical properties, but has differently definitions for those properties, and reconstruct them after applying the mixin. In our case, we are going to define a cone.

function New-Cone {
  param($radius = 3.0, $height = 4.0)
  $prototype = new-prototype
  $prototype | Update-TypeName
  $prototype | Mixin-Cylindrical $radius $height
  # override/replace Calculations
  $prototype | Add-ScriptProperty BaseArea {$this.Radius * $this.Radius * $this.PI}
  $prototype | Add-ScriptProperty LateralHeight {$this.Get-Hypotenuse($this.Radius, $this.Height)}
  $prototype | Add-ScriptProperty LateralArea {$this.PI * $this.Radius * $this.LateralHeight}
  $prototype
}


$cone = New-Cone -radius 1 -height 1

# Let's have a look at our new cone object:
$cone | Get-Member -View Extended

   TypeName: Prototype#Cone

Name          MemberType     Definition
----          ----------     ----------
E             NoteProperty   System.Double E=2.71828182845905
Height        NoteProperty   System.Int32 Height=1
PI            NoteProperty   System.Double PI=3.14159265358979
Radius        NoteProperty   System.Int32 Radius=1
Hypotenuse    ScriptMethod   System.Object Hypotenuse();
Sqrt          ScriptMethod   System.Object Sqrt();
Area          ScriptProperty System.Object Area {get=$this.LateralArea + 2.0 * $this.Pi * $this.Radius * $this.Height;}
BaseArea      ScriptProperty System.Object BaseArea {get=$this.Radius * $this.Radius * $this.PI;}
Circumference ScriptProperty System.Object Circumference {get=$this.Diameter * $this.PI;}
Diameter      ScriptProperty System.Object Diameter {get=$this.Radius * 2.0;}
LateralArea   ScriptProperty System.Object LateralArea {get=$this.Circumference * $this.LateralHeight;}
LateralHeight ScriptProperty System.Object LateralHeight {get=$this.Hypotenuse($this.Radius, $this.Height);}
SurfaceArea   ScriptProperty System.Object SurfaceArea {get=$this.BaseArea + $this.LateralArea;}

# And its values
$cone | fl

PI            : 3.14159265358979
E             : 2.71828182845905
Radius        : 1
Diameter      : 2
Circumference : 6.28318530717959
Height        : 1
LateralArea   : 8.88576587631673
SurfaceArea   : 12.0273585299065
Area          : 15.1689511834963
BaseArea      : 3.14159265358979
LateralHeight : 1.4142135623731