Premise:
I want to separate the information necessary to instantiate a class from the information necessary to "run" the class. However, the information neccesary to "run" the class may differ from class to class. Thus, I imagine the class "has" specific information to run it, and the two go together.
Here is an example of my code:
trait Machine {
type Params <: BaseParams
def start(machineParams: Params): Unit
}
trait BaseParams {
def speed: Int
def power: Int
}
class FlyingMachine() extends Machine {
type Params = FlyingParams
override def start(machineParams: Params): Unit = {
println(s"I'm flying with $machineParams")
}
}
trait FlyingParams extends BaseParams {
def height: Int
}
abstract class MachineOwner{
val machine: Machine
def params: machine.Params
def startMachine(): Unit = {
machine.start(params)
}
}
This compiles, passes tests, I'm happy.
Problem: I'm using val machine: Machine in order to define def params: machine.Params. I've been told to make this a def to let the implementer have more freedom. If I do so, I can no longer refer to machine.Params
At this point, I'm at a loss for how to continue. I keep thinking that if this should be a def and definitely not a val, then my architecture is wrong.
So
- Is my approach to this problem wrong, given the premise I set out with?
- If it's not wrong, is there a way to still achieve this while using
definstead ofvalin theMachineOwnerclass?
EDIT Given Alexey Romanov's answer, the last bit of the code would look like this
abstract class MachineOwner{
type Params1 <: BaseParams
def machine: Machine { type Params = Params1 }
def params: Params1
def startMachine(): Unit = {
machine.start(params)
}
}
class FlyingMachineOwner(
machine: FlyingMachine
) extends MachineOwner {
override type Params1 = FlyingParams
override def params = FlyingParams(1,1,1)
}
But this doesn't compile because it expects an override specifically for def machine: Machine { type Params = Params1 }. How does one define that?
It really can't be answered without knowing desired semantics.
If
MachineOwneris supposed to own a single machine, then "to make this a def to let the implementer have more freedom" is bad advice: the freedom it gives is exactly to return different machines from different calls todef machineand not to hold references to machines it gives out.If it is supposed to have multiple machines, should all of them have the same
Paramstype? Then you would do something likeOr if not, then you need a different design again, maybe
def params(machine: Machine): machine.Params. Etc. etc.For the edit: you can do
but it really seems unnecessarily complicated compared to what you get with type parameters.