设计模式——组合模式
组合模式的定义
允许你将对象组合成树形结构来表现“整体/部分“层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
讲解时,我们用图形界面上的菜单例子来举例说明。
组合模式的类图:
类图解析:
Client:客户对象将使用组件接口访问菜单和菜单项
Component:提供了一组接口,让菜单项和菜单共同使用,并且提供了默认实现。但是我们希望由具体的菜单或菜单项提供默认实现,因此我们使用了抽象类。
leaf:对于叶子节点(即菜单项)来说,父类Component中有很多方法是它不需要的,因此它只需要覆盖实现那些对它有意义的方法即可。
Composite:菜单也是只需要覆盖实现那些对它有意义的方法即可。
利用这个模式来统一处理个别对象和组合对象,意味着如果我们有了一个树形结构的菜单、子菜单和可能还带有菜单项的子菜单,那么任何一个菜单都是一种“组合”。因为它既可以包含其他菜单,也可以包含菜单项。个别对象只是菜单项——并未持有其他对象。
Component组件对象:MenuComponent
对每个方法都提供了默认的实现。因为有些方法只对菜单有意义,而有些只对菜单项有意义,默认实现是抛出UnsupportedOperationException 异常。这样,如果菜单项或菜单不支持某个操作,我们就不需要做什么事情了,直接继承默认实现就可以了。
public abstract class MenuComponent { public void operation(){ throw new UnsupportedOperationException(); } public void add(MenuComponent menuComponent){ throw new UnsupportedOperationException(); } public void remove(MenuComponent menuComponent){ throw new UnsupportedOperationException(); } public MenuComponent getChild(int position){ throw new UnsupportedOperationException(); } public String getName(){ throw new UnsupportedOperationException(); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
实现leaf叶子节点:MenuItem
只实现对它有意义的方法
public class MenuItem extends MenuComponent{ private String name; public MenuItem(String name){ this.name = name; } @Override public String getName() { return name; } @Override public void operation() { System.out.println(" I am "+getName()); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
实现Composite组合菜单:Menu
只实现对它有意义的方法
import java.util.ArrayList;
import java.util.Iterator;
public class Menu extends MenuComponent { private ArrayList menuComponents = new ArrayList(); private String name; public Menu(String name){ this.name = name; } @Override public String getName() { return name; } @Override public void add(MenuComponent menuComponent) { menuComponents.add(menuComponent); } @Override public void remove(MenuComponent menuComponent) { menuComponents.remove(menuComponent); } @Override public MenuComponent getChild(int position) { return (MenuComponent)menuComponents.get(position); } @Override public void operation() { for (Object component:menuComponents) { MenuComponent component1 = ((MenuComponent)component); System.out.println(component1.getName()); component1.operation(); } }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
Client用户:
public class Client { private MenuComponent allMenus; public Client(MenuComponent allMenus){ this.allMenus = allMenus; } public void printMenu(){ allMenus.operation(); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
测试
public class Main { public static void main(String[] args) { MenuComponent homeMenu = new Menu("Home"); MenuComponent viewMenu = new Menu("View"); MenuComponent editMenu = new Menu("Edit"); MenuComponent menus = new Menu("Menus"); menus.add(homeMenu); menus.add(viewMenu); menus.add(editMenu); homeMenu.add(new MenuItem("open")); homeMenu.add(new MenuItem("close")); homeMenu.add(new MenuItem("open as")); homeMenu.add(new MenuItem("new project...")); viewMenu.add(new MenuItem("Size")); viewMenu.add(new MenuItem("Canva")); editMenu.add(new MenuItem("Copy")); editMenu.add(new MenuItem("Past")); editMenu.add(new MenuItem("Cut")); Client client = new Client(menus); client.printMenu(); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
测试结果:
Home
open I am open
close I am close
open as I am open as
new project... I am new project...
View
Size I am Size
Canva I am Canva
Edit
Copy I am Copy
Past I am Past
Cut I am Cut
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
组合模式以单一责任设计原则换取透明性。所谓透明性,就是通过让组件的接口同时包含一些管理子节点和叶子节点的操作,客户就可以将组合和叶节点一视同仁。即一个元素究竟是组合还是叶子节点,对客户是透明的。
为了保持透明性,组合内所有的对象都必须实现相同的接口,否则客户就必须操作哪个对象是用哪个接口,这就失去了组合模式的意义。很明显,这也意味着对象会拥有一些没有意义的方法调用。有时候你可让这样的方法不做事,返回null或者false,再激烈的做法就是直接抛出异常,像MenuComponent抽象类那样,直接抛出异常。
组合模式通常是用树形结构的,也就是一种层次结构。根就是顶层的组合,然后往下是它的孩子,最末端是叶节点。组件可以有一个指向父亲的指针,以便在游走时更容易。而且,如果引用某个孩子,你想从树形结果中删除这个孩子,你会需要父亲去删除它。一旦孩子有了指向父亲的引用,这做起来就很容易。
有时候,如果这个组合结构很复杂,或者遍历的代价太高,那么实现组合节点的缓存就很有帮助。比方说,如果你要不断遍历一个组合,而且它的每一个子节点都需要进行某些计算,那你就应该使用缓存来临时保存结果,省去遍历的开支。
最后,给大家送上组合模式的demo。
文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_40763897/article/details/88852027
- 点赞
- 收藏
- 关注作者
评论(0)