Composite
别名:对象树,Object Tree
定义
将对象组合成树状结构来表示 “ 部分 - 整体 ”的层次结构,使用户对单个对象和组合对象的使用具有一致性
应用场景
1.需要实现树状结构(想表达部分-整体层次结构)
组合模式提供了两种共享公共接口的基本元素类型: 简单叶节点和复杂容器。 容器中可以包含叶节点和其他容器。 这使得你可以构建树状嵌套递归对象结构。
2.希望客户端代码以相同方式处理简单和复杂元素
组合模式中定义的所有元素共用同一个接口。 在这一接口下,客户端不必在意其所使用的对象的具体类。
实现方法
1.确保应用的核心模型能够以树状结构表示。 尝试将其分解为 简单元素和容器。 记住, 容器必须能够同时包含简单元素和其他容器。
2.声明组件接口及其一系列方法, 这些方法对简单和复杂元素 都有意义。
3.创建一个叶节点类表示简单元素。 程序中可以有多个不同的 叶节点类。
4.创建一个容器类表示复杂元素。 在该类中, 创建一个数组成 员变量来存储对于其子元素的引用。 该数组必须能够同时保 存叶节点和容器, 因此请确保将其声明为组合接口类型。
实现组件接口方法时, 记住容器应该将大部分工作交给其子 元素来完成。
5.在容器中定义添加和删除子元素的方法。
(记住, 这些操作可在组件接口中声明。 这将会违反接口隔离原则, 因为叶节点类中的这些方法为空。 但是, 这可以让 客户端无差别地访问所有元素, 即使是组成树状结构的元素。
优缺点
优点:
1.你可以利用多态和递归机制更方便地使用复杂树结构
2.开闭原则。 无需更改现有代码, 你就可以在应用中添加新元 素, 使其成为对象树的一部分
缺点:
对于功能差异较大的类, 提供公共接口或许会有困难。 在特 定情况下, 你需要过度一般化组件接口, 使其变得令人难以 理解
结构
透明组合模式
把所有的公有方法都定义在 Component 中
UML图
classDiagram
Client --> Component
Component <|-- Leaf
Composite o-->Component
class Component{
<<interface>>
-children: Component[]
+add(c:Component)
+remove(c:Component)
+getChildren() Compionent[]
+execute()
}
class Leaf{
...
+execute()
}
class Composite{
-children: Component[]
+add(c:Component)
+remove(c:Component)
+getChildren() Compionent[]
+execute()
}
通用写法
public abstract class Component {
protected String name;
public Component(String name){
this.name = name;
}
public abstract String operation();
public boolean addChild(Component component){
throw new UnsupportedOperationException("addChild not supported!");
}
public boolean removeChild(Component component){
throw new UnsupportedOperationException("removeChild not supported!");
}
public boolean getChild(int index){
throw new UnsupportedOperationException("getChild not supported!");
}
}
public class Composite extends Component{
private List<Component> components;
public Composite(String name){
super(name);
this.components = new ArrayList<>();
}
@Override
public String operation() {
StringBuilder builder = new StringBuilder(this.name);
for (Component component : this.components) {
builder.append("\n");
builder.append(component.operation());
}
return builder.toString();
}
@Override
public boolean addChild(Component component) {
if (null == component){
return super.addChild(component);
}
components.add(component);
return true;
}
}
public class Leaf extends Component{
public Leaf(String name){
super(name);
}
@Override
public String operation() {
return this.name;
}
}
public class Client {
public static void main(String[] args) {
//创建一个根节点
Component root = new Composite("root");
//创建一个树枝节点
Component branchA = new Composite("---branchA");
Component branchB = new Composite("------branchB");
//创建一个叶子节点
Component leafA = new Leaf("------leafA");
Component leafB = new Leaf("------------leafB");
Component leafC = new Leaf("---leafC");
root.addChild(branchA);
root.addChild(leafC);
branchA.addChild(leafA);
branchA.addChild(branchB);
branchB.addChild(leafB);
String result = root.operation();
System.out.println(result);
}
}
好处:
无需分辨叶子节点和树枝节点,它们具备完全一致的接口
安全模式
只规定系统各个层次的最基础的一致行为,而把组合本身的方法放在自身当中
classDiagram
Client --> Component
Component <|-- Leaf
Composite o-->Component
class Component{
<<interface>>
+execute()
}
class Leaf{
...
+execute()
}
class Composite{
-children: Component[]
+add(c:Component)
+remove(c:Component)
+getChildren() Compionent[]
+execute()
}
通用写法
//对该接口进行修改,只保留各层次的公共行为
public abstract class Component {
protected String name;
public Component(String name){
this.name = name;
}
public abstract String operation();
}
public class Composite extends Component{
private List<Component> components;
public Composite(String name){
super(name);
this.components = new ArrayList<>();
}
@Override
public String operation() {
StringBuilder builder = new StringBuilder(this.name);
for (Component component : this.components) {
builder.append("\n");
builder.append(component.operation());
}
return builder.toString();
}
public boolean addChild(Component component) {
components.add(component);
return true;
}
public boolean removeChild(Component component){
throw new UnsupportedOperationException("removeChild not supported!");
}
public boolean getChild(int index){
throw new UnsupportedOperationException("getChild not supported!");
}
}
public class Leaf extends Component{
public Leaf(String name){
super(name);
}
@Override
public String operation() {
return this.name;
}
}
public class Client {
public static void main(String[] args) {
//创建一个根节点
Composite root = new Composite("root");
//创建一个树枝节点
Composite branchA = new Composite("---branchA");
Composite branchB = new Composite("------branchB");
//创建一个叶子节点
Component leafA = new Leaf("------leafA");
Component leafB = new Leaf("------------leafB");
Component leafC = new Leaf("---leafC");
root.addChild(branchA);
root.addChild(leafC);
branchA.addChild(leafA);
branchA.addChild(branchB);
branchB.addChild(leafB);
String result = root.operation();
System.out.println(result);
}
}
参与者
1.组件( Component ) :接口描述了树中简单项目和复杂项目所共有的操作
2.叶节点 ( Leaf ) : 是树的基本结构,它不包含子项目( 一般情况下,叶节点最终会完成大部分的实际工作,因为它们将工作指派给其他部分)
3.容器 ( Container ) : 是包含叶节点或其他容器等子项目的单位。容器不知道其子项目所属的具体类,它只通过通用的组件接口与其子项目交互【容器接收到请求后会将工作分配给自己的子项目,处理中间结果,然后将最终结果返回给客户端】
4.客户端 ( Client ) :通过组件接口与所有项目交互。
案例
简单的形状来组成复杂图形,以及如何统一处理简单和复杂图形
//通用形状接口
public interface Shape {
int getX();
int getY();
int getWidth();
int getHeight();
void move(int x, int y);
boolean isInsideBounds(int x, int y);
void select();
void unSelect();
boolean isSelected();
void paint(Graphics graphics);
}
//提供基本功能的抽象形状
public class BaseShape implements Shape{
public int x;
public int y;
public Color color;
private boolean selected = false;
BaseShape(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
@Override
public int getX() {
return x;
}
@Override
public int getY() {
return y;
}
@Override
public int getWidth() {
return 0;
}
@Override
public int getHeight() {
return 0;
}
@Override
public void move(int x, int y) {
this.x += x;
this.y += y;
}
@Override
public boolean isInsideBounds(int x, int y) {
return x > getX() && x < (getX() + getWidth()) &&
y > getY() && y < (getY() + getHeight());
}
@Override
public void select() {
selected = true;
}
@Override
public void unSelect() {
selected = false;
}
@Override
public boolean isSelected() {
return selected;
}
void enableSelectionStyle(Graphics graphics) {
graphics.setColor(Color.LIGHT_GRAY);
Graphics2D g2 = (Graphics2D) graphics;
float dash1[] = {2.0f};
g2.setStroke(new BasicStroke(1.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
2.0f, dash1, 0.0f));
}
void disableSelectionStyle(Graphics graphics) {
graphics.setColor(color);
Graphics2D g2 = (Graphics2D) graphics;
g2.setStroke(new BasicStroke());
}
@Override
public void paint(Graphics graphics) {
if (isSelected()) {
enableSelectionStyle(graphics);
}
else {
disableSelectionStyle(graphics);
}
// ...
}
}
//点
public class Dot extends BaseShape{
private final int DOT_SIZE = 3;
public Dot(int x, int y, Color color) {
super(x, y, color);
}
@Override
public int getWidth() {
return DOT_SIZE;
}
@Override
public int getHeight() {
return DOT_SIZE;
}
@Override
public void paint(Graphics graphics) {
super.paint(graphics);
graphics.fillRect(x - 1, y - 1, getWidth(), getHeight());
}
}
//圆形
public class Circle extends BaseShape{
public int radius;
public Circle(int x, int y, int radius, Color color) {
super(x, y, color);
this.radius = radius;
}
@Override
public int getWidth() {
return radius * 2;
}
@Override
public int getHeight() {
return radius * 2;
}
public void paint(Graphics graphics) {
super.paint(graphics);
graphics.drawOval(x, y, getWidth() - 1, getHeight() - 1);
}
}
//三角形
public class Rectangle extends BaseShape{
public int width;
public int height;
public Rectangle(int x, int y, int width, int height, Color color) {
super(x, y, color);
this.width = width;
this.height = height;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
@Override
public void paint(Graphics graphics) {
super.paint(graphics);
graphics.drawRect(x, y, getWidth() - 1, getHeight() - 1);
}
}
//由其他形状对象组成的复合形状
public class CompoundShape extends BaseShape {
protected List<Shape> children = new ArrayList<>();
public CompoundShape(Shape... components) {
super(0, 0, Color.BLACK);
add(components);
}
public void add(Shape component) {
children.add(component);
}
public void add(Shape... components) {
children.addAll(Arrays.asList(components));
}
public void remove(Shape child) {
children.remove(child);
}
public void remove(Shape... components) {
children.removeAll(Arrays.asList(components));
}
public void clear() {
children.clear();
}
@Override
public int getX() {
if (children.size() == 0) {
return 0;
}
int x = children.get(0).getX();
for (Shape child : children) {
if (child.getX() < x) {
x = child.getX();
}
}
return x;
}
@Override
public int getY() {
if (children.size() == 0) {
return 0;
}
int y = children.get(0).getY();
for (Shape child : children) {
if (child.getY() < y) {
y = child.getY();
}
}
return y;
}
@Override
public int getWidth() {
int maxWidth = 0;
int x = getX();
for (Shape child : children) {
int childsRelativeX = child.getX() - x;
int childWidth = childsRelativeX + child.getWidth();
if (childWidth > maxWidth) {
maxWidth = childWidth;
}
}
return maxWidth;
}
@Override
public int getHeight() {
int maxHeight = 0;
int y = getY();
for (Shape child : children) {
int childsRelativeY = child.getY() - y;
int childHeight = childsRelativeY + child.getHeight();
if (childHeight > maxHeight) {
maxHeight = childHeight;
}
}
return maxHeight;
}
@Override
public void move(int x, int y) {
for (Shape child : children) {
child.move(x, y);
}
}
@Override
public boolean isInsideBounds(int x, int y) {
for (Shape child : children) {
if (child.isInsideBounds(x, y)) {
return true;
}
}
return false;
}
@Override
public void unSelect() {
super.unSelect();
for (Shape child : children) {
child.unSelect();
}
}
public boolean selectChildAt(int x, int y) {
for (Shape child : children) {
if (child.isInsideBounds(x, y)) {
child.select();
return true;
}
}
return false;
}
@Override
public void paint(Graphics graphics) {
if (isSelected()) {
enableSelectionStyle(graphics);
graphics.drawRect(getX() - 1, getY() - 1, getWidth() + 1, getHeight() + 1);
disableSelectionStyle(graphics);
}
for (Shape child : children) {
child.paint(graphics);
}
}
}
//形状编辑器
public class ImageEditor {
private EditorCanvas canvas;
private CompoundShape allShapes = new CompoundShape();
public ImageEditor() {
canvas = new EditorCanvas();
}
public void loadShapes(Shape... shapes) {
allShapes.clear();
allShapes.add(shapes);
canvas.refresh();
}
private class EditorCanvas extends Canvas {
JFrame frame;
private static final int PADDING = 10;
EditorCanvas() {
createFrame();
refresh();
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
allShapes.unSelect();
allShapes.selectChildAt(e.getX(), e.getY());
e.getComponent().repaint();
}
});
}
void createFrame() {
frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
JPanel contentPanel = new JPanel();
Border padding = BorderFactory.createEmptyBorder(PADDING, PADDING, PADDING, PADDING);
contentPanel.setBorder(padding);
frame.setContentPane(contentPanel);
frame.add(this);
frame.setVisible(true);
frame.getContentPane().setBackground(Color.LIGHT_GRAY);
}
public int getWidth() {
return allShapes.getX() + allShapes.getWidth() + PADDING;
}
public int getHeight() {
return allShapes.getY() + allShapes.getHeight() + PADDING;
}
void refresh() {
this.setSize(getWidth(), getHeight());
frame.pack();
}
public void paint(Graphics graphics) {
allShapes.paint(graphics);
}
}
}
public class Client {
public static void main(String[] args) {
ImageEditor editor = new ImageEditor();
editor.loadShapes(
new Circle(10, 10, 10, Color.BLUE),
new CompoundShape(
new Circle(110, 110, 50, Color.RED),
new Dot(160, 160, Color.RED)
),
new CompoundShape(
new Rectangle(250, 250, 100, 100, Color.GREEN),
new Dot(240, 240, Color.GREEN),
new Dot(240, 360, Color.GREEN),
new Dot(360, 360, Color.GREEN),
new Dot(360, 240, Color.GREEN)
)
);
}
}