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.
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.
functionNew-Cylinder{param($radius=3.0,$height=4.0)$prototype=new-prototype$prototype|Update-TypeName$prototype|Mixin-Cylindrical$radius$height$prototype}$cylinder=New-Cylinder-radius1-height1# Let's have a look at our new cylinder object:$cylinder|Get-Member-ViewExtendedTypeName:Prototype#CylinderNameMemberTypeDefinition------------------------ENotePropertySystem.DoubleE=2.71828182845905HeightNotePropertySystem.Int32Height=1PINotePropertySystem.DoublePI=3.14159265358979RadiusNotePropertySystem.Int32Radius=1HypotenuseScriptMethodSystem.ObjectHypotenuse();SqrtScriptMethodSystem.ObjectSqrt();BaseAreaScriptPropertySystem.ObjectBaseArea{get=2*$this.Radius*$this.Radius*$this.PI;}CircumferenceScriptPropertySystem.ObjectCircumference{get=$this.Diameter*$this.PI;}DiameterScriptPropertySystem.ObjectDiameter{get=$this.Radius*2.0;}LateralAreaScriptPropertySystem.ObjectLateralArea{get=$this.Circumference*$this.LateralHeight;}LateralHeightScriptPropertySystem.ObjectLateralHeight{get=$this.Height;}SurfaceAreaScriptPropertySystem.ObjectSurfaceArea{get=$this.BaseArea+$this.LateralArea;}# And its values$cylinder|flPI:3.14159265358979E:2.71828182845905Radius:1Diameter:2Circumference:6.28318530717959Height:1LateralHeight:1LateralArea:6.28318530717959SurfaceArea:12.5663706143592BaseArea: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.
functionNew-Cone{param($radius=3.0,$height=4.0)$prototype=new-prototype$prototype|Update-TypeName$prototype|Mixin-Cylindrical$radius$height# override/replace Calculations$prototype|Add-ScriptPropertyBaseArea{$this.Radius*$this.Radius*$this.PI}$prototype|Add-ScriptPropertyLateralHeight{$this.Get-Hypotenuse($this.Radius,$this.Height)}$prototype|Add-ScriptPropertyLateralArea{$this.PI*$this.Radius*$this.LateralHeight}$prototype}$cone=New-Cone-radius1-height1# Let's have a look at our new cone object:$cone|Get-Member-ViewExtendedTypeName:Prototype#ConeNameMemberTypeDefinition------------------------ENotePropertySystem.DoubleE=2.71828182845905HeightNotePropertySystem.Int32Height=1PINotePropertySystem.DoublePI=3.14159265358979RadiusNotePropertySystem.Int32Radius=1HypotenuseScriptMethodSystem.ObjectHypotenuse();SqrtScriptMethodSystem.ObjectSqrt();AreaScriptPropertySystem.ObjectArea{get=$this.LateralArea+2.0*$this.Pi*$this.Radius*$this.Height;}BaseAreaScriptPropertySystem.ObjectBaseArea{get=$this.Radius*$this.Radius*$this.PI;}CircumferenceScriptPropertySystem.ObjectCircumference{get=$this.Diameter*$this.PI;}DiameterScriptPropertySystem.ObjectDiameter{get=$this.Radius*2.0;}LateralAreaScriptPropertySystem.ObjectLateralArea{get=$this.Circumference*$this.LateralHeight;}LateralHeightScriptPropertySystem.ObjectLateralHeight{get=$this.Hypotenuse($this.Radius,$this.Height);}SurfaceAreaScriptPropertySystem.ObjectSurfaceArea{get=$this.BaseArea+$this.LateralArea;}# And its values$cone|flPI:3.14159265358979E:2.71828182845905Radius:1Diameter:2Circumference:6.28318530717959Height:1LateralArea:8.88576587631673SurfaceArea:12.0273585299065Area:15.1689511834963BaseArea:3.14159265358979LateralHeight:1.4142135623731