设计模式 - 组合模式

实例

界面控件库

假设一个界面控件库的设计场景,界面控件分为两大类,一类是单元控件,例如按钮、文本框等,一类是容器控件,例如窗体、中间面板等,用户可以根据需要自行搭配使用


初始解决方案

  • Button.java
/**
 * @Description 单元控件:按钮
 */
public class Button {

    private final String name;

    public Button(String name) {
        this.name = name;
    }

    public void display() {
        System.out.println(this.name);
    }
}
  • TextBox.java
/**
 * @Description 单元控件:文本框
 */
public class TextBox {

    private final String name;

    public TextBox(String name) {
        this.name = name;
    }

    public void display() {
        System.out.println(this.name);
    }
}
  • PopWindow.java
import java.util.ArrayList;
import java.util.List;

/**
 * @Description 容器控件:弹出窗口
 */
public class PopWindow {

    private final List<CenterPanel> centerPanelList = new ArrayList<>();

    private final List<Button> buttonList = new ArrayList<>();

    private final List<TextBox> textBoxList = new ArrayList<>();
    private final String name;

    public PopWindow(String name) {
        this.name = name;
    }

    public void addCenterPanel(CenterPanel centerPanel) {
        centerPanelList.add(centerPanel);
    }

    public void addButton(Button button) {
        buttonList.add(button);
    }

    public void addTextBox(TextBox textBox) {
        textBoxList.add(textBox);
    }

    public void display() {
        System.out.println(this.name);
        centerPanelList.forEach(CenterPanel::display);
        buttonList.forEach(Button::display);
        textBoxList.forEach(TextBox::display);
    }
}
  • CenterPanel.java
/**
 * @Description 容器控件:中间面板
 */
public class CenterPanel {

    private final List<PopWindow> popWindowList = new ArrayList<>();

    private final List<Button> buttonList = new ArrayList<>();

    private final List<TextBox> textBoxList = new ArrayList<>();
    private final String name;

    public CenterPanel(String name) {
        this.name = name;
    }

    public void addPopWindow(PopWindow popWindow) {
        popWindowList.add(popWindow);
    }

    public void addButton(Button button) {
        buttonList.add(button);
    }

    public void addTextBox(TextBox textBox) {
        textBoxList.add(textBox);
    }

    public void display() {
        System.out.println(this.name);
        popWindowList.forEach(PopWindow::display);
        buttonList.forEach(Button::display);
        textBoxList.forEach(TextBox::display);
    }
}
  • Test.java
public class Test {
    public static void main(String[] args) {
        CenterPanel centerPanel = new CenterPanel("中央面板");
        PopWindow popWindow = new PopWindow("弹出窗");

        TextBox textBox = new TextBox("文本框");
        Button button = new Button("按钮");

        popWindow.addTextBox(textBox);
        popWindow.addButton(button);

        centerPanel.addTextBox(textBox);
        centerPanel.addButton(button);
        centerPanel.addPopWindow(popWindow);
        centerPanel.display();
    }
}
  • 输出如下:
中央面板
弹出窗
按钮
文本框
按钮
文本框
  • 不难看出,该方案存在诸多问题:
  • (1) 容器控件的设计和实现复杂,定义了多个集合存储不同类型的成员,需要针对不同的成员提供增加、删除和获取等管理和访问成员的方法,存在大量的冗余代码,系统维护较为困难。
  • (2) 系统没有提供抽象层,客户端必须有区别地对待容器控件和单元控件,无法统一对它们进行处理。
  • (3) 系统的灵活性和可扩展性差,如果需要增加容器控件和单元控件都需要对原有代码进行修改。

组合模式

概念

  • 组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有"整体—部分"关系的层次结构
  • 组合模式使客户端对单个对象和组合对象保持一致的方式处理
  • 组合模式又称为"整体—部分"(Part-Whole)模式
  • 组合模式是一种对象结构型模式
  • 组合模式结构图

在这里插入图片描述


角色定义

角色名称释义
Component抽象构件可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等
Leaf叶子构件它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理
Composite容器构件它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法

透明组合模式

  • Component.java
/**
 * @Description 抽象组件
 */
public abstract class Component {

    public void add(Component component) {
        throw new UnsupportedOperationException("不支持该操作");
    }

    public void remove(Component component) {
        throw new UnsupportedOperationException("不支持该操作");
    }

    public Component getChild(int i) {
        throw new UnsupportedOperationException("不支持该操作");
    }

    public abstract void display();
}
  • Button.java
/**
 * @Description 按钮:叶子构件
 */
public class Button extends Component {

    private final String name;

    public Button(String name) {
        this.name = name;
    }

    @Override
    public void display() {
        System.out.println(this.name);
    }
}
  • TextBox.java
/**
 * @Description 文本框:叶子构件
 */
public class TextBox extends Component {

    private final String name;

    public TextBox(String name) {
        this.name = name;
    }

    @Override
    public void display() {
        System.out.println(this.name);
    }
}
  • CenterPanel.java
/**
 * @Description 中间面板:容器构件
 */
public class CenterPanel extends Component {

    private final List<Component> componentList = new ArrayList<>();
    private final String name;
    private final Integer level;

    public CenterPanel(String name, Integer level) {
        this.name = name;
        this.level = level;
    }

    @Override
    public void add(Component component) {
        componentList.add(component);
    }

    @Override
    public void remove(Component component) {
        componentList.remove(component);
    }

    @Override
    public Component getChild(int i) {
        return componentList.get(i);
    }

