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:
78:
|
(*
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 we 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 making it worthwhile.
http://sharp-gamedev.blogspot.com/2011/08/is-it-worth-it.html
In this example only the base classes have the delegation boilerplate and
we simulalte a protected Prop setter while keeping the implementations private.
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