There are two ways to achieve polymorphism in Nim. One is to create toInterface converter
and another is dynamic dispatch
.
interface definition
type IRepository* = object
exec*: proc(msg:string):string
implementation
type Repository* = object
proc exec(self:Repository, msg:string):string =
return &"Repository {msg}"
converter toInterface*(self:Repository):IRepository =
return IRepository(
exec: proc(msg:string):string = self.exec(msg)
)
interface definition
type IRepository* = object of RootObj
method exec*(self:IRepository, msg:string):string {.base.} = raise newException(CatchableError, "error")
implementation
type Repository* = object of IRepository
method exec*(self:Repository, msg:string):string =
return &"Repository {msg}"
nimble install interface_implements
implements
macro creates toInterface
converter.
import interface_implements
implements Repository, IRepository:
proc func1(self:Repository, msg:string):string =
return "Repository1 " & msg
proc func2(self:Repository, number:int):string =
return "Repository2 " & $number
This is converted to bellow.
proc func1(self:Repository, msg:string):string =
return "Repository " & msg
proc func2(self:Repository, number:int):string =
return "Repository2 " & $number
converter toInterface*(self:Repository):IRepository =
return IRepository(
func1: proc(msg:string):string = self.func1(msg),
func2: proc(number:int):string = self.func2(number)
)
macro implements*(implName, interfaceName, procs:untyped):untyped
repository_interface.nim
type IRepository* = object
exec*: proc(msg:string):string
mock_repository.nim
import interface_implements
import ./repository_interface
type MockRepository = object
proc newMockRepository*():MockRepository =
return MockRepository()
implements MockRepository, IRepository:
proc exec(self:MockRepository, msg:string):string =
return "MockRepository " & msg
repository.nim
import interface_implements
import ./repository_interface
type Repository = object
proc newRepository*():Repository =
return Repository()
implements Repository, IRepository:
proc exec(self:Repository, msg:string):string =
return "Repository " & msg
usecase.nim
import ./repository_interface
type Usecase = object
repository: IRepository
func newUsecase*(repository:IRepository):Usecase =
return Usecase(repository:repository)
proc exec*(self:Usecase, msg:string):string =
return self.repository.exec(msg)
presentation layer
block:
let repository = newMockRepository()
let usecase = newUsecase(repository)
assert "MockRepository mock" == usecase.exec("mock")
block:
let repository = newRepository()
let usecase = newUsecase(repository)
assert "Repository exec" == usecase.exec("exec")
interfaceDef
macro creates method
of fields of Object.
This is only available in Nim 2.0.0 and above.
import interface_implements
interfaceDef:
type IRepository* = object of RootObj
exec: proc(self:IRepository, msg:string):string
This is converted to bellow.
type IRepository* = object of RootObj
method exec*(self:IRepository, msg:string):string {.base.} = raise newException(CatchableError, "Implementation exec of IRepository is not found")
macro interfaceDefs*(body:untyped):untyped
repository_interface.nim
interfaceDef:
type IRepository* = object of RootObj
exec*: proc(msg:string):string
mock_repository.nim
import interface_implements
type MockRepository = object of IRepository
proc newMockRepository*():MockRepository =
return MockRepository()
method exec*(self:MockRepository, msg:string):string =
return "MockRepository " & msg
repository.nim
import interface_implements
type Repository = object of IRepository
proc newRepository*():Repository =
return Repository()
method exec*(self:Repository, msg:string):string =
return "Repository " & msg
usecase.nim
import ./repository_interface
type Usecase = object
repository: IRepository
func newUsecase*(repository:IRepository):Usecase =
return Usecase(repository:repository)
proc exec*(self:Usecase, msg:string):string =
return self.repository.exec(msg)
presentation layer
block:
let repository = newMockRepository()
let usecase = newUsecase(repository)
assert "MockRepository mock" == usecase.exec("mock")
block:
let repository = newRepository()
let usecase = newUsecase(repository)
assert "Repository exec" == usecase.exec("exec")