浅谈设计模式 - 组合模式(十二)

简介: 浅谈设计模式 - 组合模式(十二)

前言


组合模式是一种非常重要的设计模式,使用场景几乎随处可见,各类菜单和目录等地方都能看到组合模式的影子,组合模式通常情况下是和树形结构相辅相成的,而树是软件设计里面非常重要的数据结构,这篇文章将介绍什么是组合模式。


什么是组合模式



允许你将对象组合到树形结构表现“整体部分”的结构,组合能让客户以一致的方式处理个别对象和对象组合,组合其实更像是对于对于各种独立组建的“统一性”,可以将一类相似的事物看为一个整体但是拥有完全不同的工作机制。


介绍


可以说将相似的物品形成一个集合的模式就是组合模式,他能看两个相似的物品在一处进行完美的融合以及操作。当我们需要 整体/部分的操作时候,就可以使用这种形式。


特点


  • 组合模式讲究的是整体和部分之间的关系,整体可以包含部分,部分可以回溯到整体,互相包含
  • 组合模式可以让对象结构以“树”的形式包含关系。多数情况可以忽略整体和个体之前的差别


优缺点



优点:


  • 组合模式可以帮助对象和组合的对象一视同仁的对待


缺点:


  • 继承结构,修改抽象类违反开放关闭原则
  • 如果层次结构非常深,递归结构影响效率
  • 使用迭代器有可能造成并发遍历菜单的问题

组合模式以单一职责的原则换取透明性?

组合模式破坏了的单一职责原则,组合了多个对象的方法,同时在方法里面做了多种操作,但是这样做却是可以让整个对象可以更加直观的了解整体和部分的特性,这是设计模式里面非常常见的操作。


组合模式的结构图


组合模式的结构图如下:


网络异常,图片无法展示
|


  • Component 组件:定义组件的接口,这里可以设计为抽象类,可以设计为接口,可以视为组件的“可能的公共行为”。
  • Leaf 叶子节点:用于表示原始对象,叶子节点只需要实现自己的特殊功能即可,比如菜单的菜单子项。
  • Composite 组件节点:定义组件行为,可以具备子节点。同时实现叶子节点的相关操作(继承同一个接口),可以视为一个分类的大类


实际应用场景


由于现实场景当中这样的设计模式结构是有树状结构转换而来的,所以组合模式的使用场景就是出现树形结构的地方。比如:文件目录显示,多及目录呈现等树形结构数据的操作。下面我们就使用一个菜单的结构来了解一下组合模式的“模板”代码。


实战



模拟场景


组合模式是为树形结构设计的一种设计模式,案例参照一个菜单的管理功能作为模拟,我们需要拿到不同的菜单分类,在菜单的分类里面,我们有需要拿到不同的菜单项,我们可以由任意的菜单项进入到不同的菜单分类,同时可以进入不同的叶子节点。

这次的代码案例是从网上找的例子:


抽象组件


抽象组件定义了组件的通知接口,并实现了增删子组件及获取所有子组件的方法。同时重写了hashCodeequales方法(至于原因,请读者自行思考。如有疑问,请在评论区留言)。


package com.jasongj.organization;
import java.util.ArrayList;
import java.util.List;
public abstract class Organization {
  private List<Organization> childOrgs = new ArrayList<Organization>();
  private String name;
  public Organization(String name) {
    this.name = name;
  }
  public String getName() {
    return name;
  }
  public void addOrg(Organization org) {
    childOrgs.add(org);
  }
  public void removeOrg(Organization org) {
    childOrgs.remove(org);
  }
  public List<Organization> getAllOrgs() {
    return childOrgs;
  }
  public abstract void inform(String info);
  @Override
  public int hashCode(){
    return this.name.hashCode();
  }
  @Override
  public boolean equals(Object org){
    if(!(org instanceof Organization)) {
      return false;
    }
    return this.name.equals(((Organization) org).name);
  }
}


简单组件(部门)


简单组件在通知方法中只负责对接收到消息作出响应。


package com.jasongj.organization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Department extends Organization{
  public Department(String name) {
    super(name);
  }
  private static Logger LOGGER = LoggerFactory.getLogger(Department.class);
  public void inform(String info){
    LOGGER.info("{}-{}", info, getName());
  }
}


复合组件(公司)


复合组件在自身对消息作出响应后,还须通知其下所有子组件


package com.jasongj.organization;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Company extends Organization{
  private static Logger LOGGER = LoggerFactory.getLogger(Company.class);
  public Company(String name) {
    super(name);
  }
  public void inform(String info){
    LOGGER.info("{}-{}", info, getName());
    List<Organization> allOrgs = getAllOrgs();
    allOrgs.forEach(org -> org.inform(info+"-"));
  }
}


