2011年4月17日日曜日

ScalaでClassオブジェクトを利用したリフレクション

Scalaでリフレクションというか動的なオブジェクト生成ってどうすればいいのだろうと思っていたら、

Scala: How do I dynamically instantiate an object and invoke a method using reflection?

にサンプルがあったのでメモ。

class Foo {
  def hello(name: String): String = "Hello there, %s".format(name)
}

object FooMain {

  def main(args: Array[String]) {
    val foo  = Class.forName("Foo").newInstance.asInstanceOf[{ def hello(name: String): String }]
    println(foo.hello("Walter")) // prints "Hello there, Walter"
  }
}
この方法を利用すると以前書いたAbstractFactoryのサンプルをこれで書きかえることができるので書きかえてみた。

// ラジオボタンのアクションを設定する
    reactions += {
      case ButtonClicked(b) => b.text match {
        case "菜園" => {
          garden = new VegieGarden
          gardenPlot.clearPlants
        }
        case "一年草園" => {
          garden = new AnnualGarden
          gardenPlot.clearPlants
        }
        case "多年草園" => {
          garden = new PerennialGarden
          gardenPlot.clearPlants
        }
      }
    }
の部分は
// ラジオボタンのアクションを設定する
    reactions += {
      case ButtonClicked(b) => {
        garden = Class.forName(buttonMap.getOrElse(b.text, "")).newInstance.asInstanceOf[Garden];
        gardenPlot.clearPlants;
      }
    }

だけでOKになる。 全ソースはこちら
import java.awt.Color

import scala.swing._
import scala.swing.event._

/**
 * メインオブジェクト
 */
object Gardener extends SimpleSwingApplication {
  
  def top = new MainFrame {
    title = "AbstractFactory Sample"
    
    var garden:Garden   = new NoGarden
    val gardenPlot      = new GardenPanel{background = Color.WHITE};
    val buttonMap      = Map("菜園" -> "VegieGarden"
                              , "一年草園" -> "AnnualGarden"
                              , "多年草園" -> "PerennialGarden");
    
    // ButtonGroupの生成と設定
    val group           = new ButtonGroup
    for ((key, value) <- buttonMap) {
      val button = new RadioButton(key)
      group.buttons += button
      listenTo(button)
    };

    contents = new GridPanel(1,2) {

      // 左パネルの生成
      contents += new GridBagPanel {
        val constraints = pair2Constraints(0, 0)
        constraints.anchor = GridBagPanel.Anchor.FirstLineStart
        
        layout += new Label("庭園の種類") -> constraints;
        
        // ラジオボタンを配置する
        for (i <- 0 to (group.buttons.toList.length - 1)) {
          val constraints = pair2Constraints(0, (i + 1));
          constraints.anchor = GridBagPanel.Anchor.FirstLineStart
          constraints.ipady = 70
          layout += group.buttons.toList(i) -> constraints
        }
      }
      
      // 右パネルの生成
      contents += new GridBagPanel {
        val constraints = pair2Constraints(0, 0);
        constraints.ipady = 210
        constraints.ipadx = 210
        constraints.gridwidth = 3
        constraints.fill = GridBagPanel.Fill.Horizontal
        constraints.insets = new Insets(10, 5, 0, 5)
        layout += gardenPlot -> constraints
        
        // ボタンの生成と設定
        val list = List("Center", "Border", "Shade")
        for (i <- 0 to (list.length - 1)) {
          setButton(list(i), i, 1)
        }
        setButton("Quit", 1, 2)
        
        /**
         * ボタンを生成し、パネル上に配置する
         *
         * @param String buttoName
         * @param Int x
         * @param Int y
         * @return Unit
         */
        private def setButton(buttonName:String, x:Int, y:Int) = {
          val button = new Button(buttonName)
          val const = pair2Constraints(x, y);
          const.insets = new Insets(10, 5, 0, 5)
          layout += button -> const
          listenTo(button)
        }
        
        // ボタンのアクションを設定する
        reactions += {
          case ButtonClicked(b) => b.text match {
            case "Center" => {
              gardenPlot.centerPlant = garden.center.name
              gardenPlot.repaint
            }
            case "Border" => {
              gardenPlot.borderPlant = garden.border.name
              gardenPlot.repaint
            }
            case "Shade" => {
              gardenPlot.shadePlant = garden.shade.name
              gardenPlot.repaint
            }
            case "Quit" => System.exit(0)
          }
        }
      }
    }
    
    // ラジオボタンのアクションを設定する
    reactions += {
      case ButtonClicked(b) => {
        garden = Class.forName(buttonMap.getOrElse(b.text, "")).newInstance.asInstanceOf[Garden];
        gardenPlot.clearPlants;
      }
    }
  }
}

/**
 * 植物を表すクラス
 */
case class Plant(name:String)

/**
 * 庭トレイと
 */
trait Garden {
  def shade:Plant
  def center:Plant
  def border:Plant
}

/**
 * デフォルトの庭クラス
 */
class NoGarden extends Garden {
  def shade = new Plant("")
  def center = new Plant("")
  def border = new Plant("")
}

/**
 * 一年草園の庭クラス
 */
class AnnualGarden extends Garden {
  def border = Plant("アブラナ")
  def center = Plant("キンセンカ")
  def shade  = Plant("コレウス")
}

/**
 * 多年草園の庭クラス
 */
class PerennialGarden extends Garden {
  def border = Plant("ベンケイソウ")
  def center = Plant("ケマンソウ")
  def shade  = Plant("チダケサシ")
}

/**
 * 菜園の庭クラス
 */
class VegieGarden extends Garden {
  def border = Plant("エンドウ")
  def center = Plant("トウモロコシ")
  def shade  = Plant("ブロッコリ")
}

/**
 * 庭の様子を表示するパネル
 */
class GardenPanel extends Panel {
  
  var borderPlant = "";
  var centerPlant = "";
  var shadePlant  = "";
  var garden      = new NoGarden
  
  def clearPlants = {
    borderPlant = ""
    centerPlant = ""
    shadePlant  = ""
    repaint()
  }
  
  override def paint(g:Graphics2D) = {
    super.paint(g)
    g.setColor(Color.LIGHT_GRAY)
    g.fillArc(1, 1, 80, 80, 0, 360)
    g.setColor(Color.BLACK)
    g.drawRect(0, 0, size.width - 1, size.height - 1)
    g.drawString(centerPlant, 100, 50)
    g.drawString(borderPlant, 75, 120)
    g.drawString(shadePlant, 10, 40)
  }
}
※追伸 >- この投稿が役にたったと思った方は、上か下の広告をクリックしていただけるとうれいいです。

0 件のコメント:

コメントを投稿