如有转载,请申明:
转载至 http://blog.csdn.net/qq_35064774/article/details/52080149
1 什么是合成模式
合成模式属于对象的结构模式。合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式可以使客户端将单纯元素与复合元素同等看待。
这定义看着太抽象了,我们举个栗子。
在二维图形中,线、圆、矩形等都可以看作是图形,而通过简单图形组合起来的图形也可以看作图形。
2 如何实现合成模式
合成模式可分为安全式和透明式。
简单说来就是,透明式是对树枝树叶类提供相同的接口,树叶类也有管理聚合的方法。安全式则是只有树枝类有管理聚合的方法。
这里简单解释一下树枝和树叶类。就拿上面举的例子,线、圆、矩形这些简单图形看作是树叶类,也就是不是由对象组合而成的对象。而复杂的组合图形就是树枝类。
为了方便书写代码,我们引入一个问题。
* 现有一个矢量图绘图程序
* 支持画线、椭圆、矩形
* 我们可以通过这三种图形组合出其他图形
* 现在想把这个组合出来的图形当成一个新的图形
拿到这个问题,我们很容易就想到要用合成模式。
针对这个问题,我们大致需要定义抽象的图形类、线类、椭圆类、矩形类等。
首先定义抽象类。
package com.ittianyu.composite;
import java.awt.Graphics;
public interface Drawable {
void draw(Graphics g);
}
这里定了为一个接口,所有图形都应该有一个方法draw,这个参数是awt中用来自定义绘图的对象,如果学过javaGUI的就会明白了,不明白也没关系,这里只是为了视觉效果才用的。
接下来我们定义简单的图像类。这些类主要是属性和draw方法有区别,所以这里不再过多解释。
package com.ittianyu.composite;
import java.awt.Graphics;
public class Line implements Drawable {
private int x1, y1;
private int x2, y2;
public Line(int x1, int y1, int x2, int y2) {
super();
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
@Override
public void draw(Graphics g) {
g.drawLine(x1, y1, x2, y2);
}
}
package com.ittianyu.composite;
import java.awt.Graphics;
public class Oval implements Drawable {
private int x, y;
private int width, height;
public Oval(int x, int y, int width, int height) {
super();
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
@Override
public void draw(Graphics g) {
g.drawOval(x, y, width, height);
}
}
package com.ittianyu.composite;
import java.awt.Graphics;
public class Rectangle implements Drawable {
private int x, y;
private int width, height;
public Rectangle(int x, int y, int width, int height) {
super();
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
@Override
public void draw(Graphics g) {
g.drawRect(x, y, width, height);
}
}
有了简单图形后,我们还需要一个类似于容器类的图形类,来作为组合图形。
package com.ittianyu.composite;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
public class Graph implements Drawable {
private List<Drawable> graphs = new ArrayList<Drawable>();
@Override
public void draw(Graphics g) {
for(int i = 0; i < graphs.size(); i++)
graphs.get(i).draw(g);
}
public Graph add(Drawable d) {
graphs.add(d);
return this;
}
public Graph remove(Drawable d) {
graphs.remove(d);
return this;
}
public Drawable get(int index) {
return graphs.get(index);
}
}
我们可以发现,这个容器类也实现了Drawable ,也就是可以被外界当作图形类。这是组合最关键的思想。
其次这个组合类提供了add、remove、get等的聚合管理方法,我们就可以方便的把其他图形加入。
代码写到这里基本就实现了组合模式。最后还需要测试,由于我这里想直接在窗口里面绘制图形,所以还需要一个窗口类和测试类。
package com.ittianyu.composite;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class DrawFrame extends Frame {
private Drawable draw;
public DrawFrame(String title) throws HeadlessException {
super(title);
this.setVisible(true);
this.setSize(800, 600);
// 响应关闭窗口事件
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosed(e);
System.exit(0);
}
});
}
public Drawable getDraw() {
return draw;
}
public void setDraw(Drawable draw) {
this.draw = draw;
this.repaint();
}
@Override
public void paint(Graphics g) {
if(null == draw)
return;
draw.draw(g);
}
}
定义这个窗口类,里面可以设置Drawable 对象,并在paint中调用。
最后一步,写一个测试类。
package com.ittianyu.composite;
public class Test {
public static void main(String[] args) {
Graph graph = new Graph();
graph.add(new Line(0, 0, 800, 600))
.add(new Oval(100, 100, 200, 150))
.add(new Rectangle(400, 300, 300, 200));
DrawFrame frame = new DrawFrame("绘图测试");
frame.setDraw(graph);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
frame.setDraw(new Oval(200, 150, 400, 300));
}
}
可以发现,在窗口中,我们既可以绘制简单图形,也可以绘制组合图形。
3 在什么情况下使用合成模式
* 在把对象和由对象组合而成的复杂对象都看作同一类对象时,使用合成模式比较合适。
4 合成模式的优点和缺点
优点:
* 增加新种类构建变得轻松
* 使用时不需要知道当前使用的是树枝接点还是树叶接点,也就是不需要知道对象是不是组合而成的。
缺点:
* 控制树枝构建的类型不太容易。
* 继承的方式完成新功能的增加变得困难。