组合
通过创建一个由其他对象组合的对象来获得新功能的重用方法
新功能的获得是通过调用组合对象的功能实现的
有时又叫聚合
例如:
一个对象拥有或者对另外一个对象负责并且两个对象有相同的生命周期。(GOF)
一个对象包含另一个对象集合
被包含对象对其他对象是不可见的并且只能从包含它的对象中访问的特殊组合形式
组合的优缺点
优点
被包含对象通过包含他们的类来访问
黑盒重用,因为被包含对象的内部细节是不可见的
很好的封装
每个类专注于一个任务
通过获得和被包含对象的类型相同的对象引用,可以在运行时动态定义组合的方式
缺点
结果系统可能会包含更多的对象
为了使组合时可以使用不同的对象,必须小心的定义接口
继承
通过扩展已实现的对象来获得新功能的重用方法
基类有用通用的属性和方法
子类提供更多的属性和方法来扩展基类
继承的优缺点
优点
新的实现很容易,因为大部分是继承而来的
很容易修改和扩展已有的实现
缺点
打破了封装,因为基类向子类暴露了实现细节
白盒重用,因为基类的内部细节通常对子类是可见的
当父类的实现改变时可能要相应的对子类做出改变
不能在运行时改变由父类继承来的实现
由此可见,组合比继承具有更大的灵活性和更稳定的结构,一般情况下应该优先考虑组合。只有当下列条件满足时才考虑使用继承:
子类是一种特殊的类型,而不只是父类的一个角色
子类的实例不需要变成另一个类的对象
子类扩展,而不是覆盖或者使父类的功能失效
实例
参见Effective Java第四章第14条
package com.laz.learning; import java.util.Collection; import java.util.Iterator; import java.util.Set; public class ForwardingSet<E> { private Set<E> s; public ForwardingSet(Set<E> s) { this.s = s; } public int size() { return s.size(); } public boolean isEmpty() { return s.isEmpty(); } public boolean contains(Object o) { return s.contains(o); } public Iterator<E> iterator() { return s.iterator(); } public Object[] toArray() { return s.toArray(); } public <T> T[] toArray(T[] a) { return s.toArray(a); } public boolean add(E e) { return s.add(e); } public boolean remove(Object o) { return s.remove(o); } public boolean containsAll(Collection<?> c) { return s.containsAll(c); } public boolean addAll(Collection<? extends E> c) { return s.addAll(c); } public boolean retainAll(Collection<?> c) { return s.retainAll(c); } public boolean removeAll(Collection<?> c) { return s.removeAll(c); } public void clear() { s.clear(); } @Override public boolean equals(Object obj) { return s.equals(obj); } @Override public int hashCode() { return s.hashCode(); } @Override public String toString() { return s.toString(); } } package com.laz.learning; import java.util.Collection; import java.util.Set; public class InstrumentedSet<E> extends ForwardingSet<E> { private int addCount = 0; public InstrumentedSet(Set<E> s) { super(s); } public boolean add(E e) { addCount ++; return super.add(e); }; @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } }
如果在扩展一个类的时候,仅仅是增加了一些新的方法,而不覆盖现有的方法,你可能为认为是安全的。虽然这种扩展方式比较安全一些,但也并非完全没有风险。如果类在后续的发行版本中获得了一个新的方法,并且不幸的是,你给子类提供了一个签名相但返回类型不同的方法,那么这样的子类将无法通过编译。如果给子类提供 的方法带有与新的超类方法完全 相同的签名和返回类型,实际上就覆盖了超类中的方法,因此又回到了上述的两个问题上去了。此外,你的方法是否能够遵守新的超类方法的约定,这也是很值得怀疑的,因为当你在编写子类方法的时候,这个约定根本没有面世。
幸运的是,有一种办法可以避免前面提到的所有的问题。不用扩展现有的类,而是在新的类中增加一个私有域,它引用现有类的一个实例。这种设计被称为“复合”,因为现有的类变成了新类的一个组件。新类中的每个实例方法都可以调用被包含的现有类实例中对应的方法,并返回它的结果。这被称为“转发”,新类中的方法被称为转发方法。这样得到的类将会非常稳固,它不依赖于现有类的实现细节
继承的功能非常强大,但是也存在许多的问题,因为它违背了封装原则。只有当子类和超类之间确实存在子类型关系时,使用继承是最适当的。即使如此,如果子类和超类处在不同的包中,并且超类并不是为了继承而设计的,那么继承将会导致脆弱性。为了避免这种脆弱性,可以用复合和转发机制来代替继承,尤其是当存在适当的接口可以实现包装类的时候。包装类不仅比子类更加健壮,而且 功能 也更加强大。
需要注意一点:包装类不适合用在架设框架中;在回调框架中,对象把自身的引用 传递给其它的对象,用于后续的调用。因为被包装起来的对象并不知道它外面的包装对象。这被称为SELF问题
相关推荐
题目中的第一个选项“同一问题的不同表现形式”(A) 描述了设计模式的主要应用场景之一。设计模式帮助开发者处理常见的软件设计难题,确保代码的可读性、可维护性和可扩展性。 ### 2. 面向对象的基本原则 面向对象...
- **合成/聚合复用原则**:优先使用对象组合而不是继承来达到代码复用的目的,这有助于降低类间的耦合度。 - **迪米特法则**:一个对象应该尽可能少地与其他对象发生相互作用。这有助于减少系统的复杂性。 #### 4. ...
### Java设计模式与原理 #### 一、概述 在软件工程领域,设计模式是指在特定情境下解决软件设计问题的最佳实践。《Java 设计模式与原理》这本书被誉为Java程序员的必备指南之一,它深入浅出地讲解了23种经典的设计...
### Java设计模式选择题复习知识点详解 #### 工厂系列模式的优缺点 - **优点**: - **解耦**:工厂模式的核心优势之一在于它让用户代码与具体产品的实现相分离,使得用户无需了解产品是如何创建的,只需要知道...
Java设计模式是软件开发中的重要概念,它是一种在特定情境下解决常见问题的经验总结和最佳实践。刘伟编著的《Java设计模式》是讲解这一主题的经典书籍,它涵盖了7个面向对象设计原则和24个常用的设计模式。在这本书...
Java软件设计模式是软件开发中的重要概念,它们是经过时间考验的最佳实践,旨在解决常见的设计问题,提高代码的可读性、可维护性和可扩展性。以下是对标题和描述中涉及的一些关键知识点的详细说明: 1. **单一职责...
Java设计模式详解合集是一份宝贵的资源,包含了丰富的面向对象设计原则和多种设计模式的深入讲解。这份资料旨在帮助开发者提升软件设计能力,遵循良好的编程实践,提高代码的可读性、可维护性和复用性。以下是其中...
6. **合成复用原则**(Composite Reuse Principle):优先使用对象的组合/聚合,而不是继承,以减少类间的耦合。 **Java中的23种设计模式** Java中的23种设计模式涵盖了上述三类。例如,工厂方法模式是一种创建型...
Java的23种设计模式被广泛应用于各种项目中,以提高代码的可读性、可维护性和可扩展性。 首先,设计模式可以分为三大类: 1. 创建型模式: - 工厂方法模式:提供一个创建对象的接口,但由子类决定要实例化的类。 ...
Java设计模式是面向对象设计的重要组成部分,它是一种在特定情境下解决常见问题的标准化解决方案。设计模式总结主要包括创建型、结构型和行为型这三大类,每类模式都有其特定的应用场景和目标。 1. 创建型模式...
文档标题为“JAVA设计模式学习【技术文档】”,表明这是一份专注于Java语言的设计模式学习资源。设计模式作为软件工程领域的重要组成部分,它提供了一系列面向对象设计问题的模板式解决方案,是提升软件设计水平与...
这包括单一职责原则(每个类只有一个职责)、接口隔离原则(接口只服务于其需要使用它的类)、依赖倒转原则(依赖于抽象而非具体实现)、里氏替换原则(子类可以替换父类并保持一致的行为)、开闭原则(对扩展开放,...
在讨论Java设计模式的文档中,文章首先强调了软件设计阶段的重要性,指出软件的好坏很大程度上取决于前期的设计,而设计模式在软件设计中的核心作用在于确保代码的可读性、易理解性、可复用性和可靠性。文章接下来对...
在学习Java设计模式时,掌握其概念、应用、优点和缺点非常重要。设计模式是软件开发中经常使用的一种通用解决方案模板,用于解决特定上下文中的常见问题。它们源于设计模式的研究,通常被认为是软件设计中的最佳实践...
### JAVA设计模式的六大原则 #### 一、开闭原则(Open-Close Principle) **定义:** 开闭原则强调的是软件实体(类、模块、函数等)应该是可扩展的但不可修改的,即软件实体应该对扩展开放,对修改关闭。 **实践...
Java设计模式是软件开发中的一种最佳实践,它旨在提高代码的可维护性、可复用性和灵活性。设计模式是解决常见问题的经验总结,分为创建型、结构型和行为型三类。本文将详细讲解Java设计模式的一些核心原则和模式。 ...
Java设计模式详解 设计模式是软件开发中的重要概念,它是一种在特定场景下解决常见问题的标准解决方案,旨在提高代码的可重用性、可读性和可维护性。设计模式是面向对象编程领域的宝贵经验总结,它将过去成功的编程...
设计模式实用思想,设计模式的三个重要原则 一,针对接口编程,而不是针对实现编程 二,优先使用对象组合,而不是类继承 三,封装变化点