methodMissing closure - 没有封装?

时间:2012-12-28 06:02:22

标签: groovy

我理解时髦的基础知识 - 以及封闭......

我试图从java调用groovy:

// Patient.java

public class Patient {  
    //... other data  
    private Map<String, String> attribStore = new HashMap<String,String>();  
    // getters/setters for attribStore omitted  

    public void addAttribute(String key, String val) {
        if (!attribStore.containsKey(key)) {
            attribStore.put(key, val);
        }
    }

// GroovyHelper.java

public class GroovyHelper {
    private String codeSnippet; // groovy script code

    public String evaluateSnippetToString(Binding binding) {
        addMethodMissingHandler();
        GroovyShell shell = createGroovyShell(binding);
        Object result = shell.evaluate(codeSnippet);
        return result.toString();
    }

    // installs a patient in the binding - accesses the patient  
    // attribStore from groovy
    // The missing method is used to create an "attribute" i.e.
    // a (key,val) pair in the patient map 
    private void addMethodMissingHandler() {
        codeSnippet = "def attribStore = p.getAttribStore();\n"
        + "Patient.metaClass.methodMissing = \n{"
        + " String methodName, args -> \n"
        + "methodName = methodName.replaceFirst(/^get/, '');\n"  
        + "def attrib = methodName[0].toLowerCase() + methodName.substring(1);\n"
        + "if (!attribStore.containsKey(attrib)) { attribStore[attrib] = '0'; }\n"
        + "return attribStore[attrib]; \n" + "}\n" + codeSnippet;
    }  
}

// junit测试代码

private Patient p;
private Binding binding;
private GroovyHelper gh;

    @Before 
    public void init() {
        p = new PatientBuilder().build();
        binding = new Binding();
        binding.setVariable("p", p);
        gh = new GroovyHelper();
    }

    @Test //passes
    public void testPopulatePatientAttribStore() throws Exception {
        p.addAttribute("xyz", "4");
        gh.setCodeSnippet("p.getXyz()");
        gh.evaluateSnippetToString(binding);
    }

    @Test
    public void testGroovy() throws Exception {
        Binding binding = new Binding();
        binding.setVariable("p", new Patient()); // new patient
        p.addAttribute("xyz", "9");

        GroovyShell gs1 = new GroovyShell(binding);

        assertEquals("9", gs1.evaluate("p.getXyz()")); // fails??? - expected: <[9]> but was: <[4]>
    }

我的问题是 - 是否关闭了先前绑定的属性存储?     究竟发生了什么?
为所有冗长的代码道歉 - 我为此工作了 - 减少不相关的代码
将它缩小到最小 - 任何指针,“更多阅读要做”提示?

2 个答案:

答案 0 :(得分:1)

我认为问题的一部分是您在methodMissing元类中创建Patient,该代词委托给一位特定患者的attribStore。我会以不同的方式解决问题 - 是否可以直接在methodMissing类本身中实现Patient

public class Patient {
  // other members as before

  public Object methodMissing(String name, Object[] args) {
    if(name != null && name.startsWith("get") && name.length() > 3) {
      String attrName = name.substring(3,1).toLowerCase() + name.substring(4);
      addAttribute(attrName, "0");
      return attribStore.get(attrName);
    } else {
      throw new MissingMethodException(name, this.getClass(), args);
    }
  }
}

或者,如果这不是一个选项,你可以在Groovy中实现GroovyHelper类(使用groovyc编译它,你可以从Java调用它与任何Java类相同)吗?

public class GroovyHelper {
    static {
      // add a methodMissing to the Patient metaclass
      Patient.metaClass.methodMissing = { String name, args ->
        if(name?.startsWith("get") && name.length() > 3) {
          String attrName = name.substring(3,1).toLowerCase() + name.substring(4)
          // delegate here is the particular Patient instance on which the
          // missing method call was made
          delegate.addAttribute(attrName, "0")
          return delegate.attribStore[attrName];
        } else {
          throw new MissingMethodException(name, this.getClass(), args);
        }
      }
    }

    private String codeSnippet // groovy script code

    public String evaluateSnippetToString(Binding binding) {
        GroovyShell shell = createGroovyShell(binding)
        Object result = shell.evaluate(codeSnippet)
        return result.toString()
    }
}

答案 1 :(得分:0)

Patient的MetaClass看起来确实存在b / w GroovyShell次运行 - MetaClassRegistry可能是单例,因此可以在类加载器或JVM的生命周期内生存(可以现在还要仔细考虑一下JVM的工作原理。而且,正如您所建议的那样,我相信您的methodMissing闭包会围绕它最初得到的attribStore变量。 (一个快速测试似乎表明你应该有权访问该闭包内的绑定中的p。我猜你试过这个并且它没有用?)。在拆解方法中,每次测试后,一个选项可能是从Patient强制remove the MetaClass