1

I am developing a desktop application in Scala which has several dozen configuration options that users can configure. The options are defined as a trait so that there can be separate implementations for unit testing and the runtime application:

trait Config { var showNotifications: Boolean var showOverlay: Boolean // ...several dozen similar options } 

The implementation for unit testing is straightforward:

object TestConfig extends Config { var showNotifications: Boolean = true var showOverlay: Boolean = false // ... } 

However the implementation for runtime use is more complex because it needs to persist changes and notify various listeners. I define the getters and setters directly to achieve this; each getter and setter refers to a standard method like this:

class UserConfig extends Config { class BooleanPref(val key: String, val default: Boolean) { def get = { // ... load configuration option ... } def set(value: Boolean) = { // ... save configuration option ... } } val prefShowNotifications = new BooleanPref("showNotifications", true) def showNotifications: Boolean = prefShowNotifications.get def showNotifications_=(value: Boolean) = prefShowNotifications.set(value) val prefShowOverlay = new BooleanPref("showOverlay", false) def showOverlay: Boolean = prefShowOverlay.get def showOverlay_=(value: Boolean) = prefShowOverlay.set(value) // ... repeated several dozen times ... } 

This results in a lot of boilerplate code, which seems like it should be unnecessary. Both the getter and setter have to be mapped individually for each configuration option even though they all work in exactly the same way.

Is there any way in Scala to define both a getter and setter at the same time? ie. instead of having this:

val prefShowNotifications = new BooleanPref("showNotifications", true) def showNotifications: Boolean = prefShowNotifications.get def showNotifications_=(value: Boolean) = prefShowNotifications.set(value) 

Is it possible to assign an object or function that implements both the getter and setter, something like:

val showNotifications: Boolean = // something here 

Or is there some other way to avoid defining getters and setters individually?

5
  • 2
    I'm not understanding why you need getters and setters at all. Fields are public by default. Commented Jul 20, 2014 at 6:48
  • UserConfig doesn't store values in the class, it calls an API to record the values externally (for example, in the Windows registry). It wouldn't be suitable to store the values as fields in UserConfig because it always needs to look up the latest value from the external API, hence it needs getter and setter methods. Commented Jul 20, 2014 at 7:05
  • What if you made just one method, and then had it take a number of case classes depending on what you needed it to do, sorta taking the opposite approach to this. Rather than having a million methods that all call the same getter and setter, you have a single getter and setter that take a million different options Commented Jul 20, 2014 at 10:59
  • @ElectricCoffee I can try it to see if it's better... to be clear: are you suggesting I create a new abstract class that defines case classes like ShowNotifications, ShowOverlay etc, then the Config trait just has a get and a set method? Commented Jul 20, 2014 at 12:10
  • @gutch You don't need to put case classes into an abstract class. You just pass the case classes into BooleanPref depening on what kind of functionality you need Commented Jul 20, 2014 at 12:22

1 Answer 1

2

Something like this should simplify your code a lot.

trait Config { sealed trait BooleanPref case object ShowNotificationPref extends BooleanPref ... def set(pref: BooleanPref, value: Boolean): Unit def get(pref: BooleanPref): Boolean } class UserConfig extends Config { def get(pref: BooleanPref) = pref match { case ShowNotificationPref => ... case ... } def set(pref: BooleanPref, value: Boolean): Unit = pref match { case ShowNotificationPref => ... case ... } } 

There's still room for improvement in case the set and get implementations are factorizable somehow. Not knowing this detail, it's hard to tell.

You could also think of a macro for generating the different cases by inspecting the BooleanPref subtypes, but again, some more information about the specific case is needed.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.