1. Trang chủ
  2. » Công Nghệ Thông Tin

Programming Groovy dynamic productivity for the java developer phần 8 potx

31 221 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Programming Groovy Dynamic Productivity For The Java Developer Phần 8
Trường học University of Technology
Chuyên ngành Computer Science
Thể loại Bài báo
Năm xuất bản 2023
Thành phố Hanoi
Định dạng
Số trang 31
Dung lượng 177,15 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

METHODSYNTHESISUSINGEXPANDOMETACLASS 21914.5 Method Synthesis Using ExpandoMetaClass In Section 14.4, Method Synthesis Using methodMissing, on page214, you saw how to synthesize methods.

Trang 1

METHODSYNTHESISUSING METHODMISSING 218

System.out println "methodMissing called for $name"

def methodInList = plays.find { it == name.split( 'play' )[1]}

if (methodInList)

{

def impl = { Object[] vargs ->

return "playing ${name.split('play')[1]} "

The output from the previous code is as follows:

intercepting call for work

working

intercepting call for playTennis

methodMissing called for playTennis

playing Tennis

intercepting call for playTennis

playing Tennis

Trang 2

METHODSYNTHESISUSINGEXPANDOMETACLASS 219

14.5 Method Synthesis Using ExpandoMetaClass

In Section 14.4, Method Synthesis Using methodMissing, on page214,

you saw how to synthesize methods If you don’t have the privilege to

edit the class source file or if the class is not a POGO, that approach

will not work You can synthesize methods using theExpandoMetaClass

in these cases

You already saw how to interact with MetaClass in Section 13.2,

Inter-cepting Methods Using MetaClass, on page 197 Instead of providing

an interceptor for a domain method, you implement themethodMissing( )

method on it Let’s take thePersonclass (and the boringjack) from

Sec-tion 14.4, Method Synthesis Using methodMissing, on page 214, but

instead we’ll useExpandoMetaClass, as shown here:

Person.metaClass.methodMissing = { String name, args ->

def plays = [ 'Tennis' , 'VolleyBall' , 'BasketBall' ]

System.out println "methodMissing called for $name"

def methodInList = plays.find { it == name.split( 'play' )[1]}

if (methodInList)

{

def impl = { Object[] vargs ->

return "playing ${name.split('play')[1]} "

Trang 3

METHODSYNTHESISUSINGEXPANDOMETACLASS 220

No signature of method: Person.playPolitics()

is applicable for argument types: () values: {}

When you called work( ) on jack, Person’s work( ) was executed directly

If you call a nonexistent method, however, it is routed to the Person’s

MetaClass’smethodMissing( ).4You implement logic in this method similar

to the solution in Section14.4, Method Synthesis Using methodMissing,

on page 214 Repeated calls to supported nonexistent method do not

incur overhead, as you can see in the previous output for the second

call toplayTennis( ) You cached the implementation on the first call

In Section 13.2, Intercepting Methods Using MetaClass, on page 197,

you intercepted calls usingExpandoMetaClass’sinvokeMethod( ) You can

mix that withmethodMissing( ) to intercept calls to both existing methods

and synthesized methods, as shown here:

Person.metaClass.invokeMethod = { String name, args ->

System.out println "intercepting call for ${name}"

def method = Person.metaClass.getMetaMethod(name, args)

4 methodMissing ( ) of the MetaClass will take precedence over methodMissing ( ) if present in

your class Methods of your class’s MetaClass override the methods in your class.

Trang 4

METHODSYNTHESISUSINGEXPANDOMETACLASS 221

Person.metaClass.methodMissing = { String name, args ->

def plays = [ 'Tennis' , 'VolleyBall' , 'BasketBall' ]

System.out println "methodMissing called for ${name}"

def methodInList = plays.find { it == name.split( 'play' )[1]}

if (methodInList)

{

def impl = { Object[] vargs ->

return "playing ${name.split('play')[1]} "

The output from the previous code is as follows:

intercepting call for work

working

intercepting call for playTennis

methodMissing called for playTennis

playing Tennis

intercepting call for playTennis

playing Tennis

Trang 5

SYNTHESIZINGMETHODS FOR SPECIFICINSTANCES 222

invokeMethod vs methodMissing

invokeMethod( ) is a method of GroovyObject methodMissing( )

was introduced later in Groovy and is part of the

MetaClass-based method handling If your objective is to handle calls to

nonexisting methods, implementmethodMissing( ) because this

involves low overhead If your objective is to intercept calls to

both existing and nonexisting methods, useinvokeMethod( )

14.6 Synthesizing Methods for Specific Instances

I showed how you can inject methods into specific instances of a class

in Section 14.3, Injecting Methods into Specific Instances, on page212

You can synthesize methods dynamically as well as into specific

in-stances by providing the instance(s) with a specialized MetaClass Here

is an example:

Download InjectionAndSynthesisWithMOP/SynthesizeInstance.groovy

class Person {}

def emc = new ExpandoMetaClass(Person)

emc.methodMissing = { String name, args ->

"I'm Jack of all trades I can $name"

}

emc.initialize()

def jack = new Person()

def paul = new Person()

Trang 6

SYNTHESIZINGMETHODS FOR SPECIFICINSTANCES 223

The previous code reports the following:

I'm Jack of all trades I can sing

I'm Jack of all trades I can dance

I'm Jack of all trades I can juggle

groovy.lang.MissingMethodException:

No signature of method: Person.sing()

is applicable for argument types: () values: {}

Like injecting into specific instances, synthesizing methods for specific

instances is limited to Groovy objects

In this chapter, you learned how to intercept, inject, and synthesize

methods Groovy MOP makes it easy to perform AOP-like activities

You can create code that is highly dynamic, and you can create highly

reusable code with fewer lines of code You’ll put all these skills together

in the next chapter

Trang 7

Chapter 15

MOPping Up

You’ve seen how to synthesize methods, and in this chapter, you’ll seehow to synthesize an entire class Rather than creating explicit classesahead of time, you can create classes on the fly, which gives you moreflexibility Delegation is better than inheritance, yet it has been hard toimplement in Java You’ll see how Groovy MOP allows method delega-tion with only one line of code I’ll wrap this chapter up by reviewing thedifferent MOP techniques you’ve seen in the previous three chapters

15.1 Creating Dynamic Classes with Expando

In Groovy you can create a class entirely at runtime Suppose you’rebuilding an application that will configure devices You don’t have aclue what these devices are—you know only that devices have proper-ties and configuration scripts You don’t have the luxury of creating anexplicit class for each device at coding time So, you’ll want to synthe-size classes at runtime to interact with and configure these devices InGroovy, classes can come to life at runtime at your command

The Groovy class that gives you the ability to synthesize classes ically isExpando, which got its name because it is dynamically expand-able You can assign properties and methods to it either at construc-tion time using a Map or at any time dynamically Let’s start with anexample to synthesize a classCar I’ll show two ways to create it usingExpando

Trang 8

dynam-CREATINGDYNAMICCLASSES WITHEXPANDO 225

Download MOPpingUp/UsingExpando.groovy

carA = new Expando()

carB = new Expando(year: 2007, miles: 0)

carA.year = 2007

carA.miles = 10

println "carA: " + carA

println "carB: " + carB

The output from the previous code is as follows:

carA: {year=2007, miles=10}

carB: {year=2007, miles=0}

You createdcarA, the first instance ofExpando, without any properties

or methods You injected the year and miles later On the other hand,

you created carB, the second instance of Expando, with the year and

milesinitialized at construction time

You’re not restricted to properties You can define methods as well and

invoke them like you would invoke any method Let’s give that a try

Once again, you can define a method at construction time or inject

You can easily work with Car objects without explicitly creating a Car

class, as in the following code You’re parsing the content of the file, first

Trang 9

CREATINGDYNAMICCLASSES WITHEXPANDO 226

extracting the property names Then you create instances of Expando,

one for each line of data in the input file, and populate it with values

for the properties You even add a method, in the form of a closure, to

compute the average miles driven per year until 2008 Once the objects

are created, you can access the properties and call methods on them

dynamically You can also address the methods/properties by name, as

shown in the end

car = new Expando()

it.split( ", " ).eachWithIndex { value, index ->

props.each { name -> print "$name " }

println " Avg MPY"

ampyMethod = 'ampy'

cars.each { car ->

for (String property : props) { print "${car[property]} " }

println car "$ampyMethod" ()

}

// You may also access the properties/methods by name

car = cars[0]

println "$car.miles $car.year $car.make ${car.ampy()}"

The output from the previous code is as follows:

miles year make Avg MPY

42451 2003 Acura 8490.2

24031 2003 Chevy 4806.2

14233 2006 Honda 7116.5

42451 2003 Acura 8490.2

Trang 10

METHODDELEGATION: PUTTINGITALLTOGETHER 227

Use Expando whenever you want to synthesize classes on the fly It is

lightweight and flexible One place where you will see them shine is to

create mock objects for unit testing (see Section 16.8, Mocking Using

Expando, on page251)

15.2 Method Delegation: Putting It All Together

You use inheritance to extend the behavior of a class On the other

hand, you use delegation to rely upon contained or aggregated objects

to provide the behavior of a class Choose inheritance if your intent is to

use an object in place of another object Choose delegation if the intent

is to simply use an object Reserve inheritance for an is-a or kind-of

relationship only; you should prefer delegation over inheritance most

of the time However, it’s easy to program inheritance, because it takes

only one keyword,extends But it’s hard to program delegation, because

you have to write all those methods that route the call to the contained

objects Groovy helps you do the right thing By using MOP, you can

easily implement delegation with a single line of code, as you’ll see in

this section

In the following example, a Manager wants to delegate work to either

a Worker or an Expert You’re using methodMissing( ) and

ExpandoMeta-Classto realize this If a method called on the instance ofManagerdoes

not exist, itsmethodMissing( ) routes it to either theWorkeror theExpert,

whicheverrespondsTo( ) to the method (see Section12.2, Querying

Meth-ods and Properties, on page 190) If there are no takers for a method

among the delegates and the Manager does not handle it, the method

def simpleWork1(spec) { println "worker does work1 with spec $spec" }

def simpleWork2() { println "worker does work2" }

}

class Expert

{

def advancedWork1(spec) { println "Expert does work1 with spec $spec" }

def advancedWork2(scope, spec)

{

println "Expert does work2 with scope $scope spec $spec"

}

}

Trang 11

METHODDELEGATION: PUTTINGITALLTOGETHER 228

class Manager

{

def worker = new Worker()

def expert = new Expert()

def schedule() { println "Scheduling " }

def methodMissing(String name, args)

{

println "intercepting call to $name "

def delegateTo = null

if (name.startsWith( 'simple' )) { delegateTo = worker }

if (name.startsWith( 'advanced' )) { delegateTo = expert }

if (delegateTo?.metaClass.respondsTo(delegateTo, name, args))

{

Manager.metaClass "${name}" = { Object[] varArgs ->

return delegateTo.invokeMethod(name, *varArgs)

peter.advancedWork2( 'protype' , 'fast' )

peter.advancedWork2( 'product' , 'quality' )

intercepting call to simpleWork1

worker does work1 with spec fast

worker does work1 with spec quality

Trang 12

METHODDELEGATION: PUTTINGITALLTOGETHER 229

intercepting call to simpleWork2

worker does work2

worker does work2

intercepting call to advancedWork1

Expert does work1 with spec fast

Expert does work1 with spec quality

intercepting call to advancedWork2

Expert does work2 with scope protype spec fast

Expert does work2 with scope product spec quality

intercepting call to simpleWork3

groovy.lang.MissingMethodException:

No signature of method: Manager.simpleWork3()

is applicable for argument types: () values: {}

You figured out a way to delegate calls, but that’s a lot of work You

don’t want to put in so much effort each time you want to delegate You

can refactor this code for reuse Let’s first look at how the refactored

code will look like when used in theManagerclass:

Download MOPpingUp/DelegationRefactored.groovy

class Manager

{

{ delegateCallsTo Worker, Expert, GregorianCalendar }

def schedule() { println "Scheduling " }

}

That is short and sweet In the initializer block you call a

yet-to-be-implemented method named delegateCallsTo( ) and send the names of

classes to which you want to delegate unimplemented methods If you

want to use delegation in another class, all it takes now is that code in

the initialization block Let’s take a look at the fancy delegateCallsTo( )

method:

Download MOPpingUp/DelegationRefactored.groovy

ExpandoMetaClass.enableGlobally()

Object.metaClass.delegateCallsTo = {Class klassOfDelegates ->

def objectOfDelegates = klassOfDelegates.collect { it.newInstance() }

delegate.metaClass.methodMissing = { String name, args ->

println "intercepting call to $name "

def delegateTo = objectOfDelegates.find {

it.metaClass.respondsTo(it, name, args) }

Trang 13

METHODDELEGATION: PUTTINGITALLTOGETHER 230

if (delegateTo)

{

delegate.metaClass "${name}" = { Object[] varArgs ->

def params = varArgs?: null

return delegateTo.invokeMethod(name, *params)

When you call delegateCallsTo( ) from within your class’s instance

ini-tializer, it adds a methodMissing( ) to the class, which is known within

this closure asdelegate It takes theClasslist provided as an argument

to delegateCallsTo( ) and creates a list of delegates, which are the

can-didates to implement delegated methods In methodMissing( ), the call

is routed to an object among the delegates that will respond to the

method If there are no takers, the call fails The list of classes given to

delegateCallsTo( ) also represents the order of precedence, and the first

one has the highest precedence Of course, you have to see all this in

action, so here is the code to exercise the previous example:

peter.advancedWork2( 'protype' , 'fast' )

peter.advancedWork2( 'product' , 'quality' )

println "Is 2008 a leap year? " + peter.isLeapYear(2008)

Trang 14

REVIEW OFMOP TECHNIQUES 231

The previous code produces the following output:

Scheduling

intercepting call to simpleWork1

worker does work1 with spec fast

worker does work1 with spec quality

intercepting call to simpleWork2

worker does work2

worker does work2

intercepting call to advancedWork1

Expert does work1 with spec fast

Expert does work1 with spec quality

intercepting call to advancedWork2

Expert does work2 with scope protype spec fast

Expert does work2 with scope product spec quality

intercepting call to isLeapYear

Is 2008 a leap year? true

intercepting call to simpleWork3

groovy.lang.MissingMethodException:

No signature of method: Manager.simpleWork3()

is applicable for argument types: () values: {}

You can build on this idea further to meet your needs For instance,

if you want to mix some precreated objects, you can send them as an

array to the first parameter ofdelegateCallsTo( ) and have those objects

used along with those created from the delegates classes The previous

example shows how you can use Groovy’s MOP to implement dynamic

behavior such as method delegation

15.3 Review of MOP Techniques

You’ve seen a number of options to intercept, inject, and synthesize

methods In this section, you’ll figure out which option is right for you

Options for Method Interception

I discussed method interception in Chapter 13, Intercepting Methods

Using MOP, on page 194and in Section 14.1, Injecting Methods Using

Categories, on page203 You can useGroovyInterceptable,

ExpandoMeta-Class, or categories

If you have the privilege to modify the class source, you can implement

GroovyInterceptableon the class you want to intercept method calls The

effort is as simple as implementinginvokeMethod( )

If you can’t modify the class or if the class is a Java class, then you can

use ExpandoMetaClass or categories ExpandoMetaClass clearly stands

Trang 15

REVIEW OFMOP TECHNIQUES 232

out in this case because a singleinvokeMethod( ) can take care of

inter-cepting any methods of your class Categories, on the other hand,

would require separate methods, one per intercepted method Also, if

you use categories, you’re restricted by theuse( ) block

Options for Method Injection

I discussed method injection in Section 14.1, Injecting Methods Using

Categories, on page203 You can use categories orExpandoMetaClass

Categories compete well withExpandoMetaClassesfor method injection

If you use categories, you can control the location where methods are

injected You can easily implement different versions of method

injec-tion by using different categories You can easily nest and mix multiple

categories as well The control offered by categories—that method

injec-tion takes effect only within theuse( ) blocks and is limited to the

exe-cuting thread—may also be considered as a restriction If you want to

use the injected methods at any location and also want to inject static

method and constructors,ExpandoMetaClassis a better choice Beware,

though, thatExpandoMetaClassis not the default MetaClassin Groovy

Using the ExpandoMetaClass, you can inject methods into specific

in-stances of a class instead of affecting the entire class This is available

only for POGOs, however

Options for Method Synthesis

I discussed method injection in Section 14.4, Method Synthesis Using

methodMissing, on page214 You can usemethodMissing( ) on a Groovy

object orExpandoMetaClass

If you have the privilege to modify the class source, you can implement

the methodMissing( ) method on the class for which you want to

synthe-size methods You can improve performance by injecting the method on

the first call If you need to intercept your methods at the same time,

you can implementGroovyInterceptable

If you can’t modify the class or if the class is a Java class, then you

can add the method methodMissing( ) to the class’s ExpandoMetaClass

If you want to intercept method calls at the same time, implement

invokeMethod( ) on theExpandoMetaClassas well

Using the ExpandoMetaClass, you can synthesize methods into specific

instances of a class instead of affecting the entire class This is available

only for POGOs, however

Ngày đăng: 12/08/2014, 23:22