Java设计模式-组合模式
Posted xstxjs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java设计模式-组合模式相关的知识,希望对你有一定的参考价值。
简介
在软件设计中,设计模式是一种被广泛接受和应用的经验总结,旨在解决常见问题并提供可复用的解决方案。
组合模式是一种结构型设计模式,它允许将对象组合成树形结构以表示“部分-整体”的层次结构。这种模式能够使客户端以一致的方式处理单个对象和对象集合,将对象的组合与对象的使用具有一致性。
与其他设计模式的区别:
- 适配器模式 vs 组合模式:
- 适配器模式主要用于将一个类的接口转换为客户端所期望的另一种接口,以解决接口不兼容的问题。
- 组合模式则关注对象的组合形成树状结构,使客户端能够以一致的方式处理单个对象和对象集合。
- 装饰器模式 vs 组合模式:
- 装饰器模式通过在不改变接口的情况下为对象动态添加功能,以提供更灵活的行为扩展。
- 组合模式通过将对象组合成树形结构,表达对象之间的整体与部分关系,以统一对待单个对象和组合对象。
实现
下面是使用Java编程语言实现外观模式的示例代码:
// 抽象构件
interface Component
void operation();
// 叶子构件
class Leaf implements Component
public void operation()
// 执行叶子构件的操作
// 容器构件
class Composite implements Component
private List<Component> components = new ArrayList<>();
public void add(Component component)
components.add(component);
public void remove(Component component)
components.remove(component);
public void operation()
// 执行容器构件的操作
for (Component component : components)
component.operation();
// 客户端代码
public class Client
public static void main(String[] args)
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
Component composite1 = new Composite();
composite1.add(leaf1);
composite1.add(leaf2);
Component leaf3 = new Leaf();
Component composite2 = new Composite();
composite2.add(leaf3);
composite2.add(composite1);
composite2.operation();
优缺点
优点
- 简化客户端代码:客户端无需区分处理单个对象还是对象集合,可以统一以相同的方式操作。
- 灵活性和扩展性:可以根据需求动态地增加、删除构件,而不影响现有代码结构。
- 符合开闭原则:添加新的构件类时,不需要修改现有代码,只需添加新的类即可。
缺点
- 设计复杂性:引入组合模式会增加类和对象的数量,增加了系统的复杂性。
- 限制构件类型:组合模式限制了构件的类型,只能通过抽象构件来统一处理。
运用场景
组合模式适用于以下场景:
- 当需求中存在部分与整体的层次关系,并且希望一致地对待单个对象和对象集合时。
- 当需要对对象以及对象的组合进行操作,并且希望客户端代码尽可能简化时。
- 当需要动态地增加、删除对象,并且希望系统具有良好的扩展性时。
总结
组合模式是一种在Java设计模式中常见且有用的模式。通过将对象组合成树形结构,它提供了一种处理对象集合的一致性方法,使客户端能够以相同的方式处理单个对象和对象集合。通过使用组合模式,我们可以简化客户端代码,提供灵活的扩展性,并遵循开闭原则。这种模式在具有部分与整体层次结构、需要一致处理对象和对象集合、以及需要动态增加、删除对象的场景中非常适用。
组合模式的实现可以使用Java编程语言。在代码中,我们定义了抽象构件接口,包括叶子构件和容器构件两种类型。叶子构件表示树形结构中的叶节点,而容器构件表示树形结构中的分支节点,可以包含其他构件。客户端代码可以通过调用容器构件的操作方法来递归地处理整个树形结构,无需关心单个对象和对象集合的差异。
组合模式有一些优点和缺点。优点包括简化客户端代码、提供灵活性和扩展性,以及符合开闭原则。然而,它也会增加系统的复杂性,并限制构件的类型。在实际应用中,我们应该根据具体需求和系统设计的目标来选择是否使用组合模式。
总之,组合模式在Java设计模式中具有重要的地位和应用价值。它通过统一处理单个对象和对象集合,简化了客户端代码,并提供了灵活的扩展性。了解和合理运用组合模式将有助于我们设计出更清晰、可扩展和易维护的软件系统。
Java 设计模式系列组合模式
Java 设计模式系列(九)组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象的使用具有一致性。
一、组合模式结构
-
Component
: 抽象的组件对象,为组合中的对象声明接口,让客户端可以通过这个接口来访问和管理整个对象结构,可以在里面为定义的功能提供缺省的实现。 -
Leaf
: 叶子节点对象,定义和实现叶子对象的行为,不再包含其它的子节点对象。 -
Composite
: 组合对象,通常会存储子组件,定义包含子组件的那些组件的行为,并实现在组件接口中定义的与子组件有关的操作。 -
Client
: 客户端,通过组件接口来操作组合结构里面的组件对象。
组合模式参考实现
(1)先看看组件对象的定义,示例代码如下:
/**
* 抽象的组件对象,为组合中的对象声明接口,实现接口的缺省行为
*/
public abstract class Component {
/** 示意方法,子组件对象可能有的功能方法 */
public abstract void someOperation();
/** 向组合对象中加入组件对象 */
public void addChild(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
}
/** 从组合对象中移出某个组件对象 */
public void removeChild(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
}
/** 返回某个索引对应的组件对象 */
public Component getChildren(int index) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
}
}
(2)接下来看看Composite对象的定义,示例代码如下:
/**
* 组合对象,通常需要存储子对象,定义有子部件的部件行为,
* 并实现在Component里面定义的与子部件有关的操作
*/
public class Composite extends Component {
/**
* 用来存储组合对象中包含的子组件对象
*/
private List<Component> childComponents = null;
/** 示意方法,通常在里面需要实现递归的调用 */
public void someOperation() {
if (childComponents != null){
for(Component c : childComponents){
//递归的进行子组件相应方法的调用
c.someOperation();
}
}
}
public void addChild(Component child) {
//延迟初始化
if (childComponents == null) {
childComponents = new ArrayList<Component>();
}
childComponents.add(child);
}
public void removeChild(Component child) {
if (childComponents != null) {
childComponents.remove(child);
}
}
public Component getChildren(int index) {
if (childComponents != null){
if(index>=0 && index<childComponents.size()){
return childComponents.get(index);
}
}
return null;
}
}
(3)该来看叶子对象的定义了,相对而言比较简单,示例代码如下:
/**
* 叶子对象,叶子对象不再包含其它子对象
*/
public class Leaf extends Component {
/** 示意方法,叶子对象可能有自己的功能方法 */
public void someOperation() {
// do something
}
}
(4)对于Client,就是使用Component接口来操作组合对象结构,由于使用方式千差万别,这里仅仅提供一个示范性质的使用,顺便当作测试代码使用,示例代码如下:
public class Client {
public static void main(String[] args) {
//定义多个Composite对象
Component root = new Composite();
Component c1 = new Composite();
Component c2 = new Composite();
//定义多个叶子对象
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
Component leaf3 = new Leaf();
//组和成为树形的对象结构
root.addChild(c1);
root.addChild(c2);
root.addChild(leaf1);
c1.addChild(leaf2);
c2.addChild(leaf3);
//操作Component对象
Component o = root.getChildren(1);
System.out.println(o);
}
}
二、父组件引用
(1) Component
public abstract class Component {
/**
* 记录父组件对象
*/
private Component parent = null;
/**
* 获取一个组件的父组件对象
* @return 一个组件的父组件对象
*/
public Component getParent() {
return parent;
}
/**
* 设置一个组件的父组件对象
* @param parent 一个组件的父组件对象
*/
public void setParent(Component parent) {
this.parent = parent;
}
/**
* 返回某个组件的子组件对象
* @return 某个组件的子组件对象
*/
public List<Component> getChildren() {
throw new UnsupportedOperationException("对象不支持这个功能");
}
/*-------------------以下是原有的定义----------------------*/
/**
* 输出组件自身的名称
*/
public abstract void printStruct(String preStr);
/**
* 向组合对象中加入组件对象
* @param child 被加入组合对象中的组件对象
*/
public void addChild(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
}
/**
* 从组合对象中移出某个组件对象
* @param child 被移出的组件对象
*/
public void removeChild(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
}
/**
* 返回某个索引对应的组件对象
* @param index 需要获取的组件对象的索引,索引从0开始
* @return 索引对应的组件对象
*/
public Component getChildren(int index) {
throw new UnsupportedOperationException("对象不支持这个功能");
}
}
(2) Leaf
public class Leaf extends Component {
/**
* 叶子对象的名字
*/
private String name = "";
/**
* 构造方法,传入叶子对象的名字
* @param name 叶子对象的名字
*/
public Leaf(String name){
this.name = name;
}
/**
* 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
* @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
*/
public void printStruct(String preStr){
System.out.println(preStr+"-"+name);
}
}
(3) Composite
public class Composite extends Component{
public void addChild(Component child) {
//延迟初始化
if (childComponents == null) {
childComponents = new ArrayList<Component>();
}
childComponents.add(child);
//添加对父组件的引用
child.setParent(this);
}
public void removeChild(Component child) {
if (childComponents != null) {
//查找到要删除的组件在集合中的索引位置
int idx = childComponents.indexOf(child);
if (idx != -1) {
//先把被删除的商品类别对象的父商品类别,设置成为被删除的商品类别的子类别的父商品类别
for(Component c : child.getChildren()){
//删除的组件对象是本实例的一个子组件对象
c.setParent(this);
//把被删除的商品类别对象的子组件对象添加到当前实例中
childComponents.add(c);
}
//真的删除
childComponents.remove(idx);
}
}
}
public List<Component> getChildren() {
return childComponents;
}
/*-------------------以下是原有的实现,没有变化----------------------*/
/**
* 用来存储组合对象中包含的子组件对象
*/
private List<Component> childComponents = null;
/**
* 组合对象的名字
*/
private String name = "";
/**
* 构造方法,传入组合对象的名字
* @param name 组合对象的名字
*/
public Composite(String name){
this.name = name;
}
/**
* 输出组合对象自身的结构
* @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
*/
public void printStruct(String preStr){
//先把自己输出去
System.out.println(preStr+"+"+this.name);
//如果还包含有子组件,那么就输出这些子组件对象
if(this.childComponents!=null){
//然后添加一个空格,表示向后缩进一个空格
preStr+=" ";
//输出当前对象的子对象了
for(Component c : childComponents){
//递归输出每个子对象
c.printStruct(preStr);
}
}
}
}
三、总结
(1) 组合模式的本质
组合模式的本质: 统一叶子对象和组合对象。
组合模式通过把叶子对象当成特殊的组合对象看待,从而对叶子对象和组合对象一视同仁,统统当成了Component对象,有机的统一了叶子对象和组合对象。
正是因为统一了叶子对象和组合对象,在将对象构建成树形结构的时候,才不需要做区分,反正是组件对象里面包含其它的组件对象,如此递归下去;也才使得对于树形结构的操作变得简单,不管对象类型,统一操作。
(2) 何时选用组合模式
-
如果你想表示对象的部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,使得层次结构实现更简单,从外部来使用这个层次结构也简单。
-
如果你希望统一的使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能。
(3) 组合模式的优缺点
-
定义了包含基本对象和组合对象的类层次结构
在组合模式中,基本对象可以被组合成更复杂的组合对象,而组合对象又可以组合成更复杂的组合对象,可以不断地递归组合下去,从而构成一个统一的组合对象的类层次结构
-
统一了组合对象和叶子对象
在组合模式中,可以把叶子对象当作特殊的组合对象看待,为它们定义统一的父类,从而把组合对象和叶子对象的行为统一起来
-
简化了客户端调用
组合模式通过统一组合对象和叶子对象,使得客户端在使用它们的时候,就不需要再去区分它们,客户不关心使用的到底是什么类型的对象,这就大大简化了客户端的使用
-
更容易扩展
由于客户端是统一的面对Component来操作,因此,新定义的Composite或Leaf子类能够很容易的与已有的结构一起工作,而客户端不需要为增添了新的组件类而改变
-
很难限制组合中的组件类型
容易增加新的组件也会带来一些问题,比如很难限制组合中的组件类型。这在需要检测组件类型的时候,使得我们不能依靠编译期的类型约束来完成,必须在运行期间动态检测。
每天用心记录一点点。内容也许不重要,但习惯很重要!
以上是关于Java设计模式-组合模式的主要内容,如果未能解决你的问题,请参考以下文章