awt的组合模式


组合模式因为使用了同样的接口,会让叶子节点实现一些不必要的功能,此时一般可以使用一个空对象或者使用更为激进的使用抛出异常的形式。

awt这种老掉牙的东西就不多介绍,java的gui其实就是使用了组合模式,下面是一部分的案例代码:


//创建组件
    public MethodsTank() {
        //创建组件等
        jm = new JMenu("我的菜单(G)");
        jmb = new JMenuBar();
        jl1 = new JMenuItem("开始新游戏(F)");
        jl2 = new JMenuItem("结束游戏");
        jl3 = new JMenuItem("重新开始(R)");
        jl4 = new JMenuItem("存盘退出");
        jl5 = new JMenuItem("回到上次游戏");
        draw = new DrawTank();
        ses = new selectIsSallup();
        //设置快捷键方式
        jm.setMnemonic('G');
        jl1.setMnemonic('f');
        jl3.setMnemonic('r');
        jl4.setMnemonic('q');
        jl5.setMnemonic('w');
        //开启闪烁线程
        new Thread(ses).start();
        //先运行开始画面
        this.addTank();
    }
    public void addTank() {
        //添加菜单栏目
        jm.add(jl1);
        jm.add(jl2);
        jm.add(jl3);
        jm.add(jl4);
        jm.add(jl5);
        jmb.add(jm);
        //运行选关界面
        this.add(ses);
        //对于子菜单添加事件
        jl1.addActionListener(this);
        jl1.setActionCommand("newgame");
        jl2.addActionListener(this);
        jl2.setActionCommand("gameexit");
        jl3.addActionListener(this);
        jl3.setActionCommand("restart");
        //设置窗体的一些基本属性
        this.setTitle("我的坦克大战");
        this.setBounds(600, 350, width, height);
        //添加菜单栏的方式
        this.setJMenuBar(jmb);
        this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
        this.setVisible(true);
    }


总结


组合模式精髓在于“破而后立”,他虽然违反了设计原则,但是通过更加优雅的形式,实现了将单一的对象由部分变为一个整体。

而组合模式也经常和适配器模式搭配使用,本文的案例只是一个简单的套板,对于组合模式的实际运用场景其实更常见的情况是关于菜单和菜单子项的内容。


结语


组合模式很多情况下可能并不是十分用的上,更多的时候是和其他的设计模式搭配,组合模式我们需要关注的是“整体-部分”的融合统一即可。

相关文章
|
7月前
|
设计模式 Java 容器
【设计模式系列笔记】组合模式
组合模式(Composite Pattern)是一种结构型设计模式,它允许将对象组合成树状结构以表示部分-整体的层次结构。组合模式使得客户端可以统一处理单个对象和对象组合,而无需区分它们的类型。
90 12
|
7月前
|
设计模式 Java
浅谈设计模式 - 组合模式(十二)
浅谈设计模式 - 组合模式(十二)
91 0
|
设计模式 SQL 安全
深入浅出设计模式 - 组合模式
深入浅出设计模式 - 组合模式
115 0
深入浅出设计模式 - 组合模式
|
设计模式 Java
浅谈设计模式 - 迭代器模式(十一)
迭代器模式通常只需要知道该模式的实现原理和了解结构图即可,在设计模式当中自己实现的情况几乎是没有的,所以这个模式简单的过一遍。
92 0
|
设计模式
23种设计模式-结构模式-组合模式(十一)
23种设计模式-结构模式-组合模式(十一)
23种设计模式-结构模式-组合模式(十一)
|
设计模式
设计模式轻松学【十四】外观模式
生活中我们去餐馆吃饭,我们需要被接待,点菜,后台厨师准备菜品,做菜,服务员传送到桌上面,开始吃饭,吃完结账等等操作。但这一连串的操作中有些操作是不需要被我们关注的,比如厨师怎么做菜,怎么上菜,我们只需要点菜,吃饭,结账,所以我们可以引入外观模式来隐藏这些无需关注的细节。
135 0
设计模式轻松学【十四】外观模式
|
设计模式 Java uml
设计模式轻松学【十一】装饰模式
如果要扩展一些功能,我们可以采用装饰模式来实现。装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰者模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。
139 0
设计模式轻松学【十一】装饰模式
|
设计模式 算法
设计模式(十一)之模板方法模式
模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
138 0
设计模式(十一)之模板方法模式
|
设计模式 算法 uml
设计模式(十四)之建造者模式
建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
140 0
设计模式(十四)之建造者模式
|
设计模式 Linux
【设计模式】通过简单案例学习组合模式
【设计模式】通过简单案例学习组合模式
220 0
【设计模式】通过简单案例学习组合模式