Traits for Shared Code
For my Java/Swing program JRaceman, I defined a set of GUI classes to simplify my programmatic construction of screens. Each class took as constructor parameters a ResourceSource that provided access to resources (for localization), a resource key prefix, and an action to take when that GUI element was selected (such as a button push or menu item selection). For example, my MenuAction class looked something like this://Java code public class MenuAction extends JMenuItem implements ActionListener { public MenuAction(ResourceSource resourceSource, String resourcePrefix) { //Look up resources for label, toolTip and set those values addActionListener(this); } public void actionPerformed(ActionEvent ev) { action(); } public void action() { /* do nothing */ } }
The application would instantiate a menu item something like this:
//Java code MenuAction b = new MenuAction(resourceSource,"menu.Foo.Open") { public void action() { fooOpen(); } };
For my Mimprint photo printing application, which I converted from Java to Scala, my equivalent class to the MenuAction Java class looks something like this:
class SMenuItem(rSource:SResources, rPrefix:String)(action: =>Unit) extends JMenuItem(rSource.getResourceString(rPrefix+".label")) with SComponent with SCompToolPrompt { setupToolTip(rSource, rPrefix) setupToolPrompt(rSource, rPrefix) setupIcon(rSource, rPrefix) setupActionListener(rSource, action) }
Even without the improvement in lines of code, I like the ability to break up one class into multiple files for organizational purposes.
The application invokes SMenuItem something like this:
val mi = new SMenuItem(rSource, "menu.Foo.Open")(fooOpen)
Traits as Facade
I have also used traits as a nice way to implement the Facade pattern. In the above examples, the SResources type is actually a trait (shown here without the boilerplate and comments that appear in the real source file):trait SResources { def getResourceString(key:String) : String def getResourceStringOption(key: String) : Option[String] def getResourceFormatted(key: String, arg: Any) : String def getResourceFormatted(key: String, arg: Array[Any]) : String }
trait SResourcesFacade extends SResources { //Extending class must define this value protected val sResourcesBase : SResources def getResourceString(key:String) = sResourcesBase.getResourceString(key) def getResourceStringOption(key: String) = sResourcesBase.getResourceStringOption(key) def getResourceFormatted(key: String, arg: Any) = sResourcesBase.getResourceFormatted(key, arg) def getResourceFormatted(key: String, args: Array[Any]) = sResourcesBase.getResourceFormatted(key, args) }
class Foo extends SResourcesFacade { val sResourcesBase : SResources = new RealResources() }
This particular facade only has four methods, but this same technique would work for a facade with any number of methods.
You can find the complete Scala code for the above examples (which is not exactly the same as the code given here) in the packages net.jimmc.swing (for the GUI examples) and net.jimmc.util (for the facade examples) in the sources to Mimprint, which is distributed under the GPL.
5 comments:
The code snippets run together in my Google reader. Are they in "pre" blocks or something else?
Chris: The code snippets are formatted using a div with associated CSS that includes "pre" formatting. Unfortunately Google Reader throws away the CSS and the class attribute on the divs. I have added real <pre> tags around the code snippets in all of my posts. Hit the Refresh button on the blog view to pick up the changes.
Ah, that worked. Thanks!
Hi Jim,
Got another technical faux pax. "My Misperception of Scala's Ordered Trait" breaks your whole site in Safari and presumably Chrome because of an unescaped "<%" in the third snippet. Technically markup in PRE blocks must still be escaped. Some blogging plugins automatically handle this but not Blogger.
Great writing :)
toland: Thanks for pointing that out. Fixed.
Post a Comment