7 people like it.
Like the snippet!
Private Implementation Inheritance without boilerplate
How to avoid delegation boilerplate in inhertance heavy OOP code while maintaining proper accessibility
like seen here https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/seq.fs#L82
and mentioned in Expert F# 2.0 "Using Partially Implemented Types via Implementation Inheritance "
Uses auto-properties that only work with F# 3.0
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
|
(*
How to avoid delegation boilerplate in inheritance heavy OOP code while maintaining proper accessibility
like seen here https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/seq.fs#L82
and mentioned in Expert F# 2.0 "Using Partially Implemented Types via Implementation Inheritance "
Quote from the book:
"If implementation inheritance is used, you should in many cases consider making all implementing
classes private or hiding all implementing classes behind a signature. For example, the
Microsoft.FSharp.Collections.Seq module provides many implementations of the seq<'T> interface
but exposes no implementation inheritance."
Some explanation on why implementation inheritance should be avoided.
http://sharp-gamedev.blogspot.com/2011/07/why-inheritance-for-code-reuse-should.html
This snippet should remedy the boilerplate mentioned in the following link therefore making it worthwhile.
http://sharp-gamedev.blogspot.com/2011/08/is-it-worth-it.html
In this example only the explicit interface implementation of the base class will have the delegation boilerplate.
We inherit from the base class with an object expression and override some members and then immediately upcast to
the interface.
This way we also simulate a protected Prop setter since the interface only exposes the getter
It also demonstates the use of F# 3.0 autoproperties.
*)
type IGameScreen =
abstract Prop : string
abstract Update : unit -> string
[<AbstractClass>]
type GameScreenBase() =
member val Prop = "GameScreen.Prop" with get, set
abstract Update : unit -> string
interface IGameScreen with
member o.Prop = o.Prop
member o.Update() = o.Update()
let loadingScreen () =
{ new GameScreenBase(Prop = "LoadingScreen.Prop") with
override o.Update() = "loadingScreen.Update" } :> IGameScreen
type IMenuScreen =
inherit IGameScreen
abstract OnCancel : unit -> string
[<AbstractClass>]
type MenuScreenBase() =
inherit GameScreenBase(Prop = "MenuScreen.Prop")
abstract OnCancel : unit -> string
default o.OnCancel() = "MenuScreen.OnCancel"
interface IMenuScreen with
member o.OnCancel() = o.OnCancel()
let pauseMenuScreen () =
{ new MenuScreenBase(Prop = "PauseMenuScreen.Prop") with
//override o.OnCancel() = "pauseMenuScreen.OnCancel"
override o.Update() = "pauseMenuScreen.Update" } :> IMenuScreen
module Example =
let gameScreens : IGameScreen[] = [| loadingScreen () ; pauseMenuScreen () |]
let testGameScreens (screens : IGameScreen[]) =
for gs in screens do
gs.Prop |> printfn "%s"
gs.Update() |> printfn "%s"
testGameScreens gameScreens
let menuScreens : IMenuScreen[] = [| pauseMenuScreen () |]
let testMenuScreens (screens : IMenuScreen[]) =
for ms in screens do
ms.Prop |> printfn "%s"
ms.Update() |> printfn "%s"
ms.OnCancel() |> printfn "%s"
testMenuScreens menuScreens
|
abstract member IGameScreen.Prop : string
Full name: Script.IGameScreen.Prop
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
abstract member IGameScreen.Update : unit -> string
Full name: Script.IGameScreen.Update
type unit = Unit
Full name: Microsoft.FSharp.Core.unit
Multiple items
type AbstractClassAttribute =
inherit Attribute
new : unit -> AbstractClassAttribute
Full name: Microsoft.FSharp.Core.AbstractClassAttribute
--------------------
new : unit -> AbstractClassAttribute
Multiple items
type GameScreenBase =
interface IGameScreen
new : unit -> GameScreenBase
abstract member Update : unit -> string
member Prop : string
member Prop : string with set
Full name: Script.GameScreenBase
--------------------
new : unit -> GameScreenBase
val set : elements:seq<'T> -> Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
abstract member GameScreenBase.Update : unit -> string
Full name: Script.GameScreenBase.Update
type IGameScreen =
interface
abstract member Update : unit -> string
abstract member Prop : string
end
Full name: Script.IGameScreen
val o : GameScreenBase
override GameScreenBase.Prop : string
Full name: Script.GameScreenBase.Prop
property GameScreenBase.Prop: string
override GameScreenBase.Update : unit -> string
Full name: Script.GameScreenBase.Update
abstract member GameScreenBase.Update : unit -> string
val loadingScreen : unit -> IGameScreen
Full name: Script.loadingScreen
type IMenuScreen =
interface
inherit IGameScreen
abstract member OnCancel : unit -> string
end
Full name: Script.IMenuScreen
abstract member IMenuScreen.OnCancel : unit -> string
Full name: Script.IMenuScreen.OnCancel
Multiple items
type MenuScreenBase =
inherit GameScreenBase
interface IMenuScreen
new : unit -> MenuScreenBase
abstract member OnCancel : unit -> string
override OnCancel : unit -> string
Full name: Script.MenuScreenBase
--------------------
new : unit -> MenuScreenBase
abstract member MenuScreenBase.OnCancel : unit -> string
Full name: Script.MenuScreenBase.OnCancel
val o : MenuScreenBase
override MenuScreenBase.OnCancel : unit -> string
Full name: Script.MenuScreenBase.OnCancel
override MenuScreenBase.OnCancel : unit -> string
val pauseMenuScreen : unit -> IMenuScreen
Full name: Script.pauseMenuScreen
module Example
from Script
val gameScreens : IGameScreen []
Full name: Script.Example.gameScreens
val testGameScreens : screens:IGameScreen [] -> unit
Full name: Script.Example.testGameScreens
val screens : IGameScreen []
val gs : IGameScreen
property IGameScreen.Prop: string
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
abstract member IGameScreen.Update : unit -> string
val menuScreens : IMenuScreen []
Full name: Script.Example.menuScreens
val testMenuScreens : screens:IMenuScreen [] -> unit
Full name: Script.Example.testMenuScreens
val screens : IMenuScreen []
val ms : IMenuScreen
abstract member IMenuScreen.OnCancel : unit -> string
More information