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
|
앞서 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 클래스 몇 개를 제공하고 있다.
[Java] Class TimeCategory
|
'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 |