본문 바로가기

Script/Groovy

14. MOP - Runtime

 
MOP (Meta-Object-Protocol) 는  메소드 호출에 대한 요청이 
 
Groovy 런타임 시스템에서 처리되는 방법과 중간 계층을 제어하는 방법에 대한 규칙 모음이다.
 
 
보통 MOP 는 런타임과 컴파일 타임에서 사용되는 형식이 다르며 컴파일 타임 MOP 는 어노테이션 파트로
 
 
다음장에서 살펴보자.
 
 
 
POGO
 
Groovy 는 오브젝트를 크게 3가지 오브젝트로 분할하여 관리한다.
 
이중  GroovyObject 인터페이스를 구현한 클래스를 POGO 오브젝트라고 한다.
 
통상 Groovy 로 선언한 모든 클래스는 GroovyObject 인터페이스를 상속받아 만들어진다.
 
 
 
 
Groovy Decision Tree
 
그리고 Groovy 에서는 Groovy 는 오브젝트 타입에 따라 아래와 같은 Decision Tree 를 가진다.
 
 
 
 
GroovyObject
 
이번에는 GroovyObject 인터페이스를 확인해 보자. 
 
public interface GroovyObject {
  Object invokeMethod(String var1, Object var2);
  Object getProperty(String var1);
  void setProperty(String var1, Object var2);
  MetaClass getMetaClass();
  void setMetaClass(MetaClass var1);
}
 
이 인터페이스에는 여러 메서드가 존재하는데 MOP 를 이해하기 위해서 이를 하나씩 사용해 보도록 하자.
 
 
invokeMethod
 
먼저 invokeMethod 를 오버라이딩 하자.
 
class InvokeDemo {
  def invokeMethod(String name, Object args) {
    return "called_invokeMethod $name $args"
  }
 
public class InvokeDemo implements GroovyObject {
  @Generated
  public InvokeDemo() {
    CallSite[] var1 = $getCallSiteArray();
    super();
    MetaClass var2 = this.$getStaticMetaClass();
    this.metaClass = var2;
  }
 
  public Object invokeMethod(String name, Object args) {
    CallSite[] var3 = $getCallSiteArray();
    return new GStringImpl(new Object[]{name, args}, new String[]{"called_invokeMethod ", " ", ""});
  }
}
 
 
그리고 아래의 코드를 테스트 해보자.
 
이상하게도 someMethod 를 구현하지 않았는데도 불구하고 invokeMethod 가 불려 실행이 성공한다.
 
 
def invokeDemo = new InvokeDemo()
 
assert invokeDemo.someMethod() == 'called_invokeMethod someMethod []'
 
 이처럼 invokeMethod 는 GroovyObject 에 존재하고 있지 않은 Method 가 불리면 자동으로 Call 되는 함수이다.
 
이는 Groovy Decision Tree 상에서 아래와 같이 동작한 것이다.
 
 
 
그렇다면 methodMissing 함수를 정의하면 실패하는지도 한번 확인 차 테스트 해보자.
 
class InvokeDemo {
  def invokeMethod(String name, Object args) {
    return "called_invokeMethod $name $args"
  }
 
  def methodMissing(String name, Object args) {
    return "Method Missing"
  }
}
 
def invokeDemo = new InvokeDemo()
assert invokeDemo.someMethod() == 'called_invokeMethod someMethod []'
 
assert invokeDemo.someMethod() == 'called_invokeMethod someMethod []'
                         |          |          |
                         |          |          false
                         |         'Method Missing'
                     InvokeDemo@6d3a388c
 
실제로 실패하는 것을 확인할 수 있으며 methodMissing 함수가 호출되었다.
 
 
getProperty, setProperty
 
이번에는 getProperty 함수를 오버라이딩 하여 사용해 보자.
 
(setProperty 함수는 이와 방식이 유사하므로 따로 살펴보지는 않는다)
 
그루비에서 property 를 읽는 경우  GroovyObject 의 getProperty 메서드를 통한다.
 
invokeMethod 의 methodMissing 함수와 마찬가지로 propertyMissing 함수를 정의하면 이 함수를 호출한다.
 
class PropertyDemo {
  def prop1 = 'prop1'
  def prop2 = 'prop2'
  def prop3 = 'prop3'
 
  def getProperty(String name){
    println "getProperty() called with some arg $name"
 
    if( metaClass.hasProperty(this,name)) {
      return metaClass.getProperty(this,name)
    } else {
      return 'Property Missing'
    }
  }
}
 
def pd = new PropertyDemo()
println pd.prop3
println pd.prop4
 
getProperty() called with some arg prop3
prop3
 
getProperty() called with some arg prop4
Property Missing
 
 
getMetaClass
 
GroovyObject 는 모두 MetaClass 와 연관되어 있다.
 
MetaClass 는 모든 property 관리를 위한 클래스이며 동적으로 프로퍼티를 할당하게 해준다.
 
class Student {
 
}
 
def student = new Student()
student.metaClass.name = 'Dan'
student.metaClass.testMethod = {
println "$name"
}
 
student.testMethod()
 
Dan
 
 
 
Category Classes
 
앞서 metaClass 를 사용하여 동적으로 property 를 넣었고 closure 도 넣었다.
 
마찬가지로 기존에 있는 클래스에도 아래와 같이 확장할 수 있다.
 
String.metaClass.shout = { -> toUpperCase() }
 
println "Hello, world".shout()
 
HELLO, WORLD
 
문제는 이렇게 기존 클래스를 확장하고 Document 를 잘 작성하지 않는 이상
 
사람들은 해당 method 에 대해 사용상 혼란이 있을 수 있다.
 
그래서 아래와 같이 기존 클래스에 대한 Category 클래스를 설계하고 
 
use 예약어를 사용해 코드 블럭을 만든 후 확장 함수를 사용할 수 있다.
 
class StringCategory {
  static String shout(String str) {
    str.toUpperCase()
  }
}
 
use(StringCategory){
  println "Hello,world".shout()
}
HELLO, WORLD
 
이런 방식으로 Groovy 에서는 Built In Category 클래스 몇 개를 제공하고 있다. 
 
그 예로 http://groovy-lang.org/api.html 에서 TimeCategory 를 찾으면 아래와 같이 설명이 나와 있다.
 

[Java] Class TimeCategory

  • groovy.time.TimeCategory
  • Apply a number of methods to allow convenient Date/Time manipulation,such as:
    use (groovy.time.TimeCategory) {
    // application on numbers:
    println 1.minute.from.now
    println 10.hours.ago
     
    // application on dates
    def someDate = new Date()
    println someDate - 3.months
    }
 
 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

'Script > Groovy' 카테고리의 다른 글

16. Builder  (0) 2020.01.21
15. MOP - Compile Time  (0) 2020.01.21
13. OOP  (0) 2020.01.21
12. Exception  (0) 2020.01.21
11. Conditional Statement  (0) 2020.01.21