在应用程序启动时动态触发对象方法

时间:2012-03-18 14:37:19

标签: java scala reflection

我有一个应该生产汽车并与它们一起运行的应用程序。汽车对象创建是一个复杂的过程,因此我需要为每种类型的汽车提供工厂。此外,我希望用户能够提供自己的汽车类型和生产它们的工厂。这些汽车类型和工厂应该作为罐子插入我的应用程序(可能有更好的方式比罐子,但我没有看到它。)

我想到了一个共同的CarFactory,它接受汽车的名称(“mercedes”,“bmw”,“nissan”等)作为参数。 CarFactory有一个地图,每个名称都映射到自己的工厂类。代码看起来像这样(抱歉,我无法提供工作副本,因为我仍在评估它,并且没有一个没有错误编译的版本)

import scala.collection.mutable.Map

class CarFactory {
  var knownCarTypes = Map[String, Class[Factory]]()

  def create(carType: String) = knownCarTypes.get(carType) match {
      case Some(factoryClass) => Some(factoryClass.getMethod("create").invoke(null).asInstanceOf[Car])
      case None => None
    }
  }
}

knownCarTypes是可变的,因为我希望用户工厂在这张地图上注册,提供他们负责的汽车类型以及工厂类的名称。所以从用户类看起来就是这个

class Mercedes extends Car

object MercedesFactory extends Factory {
  def register() {
    CarFactory.knownCarTypes("mercedes") = getClass
  }

  def create() = new Mercedes()
}

这是我的问题。我不知道如何触发用户工厂的 register()方法。可能吗?有没有比我的方法更好的解决方案?

我考虑为工厂制作共同特征,通过反射找到实现特征和触发方法的所有已加载类。但它看起来很复杂。我希望这里可以使用一些设计模式或OOP技巧。你觉得怎么样?

谢谢!

2 个答案:

答案 0 :(得分:1)

如果我理解你的问题,你所要做的就是从对象的“身体”中调用注册:

object MercedesFactory extends Factory {
  def register() {
    CarFactory.knownCarTypes("mercedes") = getClass
  }
  register

  def create() = new Mercedes()
}

答案 1 :(得分:1)

最后我通过反思得到了它。我遍历指定路径上的所有jar,发现所有实现我的 com.example.Factory 特征的类并触发了他们的 register()方法。对于罐子检查,我使用Clapper ClassFinder并且用于调用对象方法我跟着Thomas Jung advice.这是最终代码

import org.apache.commons.io.FileUtils
import org.clapper.classutil.{ClassFinder, ClassInfo}
import scala.collection.JavaConverters._

def triggerFactories() {
  val jars = FileUtils.iterateFiles(new File("lib"), Array[String]("jar"), true).asScala.toList
  val classes = ClassFinder(jars).getClasses
  val factories = ClassFinder.concreteSubclasses("com.example.Factory", classes)

  factories.foreach { (factory: ClassInfo) =>
    companion[Factory](factory.name).register()
  }
}

def companion[T](name: String)(implicit man: Manifest[T]): T =
  Class.forName(name).getField("MODULE$").get(man.erasure).asInstanceOf[T]

这对我有用。它看起来很棘手,但我希望将来不会破坏我的应用程序中的任何内容。如果有更好的方法,请发帖,我会重新接受答案。