Skip to content

Instantly share code, notes, and snippets.

@turboBasic
Last active November 7, 2021 17:06
Show Gist options
  • Select an option

  • Save turboBasic/644a01b6b998ed041ca4af0de3e9f714 to your computer and use it in GitHub Desktop.

Select an option

Save turboBasic/644a01b6b998ed041ca4af0de3e9f714 to your computer and use it in GitHub Desktop.
Use static factory to dynamically change behavior of interface #groovy #design-pattern

Example of Dependency Injection usage in Groovy

This is to demonstrate how to dynamically switch implementations of interface by combination of DI and Factory

Files

src
├╴xyz
│ └╴bebee
│   └╴example
│     └╴groovy
│       ├ ObjectDescription.groovy              Interface
│       ├ BaseObjectDescription.groovy          Basic implementation of interface
│       ├ StandardObjectDescription.groovy      Advanced implementation of interface
│       └ DescriptionFactory.groovy             Factory which creates / switches active
│                                                   instance of ‘ObjectDescription’ interface
├ client_demo.groovy                            Client code
└ README.md                                     This file

Tasks

  • TODO replace Gist with standard repository
  • TODO leave short summary instead of Gist with link to repository
package xyz.bebee.example.groovy
/**
* Implements bare minimum description of object
*/
class BaseObjectDescription implements ObjectDescription {
final def object
BaseObjectDescription(def object) {
this.object = object
}
List<String> getAttributes() {
[]
}
String getPackageName() {
getPackage()?.name ?: '<not in package>'
}
String getStringValue() {
object.toString()
}
String getTypeName() {
getObjectClass().getName()
}
String toString() {
"""
object type: ${typeName}
package of type definition: ${packageName}
object type attributes: ${attributes.join ', '}
object value: ${stringValue}
"""
.stripIndent().trim()
}
protected java.lang.Class getObjectClass() {
object.getClass()
}
protected java.lang.Package getPackage() {
getObjectClass().getPackage()
}
}
@Library('Examples@develop')
import xyz.bebee.example.groovy.ObjectDescription
import xyz.bebee.example.groovy.BaseObjectDescription
import xyz.bebee.example.groovy.StandardObjectDescription
import xyz.bebee.example.groovy.DescriptionFactory
/**
* Client script.
*
* Usage examples for xyz.bebee.example.groovy.ObjectDescription interface and
* its dynamycally-switched implementations
*/
/** Example 1: instance of locally-defined class */
class LocalClass {
String xxx
List someList
String toString() {
"{ xxx: ${xxx}, someList: ${someList} }"
}
}
def classInstance = new LocalClass(xxx: '12345', someList: [[[[:]]]])
/** Example 2: Java array */
int[] javaArrayInstance = (1..5).toArray()
/**
* We are applying two diferent implementations of ObjectDescription interface
* to the same list of objects and comparing the difference
*/
[
{ new BaseObjectDescription(it) },
{ new StandardObjectDescription(it) },
]
.each {
Closure generator ->
/** Initialize Factory with different implementations of interface */
DescriptionFactory.init( generator )
println "INFO: DescriptionFactory successfully initialized with ${ generator.call('string').class } \n"
[
classInstance,
javaArrayInstance,
false,
-1,
'',
null,
[null],
]
.each {
def object ->
println DescriptionFactory.getDescriptionInstance( object )
}
}
package xyz.bebee.example.groovy
/**
* Factory which creates differently implemented instances
* of ObjectDescription interface and uses Dependency Injection
* so that client has full control in chosing active implementation.
*
* Instance of ObjectDescription is generated by Closure which
* is provided by Client
*
* Inspired by https://enterprisecraftsmanship.com/posts/singleton-vs-dependency-injection/
*/
class DescriptionFactory {
private static Closure<ObjectDescription> createInstance
static ObjectDescription getDescriptionInstance(Object o) {
createInstance.call(o)
}
static void init(Closure<ObjectDescription> createInstanceFunction) {
// TODO this should be synchronized
createInstance = createInstanceFunction
}
}
package xyz.bebee.example.groovy
/**
* Provides description of class/type of object
*/
interface ObjectDescription {
List<String> getAttributes()
String getPackageName()
String getStringValue()
String getTypeName()
}
package xyz.bebee.example.groovy
/**
* Implements advanced description of object
* (attributes etc.)
*/
class StandardObjectDescription extends BaseObjectDescription {
StandardObjectDescription(def object) {
super(object)
}
@Override
List<String> getAttributes() {
List<String> attributes = []
def theClass = getObjectClass()
[
[ { it in org.codehaus.groovy.runtime.NullObject }, 'null' ],
[ { it in java.lang.Number }, 'number' ],
[ { it in java.io.Serializable }, 'serializable' ],
[ {
[
org.codehaus.groovy.runtime.NullObject,
Boolean,
Byte,
Character,
Float,
Double,
Short,
Integer,
Long,
String,
]
.any { type -> it in type }
}, 'primitive' ],
[ {
it.isArray() ||
it in Collection ||
it in Map
}, 'container' ],
[ { it.isArray() }, 'array' ],
[ { it in Map }, 'map' ],
[ { !it.getPackage() }, 'local class' ],
]
.each { check ->
if ( check.first().call(theClass) ) {
attributes << check.last()
}
}
return attributes
}
@Override
String getPackageName() {
getObjectClass().isArray()
/*Y*/ ? getPackage()?.getName() ?: '<local file>'
/*N*/ : getPackage()?.getName() ?: '<local file>'
}
@Override
String getTypeName() {
getObjectClass().getCanonicalName()
}
@Override
String getStringValue() {
object instanceof org.codehaus.groovy.runtime.NullObject
/*Y*/ ? '<NULL>'
/*N*/ : "<${object}>"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment