• 设计模式(15)组合模式


    一、介绍:

    1、定义:组合多个对象形成树形结构以表示“整体-部分”的关系的层次结构。组合模式对叶子节点和容器节点的处理具有一致性,又称为整体-部分模式。

    2、优缺点:

    优点:

    (1)高层模块调用简单:组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码。

    (2)节点自由增加:更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码。

    缺点:

    (1)在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则

    (2)设计较复杂,客户端需要花更多时间理清类之间的层次关系。

    (3)不容易限制容器中的构件。

    3、组成:

    (1)抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)。

    (2)树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。

    (3)树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。

    1. // 定义抽象构件
    2. public abstract class Component {
    3. protected String name;
    4. public Component(String name) {
    5. this.name = name;
    6. }
    7. public abstract void add(Component component);
    8. public abstract void remove(Component component);
    9. public abstract void display();
    10. }
    11. // 定义叶子构件
    12. public class Leaf extends Component {
    13. public Leaf(String name) {
    14. super(name);
    15. }
    16. @Override
    17. public void add(Component component) {
    18. System.out.println("Cannot add to a leaf");
    19. }
    20. @Override
    21. public void remove(Component component) {
    22. System.out.println("Cannot remove from a leaf");
    23. }
    24. @Override
    25. public void display() {
    26. System.out.println("Leaf: " + name);
    27. }
    28. }
    29. // 定义容器构件
    30. public class Composite extends Component {
    31. private List children = new ArrayList<>();
    32. public Composite(String name) {
    33. super(name);
    34. }
    35. @Override
    36. public void add(Component component) {
    37. children.add(component);
    38. }
    39. @Override
    40. public void remove(Component component) {
    41. children.remove(component);
    42. }
    43. @Override
    44. public void display() {
    45. System.out.println("Composite: " + name);
    46. for (Component component : children) {
    47. component.display();
    48. }
    49. }
    50. }
    51. // 客户端代码
    52. public class Client {
    53. public static void main(String[] args) {
    54. Component root = new Composite("root");
    55. Component leaf1 = new Leaf("leaf1");
    56. Component leaf2 = new Leaf("leaf2");
    57. Component composite1 = new Composite("composite1");
    58. Component leaf3 = new Leaf("leaf3");
    59. Component composite2 = new Composite("composite2");
    60. root.add(leaf1);
    61. root.add(leaf2);
    62. root.add(composite1);
    63. composite1.add(leaf3);
    64. composite1.add(composite2);
    65. root.display();
    66. }
    67. }

    4、应用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。

    二、demo:

    1、菜单:

    (1)数据库model

    1. public class MenuDTO {
    2. private String menuName;
    3. private String menuCode;
    4. private String parentMenuCode;
    5. public MenuDTO(String menuName,String menuCode,String parentMenuCode){
    6. this.menuCode = menuCode;
    7. this.menuName = menuName;
    8. this.parentMenuCode = parentMenuCode;
    9. }
    10. /**省略所有setget芳芳*/
    11. }

     抽象构件Component

    1. public abstract class MenuComponent extends MenuDTO {
    2. MenuComponent(String menuName, String menuCode,String parentMenuCode) {
    3. super(menuName, menuCode,parentMenuCode);
    4. }
    5. void addMenu(MenuComponent component){}
    6. void removeMenu(MenuComponent component){}
    7. }

    (2)树枝构件(Composite): 

    1. public class MenuVO extends MenuComponent {
    2. private List<MenuComponent> children = new ArrayList<>();
    3. MenuVO(String menuName, String menuCode,String parentMenuCode) {
    4. super(menuName, menuCode,parentMenuCode);
    5. }
    6. @Override
    7. void addMenu(MenuComponent component) {
    8. children.add(component);
    9. }
    10. @Override
    11. void removeMenu(MenuComponent component) {
    12. }
    13. }

    (3)树叶

    1. public class MenuLeaf extends MenuComponent {
    2. MenuLeaf(String menuName, String menuCode,String parentMenuCode) {
    3. super(menuName, menuCode,parentMenuCode);
    4. }
    5. @Override
    6. void addMenu(MenuComponent component) {
    7. super.addMenu(component);
    8. }
    9. @Override
    10. void removeMenu(MenuComponent component) {
    11. super.removeMenu(component);
    12. }
    13. }

     客户端:

    1. public class Test {
    2. public static void main(String args[]) {
    3. MenuComponent menuVOS = listMenus();
    4. System.out.println(menuVOS);
    5. }
    6. public static MenuComponent listMenus(){
    7. //模拟数据库查询,查询所有一级菜单(menu_type = 1)、二级菜单(menu_type = 2)
    8. List<MenuDTO> firstMenus = new ArrayList<>();
    9. MenuDTO menuDTO = new MenuDTO("菜单1","cd1","root");
    10. firstMenus.add(menuDTO);
    11. menuDTO = new MenuDTO("菜单2","cd2","root");
    12. firstMenus.add(menuDTO);
    13. menuDTO = new MenuDTO("菜单3","cd3","root");
    14. firstMenus.add(menuDTO);
    15. List<MenuDTO> secondMenus = new ArrayList<>();
    16. menuDTO = new MenuDTO("菜单1-1","cd1-1","cd1");
    17. secondMenus.add(menuDTO);
    18. menuDTO = new MenuDTO("菜单1-2","cd1-2","cd1");
    19. secondMenus.add(menuDTO);
    20. menuDTO = new MenuDTO("菜单2-1","cd2-1","cd2");
    21. secondMenus.add(menuDTO);
    22. Map<String, List<MenuDTO>> childMenuMap = secondMenus.stream().collect(Collectors.groupingBy(MenuDTO::getParentMenuCode));
    23. /**实现
    24. * 根节点
    25. * 菜单1 菜单2 菜单3
    26. *菜单1-1 菜单1-2 菜单2-1
    27. * */
    28. //1、定义根节点
    29. MenuComponent root = new MenuVO("根节点","root",null);
    30. //2、处理菜单层级
    31. for(MenuDTO firstMenu : firstMenus){
    32. //二级菜单
    33. MenuComponent firstMenuVO = new MenuVO(firstMenu.getMenuName(),firstMenu.getMenuCode(),firstMenu.getParentMenuCode());
    34. //三级菜单
    35. List<MenuDTO> secondMenuVOs = childMenuMap.get(firstMenu.getMenuCode());
    36. if(!CollectionUtils.isEmpty(secondMenuVOs)){
    37. for(MenuDTO secondMenu : secondMenuVOs){
    38. MenuComponent secondMenuVO = new MenuVO(secondMenu.getMenuName(),secondMenu.getMenuCode(),secondMenu.getParentMenuCode());
    39. firstMenuVO.addMenu(secondMenuVO);
    40. }
    41. }
    42. root.addMenu(firstMenuVO);
    43. }
    44. return root;
    45. }
    46. }

    运行main方法 

    2、文件夹:

    (1)抽象构件Component

    1. public abstract class FileComponent {
    2. //文件名称
    3. protected String name;
    4. //文件的层级 1 一级目录 2 二级目录 ...
    5. protected Integer level;
    6. //文件的类型 1 文件夹 2文件
    7. protected Integer type;
    8. //添加子文件/文件夹
    9. public abstract void add(FileComponent fileComponent);
    10. //移除子文件/文件夹
    11. public abstract void remove(FileComponent fileComponent);
    12. //获取指定的子文件/文件夹
    13. public abstract FileComponent getChild(int index);
    14. //打印子 子文件/子文件夹 名称的方法
    15. public abstract void print();
    16. }

    (2)树枝构件(Composite)

    1. public class FileFolder extends FileComponent{
    2. //文件夹可以有多个子文件夹或者子文件
    3. private List<FileComponent> fileComponentList;
    4. public FileFolder(String name, Integer level, Integer type) {
    5. this.name = name;
    6. this.level = level;
    7. this.type = type;
    8. this.fileComponentList = new ArrayList<>();
    9. }
    10. @Override
    11. public void add(FileComponent fileComponent) {
    12. fileComponentList.add(fileComponent);
    13. }
    14. @Override
    15. public void remove(FileComponent fileComponent) {
    16. fileComponentList.remove(fileComponent);
    17. }
    18. @Override
    19. public FileComponent getChild(int index) {
    20. return fileComponentList.get(index);
    21. }
    22. @Override
    23. public void print() {
    24. //打印菜单名称
    25. for (int i = 0; i < level; i++) {
    26. System.out.print("\t");
    27. }
    28. System.out.println(name);
    29. //打印子菜单或者子菜单项名称
    30. for (FileComponent component : fileComponentList) {
    31. component.print();
    32. }
    33. }
    34. }

    (3)树叶构件(Leaf)

    1. public class FileItem extends FileComponent{
    2. public FileItem(String name, Integer level, Integer type) {
    3. this.name = name;
    4. this.level = level;
    5. this.type = type;
    6. }
    7. @Override
    8. public void add(FileComponent fileComponent) {
    9. }
    10. @Override
    11. public void remove(FileComponent fileComponent) {
    12. }
    13. @Override
    14. public FileComponent getChild(int index) {
    15. return null;
    16. }
    17. @Override
    18. public void print() {
    19. //打印文件的名称
    20. for (int i = 0; i < level; i++) {
    21. System.out.print("\t");
    22. }
    23. System.out.println(name);
    24. }
    25. }

    客户端:

    1. public class Test {
    2. public static void main(String[] args) {
    3. //定义根目录
    4. FileComponent rootComponent = new FileFolder("我是根目录",1,1);
    5. //定义二级文件夹
    6. FileComponent secondLevelComponent = new FileFolder("我是二级目录",2,1);
    7. //定义文件
    8. FileComponent file = new FileItem("我是文件",3,2);
    9. //向根目录添加二级目录
    10. rootComponent.add(secondLevelComponent);
    11. //向二级目录添加文件
    12. secondLevelComponent.add(file);
    13. //打印
    14. rootComponent.print();
    15. }
    16. }

  • 相关阅读:
    项目实战:设置静态资源放行
    蓝桥等考Python组别九级004
    移动互联网时代,如何撬开流量的密码?
    nginx 配置防盗链(了解)
    将cookie字符串转成editthiscookie插件的json格式
    @Autowired与@Resource区别
    HttpClient / Http客户端
    百度收录提交工具-免费主动提交百度快速收录软件
    源码分享-HTML文档解析---GoLang实现
    【STM32】入门(十一):初识uCOS-III
  • 原文地址:https://blog.csdn.net/w_t_y_y/article/details/134027193