文档
http://www.groovy-lang.org/design-patterns.html
1、 Patterns
1.1. Abstract Factory Pattern
抽象工厂模式
1.1.1. Example
假设我们要写一个 游戏系统。
我们也许注意到很多游戏都有类似的features和control
我们决定尝试分离 通用的 和 game-specific 代码到不同的类中
首先,我们看一下 Two-up game 的 game-specific 代码
class TwoupMessages {
def welcome = 'Welcome to the twoup game, you start with $1000'
def done = 'Sorry, you have no money left, goodbye'
}
class TwoupInputConverter {
def convert(input) { input.toInteger() }
}
class TwoupControl {
private money = 1000
private random = new Random()
private tossWasHead() {
def next = random.nextInt()
return next % 2 == 0
}
def moreTurns() {
if (money > 0) {
println "You have $money, how much would you like to bet?"
return true
}
false
}
def play(amount) {
def coin1 = tossWasHead()
def coin2 = tossWasHead()
if (coin1 && coin2) {
money += amount
println 'You win'
} else if (!coin1 && !coin2) {
money -= amount
println 'You lose'
} else {
println 'Draw'
}
}
}
然后是 一个 number guessing game 的 game-specific 代码
class GuessGameMessages {
def welcome = 'Welcome to the guessing game, my secret number is between 1 and 100'
def done = 'Correct'
}
class GuessGameInputConverter {
def convert(input) { input.toInteger() }
}
class GuessGameControl {
private lower = 1
private upper = 100
private guess = new Random().nextInt(upper - lower) + lower
def moreTurns() {
def done = (lower == guess || upper == guess)
if (!done) {
println "Enter a number between $lower and $upper"
}
!done
}
def play(nextGuess) {
if (nextGuess <= guess) {
lower = [lower, nextGuess].max()
}
if (nextGuess >= guess) {
upper = [upper, nextGuess].min()
}
}
}
现在,让我们写工厂代码:
def guessFactory = [messages: GuessGameMessages, control: GuessGameControl, converter: GuessGameInputConverter]
def twoupFactory = [messages: TwoupMessages, control: TwoupControl, converter: TwoupInputConverter]
class GameFactory {
def static factory
def static getMessages() { return factory.messages.newInstance() }
def static getControl() { return factory.control.newInstance() }
def static getConverter() { return factory.converter.newInstance() }
}
接下来是我们如何使用这个工厂
GameFactory.factory = twoupFactory
def messages = GameFactory.messages
def control = GameFactory.control
def converter = GameFactory.converter
println messages.welcome
def reader = new BufferedReader(new InputStreamReader(System.in))
while (control.moreTurns()) {
def input = reader.readLine().trim()
control.play(converter.convert(input))
}
println messages.done
1.2. Adapter Pattern
适配器模式,有时也被称为 wrapper模式
1.2.1. Delegation Example
假设我们有下面的类
class SquarePeg {
def width
}
class RoundPeg {
def radius
}
class RoundHole {
def radius
def pegFits(peg) {
peg.radius <= radius
}
String toString() { "RoundHole with radius $radius" }
}
我们可以使用 hole.pegFits(new RoundPeg(radius:4))
但是不能使用 SquarePeg,因为 SquarePeg 没有 radius property
所以我们可以创建一个 Adapter
class SquarePegAdapter {
def peg
def getRadius() {
Math.sqrt(((peg.width / 2) ** 2) * 2)
}
String toString() {
"SquarePegAdapter with peg width $peg.width (and notional radius $radius)"
}
}
现在我们可以使用 hole.pegFits(new SquarePegAdapter(peg: new SquarePeg(width: 4)))
1.2.2. Inheritance Example
接上面的示例,我们也可以使用继承的方式实现Adapter
class SquarePegAdapter extends SquarePeg {
def getRadius() {
Math.sqrt(((width / 2) ** 2) * 2)
}
String toString() {
"SquarePegAdapter with width $width (and notional radius $radius)"
}
}
现在我们可以使用 hole.pegFits(new SquarePegAdapter(width: 4))
1.2.3. Adapting using Closures
接上面的示例,我们也可以Closure的方式实现Adapter
先定义一个接口
interface RoundThing {
def getRadius()
}
然后定义一个Closure
def adapter = {
p -> [getRadius: { Math.sqrt(((p.width / 2) ** 2) * 2) }] as RoundThing
}
现在我们可以使用 hole.pegFits(adapter(new SquarePeg(width: 4)))
1.2.4. Adapting using the ExpandoMetaClass
接上面的示例,我们也可以 ExpandoMetaClass 的方式实现Adapter
def peg = new SquarePeg(width: 4)
peg.metaClass.radius = Math.sqrt(((peg.width / 2) ** 2) * 2)
现在我们可以使用 hole.pegFits(new SquarePeg(width: 4))
1.3. Bouncer Pattern
Bouncer模式描述了一种方法的使用,其唯一目的是要么抛出异常(当特定条件成立时)或者什么都不做。
这种方法通常用于 defensively guard pre-conditions of a method.
编写实用程序方法时,应始终防范错误的输入参数。
在编写内部方法时,您可以通过进行足够的单元测试来确保始终保持某些前提条件。
在这种情况下,您可能会降低对您的方法进行防范的可能性。
在Groovy中你可以频繁的使用 assert
而不是使用大量的checker方法
1.3.1. Null Checking Example
void doStuff(String name, Object value) {
assert name != null, 'name should not be null'
assert value != null, 'value should not be null'
// do stuff
}
1.3.2. Validation Example
def stringDivide(String dividendStr, String divisorStr) {
assert dividendStr =~ NumberChecker.NUMBER_PATTERN
assert divisorStr =~ NumberChecker.NUMBER_PATTERN
def dividend = dividendStr.toDouble()
def divisor = divisorStr.toDouble()
assert divisor != 0, 'Divisor must not be 0'
dividend / divisor
}
1.4. Chain of Responsibility Pattern
责任链模式
1.4.1. Example
class UnixLister {
private nextInLine
UnixLister(next) { nextInLine = next }
def listFiles(dir) {
if (System.getProperty('os.name') == 'Linux') {
println "ls $dir".execute().text
} else {
nextInLine.listFiles(dir)
}
}
}
class WindowsLister {
private nextInLine
WindowsLister(next) { nextInLine = next }
def listFiles(dir) {
if (System.getProperty('os.name') == 'Windows XP') {
println "cmd.exe /c dir $dir".execute().text
} else {
nextInLine.listFiles(dir)
}
}
}
class DefaultLister {
def listFiles(dir) {
new File(dir).eachFile { f -> println f }
}
}
def lister = new UnixLister(new WindowsLister(new DefaultLister()))
lister.listFiles('Downloads')
网友评论