    @Override
    public void display() {
        System.out.println(this.name);
        componentList.forEach(component -> {
            for (int i = 0; i < this.level; i++) {
                System.out.print(" ");
            }
            component.display();
        });
    }
}
  • PopWindow.java
/**
 * @Description 弹出窗口:容器构件
 */
public class PopWindow extends Component {

    private final List<Component> componentList = new ArrayList<>();
    private final String name;
    private final Integer level;

    public PopWindow(String name, Integer level) {
        this.name = name;
        this.level = level;
    }

    @Override
    public void add(Component component) {
        componentList.add(component);
    }

    @Override
    public void remove(Component component) {
        componentList.remove(component);
    }

    @Override
    public Component getChild(int i) {
        return componentList.get(i);
    }

    @Override
    public void display() {
        System.out.println(this.name);
        componentList.forEach(component -> {
            for (int i = 0; i < this.level; i++) {
                System.out.print(" ");
            }
            component.display();
        });
    }
}
  • Test.java
/**
 * @Description 透明组合模式测试类
 */
public class Test {
    public static void main(String[] args) {
        Component centerPanel = new CenterPanel("中央面板", 1);
        Component popWindow = new PopWindow("弹出窗", 2);
        Component textBox = new TextBox("文本框");
        Component button = new Button("按钮");
        popWindow.add(textBox);
        popWindow.add(button);
        centerPanel.add(textBox);
        centerPanel.add(button);
        centerPanel.add(popWindow);
        centerPanel.display();
    }
}
  • 输出如下:
中央面板
 文本框
 按钮
 弹出窗
  文本框
  按钮
  • 类图如下:

在这里插入图片描述

  • 透明组合模式:抽象构件Component中声明了所有用于管理成员对象的方法,包括add()remove()以及getChild()等方法,这样做的好处是确保所有的构件类都有相同的接口
  • 透明组合模式的缺点:不够安全,叶子对象和容器对象是有区别的,叶子对象不可能包含成员对象,为其提供add()remove()以及getChild()等方法是没有意义的,在运行阶段调用这些方法可能会出错

安全组合模式

  • Component.java
/**
 * @Description 抽象组件
 */
public abstract class Component {

    public abstract void display();

}
  • Button.java
/**
 * @Description 按钮:叶子构件
 */
public class Button extends Component {

    private final String name;

    public Button(String name) {
        this.name = name;
    }

    @Override
    public void display() {
        System.out.println(this.name);
    }
}
  • TextBox.java
/**
 * @Description 文本框:叶子构件
 */
public class TextBox extends Component {

    private final String name;

    public TextBox(String name) {
        this.name = name;
    }

    @Override
    public void display() {
        System.out.println(this.name);
    }
}
  • CenterPanel.java
/**
 * @Description 中间面板:容器构件
 */
public class CenterPanel extends Component {

    private final List<Component> componentList = new ArrayList<>();
    private final String name;
    private final Integer level;

    public CenterPanel(String name, Integer level) {
        this.name = name;
        this.level = level;
    }

    public void add(Component component) {
        componentList.add(component);
    }

    public void remove(Component component) {
        componentList.remove(component);
    }

    public Component getChild(int i) {
        return componentList.get(i);
    }

    @Override
    public void display() {
        System.out.println(this.name);
        componentList.forEach(component -> {
            for (int i = 0; i < this.level; i++) {
                System.out.print(" ");
            }
            component.display();
        });
    }
}
  • PopWindow.java
/**
 * @Description 弹出窗口:容器构件
 */
public class PopWindow extends Component {

    private final List<Component> componentList = new ArrayList<>();
    private final String name;
    private final Integer level;

    public PopWindow(String name, Integer level) {
        this.name = name;
        this.level = level;
    }

    public void add(Component component) {
        componentList.add(component);
    }

    public void remove(Component component) {
        componentList.remove(component);
    }

    public Component getChild(int i) {
        return componentList.get(i);
    }

    @Override
    public void display() {
        System.out.println(this.name);
        componentList.forEach(component -> {
            for (int i = 0; i < this.level; i++) {
                System.out.print(" ");
            }
            component.display();
        });
    }
}
  • Test.java
/**
 * @Description 安全组合模式测试类
 */
public class Test {
    public static void main(String[] args) {
        CenterPanel centerPanel = new CenterPanel("中央面板", 1);
        PopWindow popWindow = new PopWindow("弹出窗", 2);
        Component textBox = new TextBox("文本框");
        Component button = new Button("按钮");
        popWindow.add(textBox);
        popWindow.add(button);
        centerPanel.add(textBox);
        centerPanel.add(button);
        centerPanel.add(popWindow);
        centerPanel.display();
    }
}
  • 输出如下:
中央面板
 文本框
 按钮
 弹出窗
  文本框
  按钮
  • 安全组合模式:不在抽象构件Component中声明任何用于管理成员对象的方法,而是在Composite类中声明并实现这些方法
  • 安全组合模式的缺点:不够透明,叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程

总结

  • 优点
1.清楚地定义分层次的复杂对象,表示对象的全部或部分层次
2.让客户端忽略了层次的差异,方便对整个层次结构进行控制
3.简化客户端代码,符合开闭原则
  • 缺点
1.限制类型时会较为复杂
2.使设计变得更加抽象
  • 适用场景
1.希望客户端可以忽略组合对象与单个对象的差异时
2.处理一个树形结构
  • 相关设计模式
1.组合模式和访问者模式
  • 组合模式源代码
Container(AWT)HashMapArrayList(jdk)SqlNode(mybatis)

源码


- End -
- 个人学习笔记 -
- 仅供参考 -

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Maggieq8324

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值