Skip to content
Skip to breadcrumbs
Skip to header menu
Skip to action menu
Skip to quick search
Quick Search
Browse
Pages
Blog
Labels
Attachments
Mail
Advanced
What’s New
Space Directory
Feed Builder
Keyboard Shortcuts
Confluence Gadgets
Log In
Sign Up
Dashboard
Groovy
Copy Page
You are not logged in. Any changes you make will be marked as
anonymous
. You may want to
Log In
if you already have an account. You can also
Sign Up
for a new account.
This page is being edited by
.
Paragraph
Paragraph
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Preformatted
Quote
Bold
Italic
Underline
More colours
Strikethrough
Subscript
Superscript
Monospace
Clear Formatting
Bullet list
Numbered list
Outdent
Indent
Align left
Align center
Align right
Link
Table
Insert
Insert Content
Image
Link
Attachment
Symbol
Emoticon
Wiki Markup
Horizontal rule
tinymce.confluence.insert_menu.macro_desc
Info
JIRA Issue
Status
Gallery
Tasklist
Table of Contents
Other Macros
Page Layout
No Layout
Two column (simple)
Two column (simple, left sidebar)
Two column (simple, right sidebar)
Three column (simple)
Two column
Two column (left sidebar)
Two column (right sidebar)
Three column
Three column (left and right sidebars)
Undo
Redo
Find/Replace
Keyboard Shortcuts Help
<p>The <a href="http://www.refactoring.com/catalog/replaceInheritanceWithDelegation.html">Replace Inheritance with Delegation</a> refactoring (sometimes known as <em>Replace Implementation Inheritance With Composition</em>) provides a systematic way to replace inheritance hierarchies.</p> <p>Inheritance hierarchies allow system designers to elegantly express relationships between different types of objects in a system. However, such hierarchies can be difficult to implement and refactor. Many developers favour restricting the use of inheritance hierarchies to very simple scenarios. If you started out using inheritance hierarchies (perhaps you had a simple scenario to start with) but are now finding the inheritance hierarchy is now getting in the way, apply this pattern.</p> <h3>Property Example</h3> <p>Suppose we have the following property-centric class:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class Person { def name def age def nationality } </pre></td></tr></table> <p>We might have a related class such as this:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class StaffMemberUsingInheritance extends Person { def salary } </pre></td></tr></table> <p>For this simple case, we can stop here. For more complicated cases, the inheritance might start getting in the way. Here is how you can remove it using traditional delegation:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class StaffMemberUsingDelegation { private delegate = new Person() def salary def getName() { delegate.name } def setName(name) { delegate.name = name } def getAge() { delegate.age } def setAge(age) { delegate.age = age } def getNationality() { delegate.nationality } def setNationality(nationality) { delegate.nationality = nationality } } </pre></td></tr></table> <p>It looks like we have greatly increased the size of our code. This is because the earlier example was making using of Groovy's compact property notation which we can't use in this case. We should notice however, that most of this code is fairly boiler-plate in style.</p> <p>Even though this example is not too bad, it becomes annoying to have to read, write and maintain this boiler-plate code. Instead, we can make use of Groovy's Meta-Programming capabilities as follows:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class StaffMemberUsingMOP { private delegate = new Person() private hasLocalProperty(name) { metaClass.properties.collect{ it.name }.contains(name) } def salary StaffMemberUsingMOP(Map map) { map.each{ k, v -> setProperty(k, v) } } void setProperty(String name, value) { if (hasLocalProperty(name)) this.@"$name" = value else delegate.setProperty(name, value) } def getProperty(String name) { if (hasLocalProperty(name)) return this.@"$name" else return delegate.getProperty(name) } } </pre></td></tr></table> <p>We can use the above classes with this script code:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> def p1 = new StaffMemberUsingInheritance(name:'Tom', age:20, nationality:'French', salary:1000) def p2 = new StaffMemberUsingDelegation(name:'Dick', age:25, nationality:'German', salary:1100) def p3 = new StaffMemberUsingMOP(name:'Harry', age:30, nationality:'Dutch', salary:1200) describe(p1) describe(p2) describe(p3) </pre></td></tr></table> <p>With the result being:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> Tom has a salary of 1000 Dick has a salary of 1100 Harry has a salary of 1200 </pre></td></tr></table> <h3>Method Example</h3> <p>The above example focussed on classes that were very data centric and were implemented using properties. Let's consider the a similar example but this time with classes which are method centric.</p> <p>First, we define a <code>Person</code> class.</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class Person { private name private age Person(name, age) { this.name = name this.age = age } def haveBirthday() { age++ } def describe() { "$name is $age years old" } } </pre></td></tr></table> <p>We can use inheritance to define a staff member class as follows:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class StaffMemberUsingInheritance extends Person { private salary StaffMemberUsingInheritance(name, age, salary) { super(name, age) this.salary = salary } def describe() { super.describe() + " and has a salary of $salary" } } </pre></td></tr></table> <p>This works well here, but in complex systems the inheritance might start to complicate our system. An alternative way to implement the functionality using traditional delegation is shown here:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class StaffMemberUsingDelegation { private delegate private salary StaffMemberUsingDelegation(name, age, salary) { delegate = new Person(name, age) this.salary = salary } def haveBirthday() { delegate.haveBirthday() } def describe() { delegate.describe() + " and has a salary of $salary" } } </pre></td></tr></table> <p>The pattern here is simple, for each method in the delegate class that we want available in the staff member class, we create a method that explicitly calls the delegate. Simple but this is boiler-plate code that needs to change whenever we change the underlying classes.</p> <p>So, as an alternative, we can use Groovy's Meta Object Programming facilities to auto delegate methods using <code>invokeMethod</code>:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class StaffMemberUsingMOP { private delegate private salary StaffMemberUsingMOP(name, age, salary) { delegate = new Person(name, age) this.salary = salary } def invokeMethod(String name, args) { delegate.invokeMethod(name, args) } def describe() { delegate.describe() + " and has a salary of $salary" } } </pre></td></tr></table> <p>The result in this case looks like we didn't save much code at all. If however, there were lots of methods from the delegate that we needed to use, this one wouldn't grow in size, whereas the previous approach would become larger. Also, this version will require less maintenance as we don't need to explicitly change it if the <code>Person</code> class changes over time. Note that because the <code>describe</code> method didn't follow the <em>boiler-plate</em> approach of simple delegation (because it needed extra logic), we are still required to implement that method manually.</p> <p>This script code shows the various classes described above in action:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> def p1 = new StaffMemberUsingInheritance('Tom', 20, 1000) def p2 = new StaffMemberUsingDelegation('Dick', 25, 1100) def p3 = new StaffMemberUsingMOP('Harry', 30, 1200) p1.haveBirthday() println p1.describe() p2.haveBirthday() println p2.describe() p3.haveBirthday() println p3.describe() </pre></td></tr></table> <p>Which results in the following output:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> Tom is 21 years old and has a salary of 1000 Dick is 26 years old and has a salary of 1100 Harry is 31 years old and has a salary of 1200 </pre></td></tr></table> <h3>Further Information on this Pattern</h3> <ul> <li><a class="confluence-link" href="/display/GROOVY/Delegation+Pattern" data-linked-resource-id="4882444" data-linked-resource-type="page" data-linked-resource-default-alias="Delegation Pattern" data-base-url="http://docs.codehaus.org">Delegation Pattern</a></li> <li><a href="http://ivan.truemesh.com/archives/000490.html">Replacing Inheritance with Composition</a></li> </ul>
Please type the word appearing in the picture.
Attachments
Labels
Location
Watch this page
< Edit
Preview >
Loading…
Save
Cancel
Next hint
search
attachments
weblink
advanced