复合优先于继承

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问题

摘抄自:Effective Java

<think>好的,我需要回答用户关于C++中关系复合的实现与用法的问题。首先,我要明确“关系复合”在这里的具体含义。根据用户提供的引用,特别是引用[3]提到的“非复合转换的复制”,可能用户指的是类之间的复合关系,即组合(Composition)或者对象组合的设计模式。 接下来,我需要回顾C++中的复合(组合)概念。复合是指一个类包含另一个类的对象作为成员,通过这种方式实现代码复用和模块化。这与继承不同,复合强调的是“拥有”关系,而不是“是”关系。例如,汽车类包含引擎类的对象,这就是复合。 然后,我需要参考用户提供的引用内容。引用[1]提到了结构体和共用体,这可能与数据成员的组合有关。引用[2]讨论了继承复合和委托,其中复合作为三种设计模式之一,需要详细说明。引用[3]虽然涉及转换的复制,但用户可能想了解如何通过复合结构来实现这样的功能。 接下来,我需要组织回答的结构。首先解释复合的概念,与继承对比,说明其特点。然后给出C++中的具体实现方式,比如类中包含其他类的对象作为成员。需要代码示例来演示,比如汽车和引擎的例子。还要提到匿名成员和访问权限,可能参考引用[1]中的共用体用法。最后,说明复合的应用场景和优缺点。 同时,用户要求生成相关问题,我需要根据内容提出几个相关的问题,比如复合继承的区别、复合的设计模式应用等。 需要确保使用正确的中文术语,如“组合”或“复合”,并根据引用中的例子来构造示例代码。例如,引用[1]中的结构体widget包含匿名共用体,这可以作为复合的一个例子。但用户的问题更偏向于类之间的关系,所以可能需要另一个更典型的例子,如汽车和引擎。 还要注意用户提到的“关系复合”可能涉及更复杂的情况,比如多个对象的组合或复合设计模式,需要涵盖这些方面。需要确保回答准确,引用相关文献,比如引用[2]中关于复合的部分。 最后,检查是否符合格式要求:行内公式用$...$,独立公式用$$...$$,虽然这个问题可能不涉及数学公式,但如果有的话需要正确应用。确保LaTeX语法正确,用中文回答,结构清晰,分步骤说明,并在引用处添加标识。</think>关于C++中关系的复合(Composition)的实现与用法,以下是结构化说明: --- ### 一、复合关系定义 复合关系是面向对象设计中**"has-a"(拥有)**关系的体现,表示一个类包含另一个类的对象作为成员。与继承的**"is-a"**关系不同,复合通过**组合现有类的功能**实现代码复用[^2]。 ### 二、C++实现方式 #### 1. 直接成员包含 ```cpp class Engine { public: void start() { cout << "Engine started\n"; } }; class Car { private: Engine engine; // 复合关系:Car拥有Engine对象 public: void startCar() { engine.start(); cout << "Car ready\n"; } }; ``` *特点*:Engine对象的生命周期与Car对象完全绑定。 #### 2. 指针/引用成员(委托) ```cpp class AudioDecoder { public: virtual void decode() = 0; }; class MP3Player { private: AudioDecoder* decoder; // 委托关系 public: MP3Player(AudioDecoder* d) : decoder(d) {} void play() { decoder->decode(); } }; ``` *特点*:支持运行时多态,但需要管理对象生命周期[^2]。 ### 三、关键特性对比 | 特性 | 复合 | 继承 | |---------------------|--------------------------|--------------------------| | 关系类型 | 横向组合 | 纵向扩展 | | 耦合度 | 低耦合 | 高耦合 | | 生命周期管理 | 自动管理 | 自动管理 | | 功能复用方式 | 黑箱复用 | 白箱复用 | ### 四、高级用法 #### 1. 匿名成员(参考引用[1]) ```cpp struct GraphicsCard { union { // 匿名共用体成员 int vram_size; char model[20]; }; }; ``` *优势*:直接访问子成员,无需中间对象。 #### 2. 复合设计模式 ```cpp class FileSystem { public: void read() {/*...*/} }; class Logger { private: FileSystem fs; // 复合关系 public: void log(const string& msg) { fs.read(); // 记录日志操作 } }; ``` *应用场景*:日志系统复用文件系统功能[^3]。 ### 五、选择原则 1. **优先使用复合**:当不需要多态特性时 2. **明确生命周期**:成员对象是否需要与宿主对象共存亡 3. **接口隔离**:通过复合实现接口最小化原则 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值