本篇文章旨在突出Groovy的闭包和动态类型特性,所以建议阅读前有相关知识结构。
设计模式与JAVA的默契程度不可小视,但部分模式使用Groovy的闭包和动态类型特性来实现更显得轻巧与灵活。
下面使用JAVA和Groovy分别实现Visitor模式,并分析两者的优劣:
首先是Visitor模式的定义及使用场景:
GoF对访问者模式定义为:表示一个作用于某对象结构中各元素的操作。它可以使你不修改各元素类的前提下定义作用于这些元素的新操作,也就是动态的增加新的方法。
Visitor模式是一种分离对象数据结构与行为的方法,通过这种分离,可以为一个已存在的类或类群增加新的操作而无需为它们作任何修改。
Visitor模式的优点:
- 分离对象的数据结构与行为,让不同的类完成不同的功能
- 可以不修改已有类的基础上增加新的操作行为
- 从另一个角度来看,同一个数据结构,为其实现不同的观察者,便可呈现不同的行为
例子很简单,以图形类基础,扩展不同图形,并实现各种图形的面积计算方法。使用Visitor模式将不同图形集合类实现不同的商业逻辑行为。
JAVA实现:
import java.util.ArrayList;
import java.util.List;
//绘制图形类
public class Drawing {
//图形集合
List<Shape> shapes = new ArrayList<Shape>();
//留给客户端调用的方法
public void accept(Visitor v){
for(Shape shape : shapes){
shape.accept(v);
}
}
//添加图形
public void addShape(Shape shape){
shapes.add(shape);
}
//删除图形
public void removeShape(Shape shape){
shapes.remove(shape);
}
//Main方法
public static void main(String[] args){
Drawing draw = new Drawing();
draw.addShape(new Square(1));
draw.addShape(new Circle(1));
//VisitorA的商业逻辑
VisitorA visitorA = new VisitorA();//←
draw.accept(visitorA);
System.out.println("VisitorA : Total : " + visitorA.getTotalArea());//←
//VisitorB的商业逻辑
Visitor visitorB = new VisitorB();
draw.accept(visitorB);
}
}
//Visitor模式接口
interface Visitor{
public void visit(Shape shape);
}
//Visitor的一个实现类:用于计算图形集合的面积综合
class VisitorA implements Visitor {
double totalArea;//←
public double getTotalArea() {
return totalArea;
}
@Override
public void visit(Shape shape) {
totalArea += shape.area();
}
}
//Visitor的另一个实现类:用于输出图形集合中每个图形的面积
class VisitorB implements Visitor {
@Override
public void visit(Shape shape) {
System.out.println("VisitorB : " + shape.getClass().getName() + " : " + shape.area());
}
}
//图形父类
abstract class Shape {
public void accept(Visitor v){ v.visit(this);}
//面积计算方法
public abstract double area();
}
//正方形
class Square extends Shape {
double width;
Square(double width){
this.width = width;
}
public double area(){
return Math.pow(width, 2);
}
}
//圆形
class Circle extends Shape {
double radius;
Circle(double radius){
this.radius = radius;
}
public double area(){
return Math.PI * Math.pow(radius ,2);
}
}
输出:
VisitorA : Total : 4.141592653589793
VisitorB : Square : 1.0
VisitorB : Circle : 3.141592653589793
Groovy实现:
//绘制图形类
class Drawing {
//图形集合
def List shapes
//留给客户端调用的方法,参数为闭包类型
def accept(Closure yield) { shapes.each {it.accept(yield)}}//利用Collection的each方法,使用闭包循环调用集合内图形
//Main方法
static void main(args) {
//构造方法都可以在运行时生成,果然是动态语言
def picture = new Drawing(shapes : [new Square(width:1),new Circle(radius:1)])
def total = 0
//使用闭包计算图形集合的面积综合
picture.accept {total += it.area()}//←
println "VisitorA : Total : $total"
//使用闭包输出图形集合中每个图形的面积
picture.accept {println "VisitorB : ${it.class.getName()} : ${it.area()}"}
}
}
//图形父类
class Shape {
def accept(Closure yield) { yield(this) }//有是闭包
}
//正方形
class Square extends Shape {
def width
def area() { width ** 2 }
}
//圆形
class Circle extends Shape {
def radius
def area() { Math.PI * radius ** 2}
}
输出:
VisitorA : Total : 4.141592653589793
VisitorB : Square : 1
VisitorB : Circle : 3.141592653589793
分析:
1.先不用深入看代码,单从代码行数上来看Groovy就有明显的优势,5:2的比例,生产效率远大于JAVA。
2.请注意被【←】标注的代码行,有没有看出什么问题。是的,Groovy的闭包可以轻松使用被调用者的局部变量;而JAVA呢,为使Visitor实现类内的属性暴露给被调用者,不得不用丑陋的VisitorA visitorA = new VisitorA()来声明而破坏了接口的统一性。
3.代码中Groovy的动态生成构造函数等很多特性也显示了它就是为敏捷而生的。