也可以将叶子构件的add()、remove()等方法的实现代码移至Component中,由Component提供统一的默认实现,这样子类就不必强制去实现管理子Component。代码如下所示:
public abstract class Component { public void add(Component c) { throw new RuntimeException("不支持的操作"); } public void remove(Component c) { throw new RuntimeException("不支持的操作"); } public Component getChild(int i) { throw new RuntimeException("不支持的操作"); } public abstract void operation(); //业务方法 }透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的。叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供add()、remove()以及getChild()等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)。
安全组合模式安全组合模式中,在抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明并实现这些方法。
安全组合模式的完整结构图如下:
此时Component就应该这样定义了
public abstract class Component { // 业务方法 public abstract void operation(); }安全组合模式的缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。在实际应用中,安全组合模式的使用频率也非常高,在Java AWT中使用的组合模式就是安全组合模式。
模式应用 模式在JDK中的应用Java SE中的AWT和Swing包的设计就基于组合模式,在这些界面包中为用户提供了大量的容器构件(如Container)和成员构件(如Checkbox、Button和TextComponent等),其结构如下图所示
Component类是抽象构件,Checkbox、Button和TextComponent是叶子构件,而Container是容器构件,在AWT中包含的叶子构件还有很多。在一个容器构件中可以包含叶子构件,也可以继续包含容器构件,这些叶子构件和容器构件一起组成了复杂的GUI界面。除此以外,在XML解析、组织结构树处理、文件系统设计等领域,组合模式都得到了广泛应用。
模式在开源项目中的应用Spring中org.springframework.web.method.support.HandlerMethodArgumentResolver使用了安全组合模式。提取关键代码如下:
public interface HandlerMethodArgumentResolver { boolean supportsParameter(MethodParameter parameter); Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; }再看下它的一个实现类org.springframework.web.method.support.HandlerMethodArgumentResolverComposite
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>(); /** * Add the given {@link HandlerMethodArgumentResolver}. */ public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver resolver) { this.argumentResolvers.add(resolver); return this; } /** * Add the given {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}. */ public HandlerMethodArgumentResolverComposite addResolvers( @Nullable HandlerMethodArgumentResolver... resolvers) { if (resolvers != null) { Collections.addAll(this.argumentResolvers, resolvers); } return this; } /** * Clear the list of configured resolvers. */ public void clear() { this.argumentResolvers.clear(); } @Override public boolean supportsParameter(MethodParameter parameter) { return getArgumentResolver(parameter) != null; } @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first."); } return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } } 模式总结 主要优点
组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。