原文: https://howtodoinjava.com/design-patterns/structural/composite-design-pattern/

组合设计模式是修改对象结构的结构型模式。 此模式最适合需要使用对象的情况,这些对象形成树状结构。 在该树中,每个节点/对象(根节点除外)都是复合节点或叶节点。 实现组合模式可使客户统一对待单个对象和构图。

  1. Table of Contents
  2. When to use composite design pattern
  3. Participants in composite design pattern
  4. Sample problem to solve
  5. Solution using composite design pattern
  6. Final notes

何时使用组合设计模式

组合设计模式将对象组合成树状结构,以表示整个部分的层次结构。 复合可以使客户统一对待单个对象和对象组成。

  • 当应用具有分层结构并且需要跨结构的通用功能时。
  • 当应用需要跨层次结构聚合数据时。
  • 当应用要统一对待复合对象和单个对象时。

组合设计模式的真实示例用法可能是:

  1. 建立银行中客户帐户的综合视图(即客户的投资组合)
  2. 建立总账
  3. 计算机/网络监控应用
  4. 零售和库存申请
  5. 文件系统实现中的目录结构
  6. GUI 屏幕中的菜单项

组合设计模式的参与者

以下是任何基于组合模式的解决方案的参与者。

组合设计模式 - 图1

组合设计模式

参与此模式的类和对象:

  1. component

  • 声明组合中对象的接口。
  • 根据需要为所有类通用的接口实现默认行为。
  • 声明用于访问和管理其子组件的接口。
    1. Leaf

  • 表示组合中的叶对象。 叶子没有孩子。
  • 定义组合中原始对象的行为。
    1. composite

  • 定义具有子级的组件的行为。
  • 存储子组件。
  • Component接口中实现与子相关的操作。
    1. Client

  • 通过Component接口操作组合中的对象。

在上图中,客户端使用Component接口与复合层次结构中的对象进行交互。 在层次结构内部,如果对象是复合对象,则它将请求传递给叶节点。 如果对象是叶节点,则立即处理请求。

复合叶子还可以选择在叶子节点处理请求之前或之后修改请求/响应。

要解决的示例问题

假设我们正在构建财务应用。 我们的客户拥有多个银行帐户。 我们被要求准备一个设计,该设计可用于生成客户的合并帐户视图,该视图能够在合并所有帐户对帐单后显示客户的总帐户余额以及合并对帐单。 因此,应用应该能够生成:

1)客户来自所有帐户的总帐户余额
2)合并帐户对帐单

使用组合设计模式的解决方案

在这里,我们正在处理形成树状结构的帐户对象,在该结构中,我们将遍历帐户对象并仅对帐户对象执行一些操作,因此可以应用组合设计模式。

让我们来看看参与的类:

Component.java

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. public abstract class Component
  4. {
  5. AccountStatement accStatement;
  6. protected List<Component> list = new ArrayList<>();
  7. public abstract float getBalance();
  8. public abstract AccountStatement getStatement();
  9. public void add(Component g) {
  10. list.add(g);
  11. }
  12. public void remove(Component g) {
  13. list.remove(g);
  14. }
  15. public Component getChild(int i) {
  16. return (Component) list.get(i);
  17. }
  18. }

CompositeAccount.java

  1. public class CompositeAccount extends Component
  2. {
  3. private float totalBalance;
  4. private AccountStatement compositeStmt, individualStmt;
  5. public float getBalance() {
  6. totalBalance = 0;
  7. for (Component f : list) {
  8. totalBalance = totalBalance + f.getBalance();
  9. }
  10. return totalBalance;
  11. }
  12. public AccountStatement getStatement() {
  13. for (Component f : list) {
  14. individualStmt = f.getStatement();
  15. compositeStmt.merge(individualStmt);
  16. }
  17. return compositeStmt;
  18. }
  19. }

AccountStatement.java

  1. public class AccountStatement
  2. {
  3. public void merge(AccountStatement g)
  4. {
  5. //Use this function to merge all account statements
  6. }
  7. }

DepositAccount.java

  1. public class DepositAccount extends Component
  2. {
  3. private String accountNo;
  4. private float accountBalance;
  5. private AccountStatement currentStmt;
  6. public DepositAccount(String accountNo, float accountBalance) {
  7. super();
  8. this.accountNo = accountNo;
  9. this.accountBalance = accountBalance;
  10. }
  11. public String getAccountNo() {
  12. return accountNo;
  13. }
  14. public float getBalance() {
  15. return accountBalance;
  16. }
  17. public AccountStatement getStatement() {
  18. return currentStmt;
  19. }
  20. }

SavingsAccount.java

  1. public class SavingsAccount extends Component
  2. {
  3. private String accountNo;
  4. private float accountBalance;
  5. private AccountStatement currentStmt;
  6. public SavingsAccount(String accountNo, float accountBalance) {
  7. super();
  8. this.accountNo = accountNo;
  9. this.accountBalance = accountBalance;
  10. }
  11. public String getAccountNo() {
  12. return accountNo;
  13. }
  14. public float getBalance() {
  15. return accountBalance;
  16. }
  17. public AccountStatement getStatement() {
  18. return currentStmt;
  19. }
  20. }

Client.java

  1. public class Client
  2. {
  3. public static void main(String[] args)
  4. {
  5. // Creating a component tree
  6. Component component = new CompositeAccount();
  7. // Adding all accounts of a customer to component
  8. component.add(new DepositAccount("DA001", 100));
  9. component.add(new DepositAccount("DA002", 150));
  10. component.add(new SavingsAccount("SA001", 200));
  11. // getting composite balance for the customer
  12. float totalBalance = component.getBalance();
  13. System.out.println("Total Balance : " + totalBalance);
  14. AccountStatement mergedStatement = component.getStatement();
  15. //System.out.println("Merged Statement : " + mergedStatement);
  16. }
  17. }
  18. Output:
  19. Total Balance : 450.0

最后的笔记

  1. 组合模式定义了由各个对象和复合对象组成的类层次结构。
  2. 客户端通过组件接口统一对待原始对象和组合对象,这使客户端代码变得简单。
  3. 添加新组件很容易,并且由于客户端通过组件接口处理新组件,因此不需要更改客户端代码。
  4. 可以使用迭代器设计模式遍历复合层次结构。
  5. 访问者设计模式可以对组合应用操作。
  6. 享元设计模式通常与组合结合以实现共享的叶节点。

祝您学习愉快!