Reputation: 1586
My code is always a mess, and I think I lack some foundational knowledge. When I try to split functionality out over traits and objects, I'm not quite sure what a best practice would be... or why to choose one form over another. I've used the three approaches below, but I'm not necessarily clear on why to choose one over the others. Can anyone help me think about different ways to structure these kinds of dependencies?
Option 1 - simplest approach, modular, call as needed... honestly, my inclination is to go this route always to keep things simple... though it often requires passing loads of arguments into the methods in anything more complicated than this simple example, so it's not very elegant.
object A {
def run(x: T): Unit = ??? // do stuff with x
}
object Main {
val x: T = ???
def main(args: Array[String]): Unit = {
A.run(x)
}
}
Option 2 - I've mostly used this approach in my code historically... if the code in trait A was designed for object Main, and I'm simply splitting it out to make things easier to follow... this logically makes sense to me. It also minimizes the arguments passed around to functions, which feels cleaner. But... this makes code harder to reuse. If six months from now I need one function out of trait A somewhere other than object Main because I've extended a project in an unanticipated way, it becomes difficult.
trait A {
def x: T
def run: Unit = ??? // do stuff with x
}
object Main extends A {
val x: T = ???
def main(args: Array[String]): Unit = {
run
}
}
Option 3 - I think this is best when stirring together lots of dependencies? Honestly I'm not really sure. Again, this becomes really difficult to reuse pieces as projects expand in functionality. Likely due to poor design choices by me.
trait A {
this: Main =>
val runner: Runner
class Runner(x: T) {
def run: Unit = ??? // do stuff with x
}
}
object Main extends A {
val x: T = ???
val runner = new Runner(x)
def main(args: Array[String]): Unit = {
runner.run
}
}
Any thoughts on why to choose one vs. the others to help me take one step towards writing better code? Does it come down to simply what is most readable and balancing the tradeoffs of passing fewer arguments around vs. making things cleaner and more readable, or is there a smarter way to think about program design.
Any recommendations for places to learn how to make these kinds of decisions better?
Upvotes: 4
Views: 363
Reputation: 900
I use an object
when you want a simple interface to do stuff that doesn't change any state (pure functions). Think in the Math
class as an example of this. Also, if you want to implement the singleton
dessing pattern you can use object
too.
I use a trait
if you know that a bunch of different class of objects will have the same interface. If you don't, It could be overkill to define a trait
.
And if you want a real object instance of a class is beacuse you need to encapsulate transformation of private properties. I usually do that when I know that I need an abstract data-type
, that is, a data structure or entity that will hide implementation details.
Please note that, as Jörg W Mittag says, this is not like Java, because in scala you can define objects using classes.
Upvotes: 2