在软件开发中,我们经常会遇到需要为现有对象添加额外属性或行为的场景,但又受到多种限制。例如,当我们面对一个图的顶点接口实现时,可能需要为每个顶点存储一个额外的属性(如其在位置列表中的索引),同时必须满足以下严格条件:
传统的解决方案,如使用 HashMap(在C++中通常是 std::unordered_map),虽然平均时间复杂度为O(1),但其最差情况时间复杂度为O(n),因此不符合严格的O(1)最差情况要求。直接尝试通过继承来扩展顶点类,又会受限于私有嵌套类的访问权限。
面对这些挑战,我们需要一种既能规避原始代码修改,又能满足性能和访问限制的策略。
组合模式是一种强大的设计模式,它允许我们将一个类的实例作为另一个类的成员。通过这种方式,我们可以创建一个新的“包装”类,它内部包含原始对象,并在此基础上添加新的属性。这种方法完美地解决了不允许修改原始代码和私有嵌套类的限制。
核心思想: 创建一个新的类 MyVertex,它将包含一个 GivenVertex 类型的成员变量,以及我们想要添加的新属性(例如 position)。
优点:
示例代码:
#include// 替换 以提高可读性 #include // 用于断言 // 假设这是我们不能修改的原始顶点类 class GivenVertex { private: int color = 3; // 原始属性 // ... 可能还有其他私有成员 public: GivenVertex() {} int getClr() { retu rn color; } // ... 可能还有其他公共方法 }; // 我们的新类,使用组合模式来扩展功能 class MyVertex { public: int position; // 新添加的属性 GivenVertex V; // 包含原始GivenVertex的实例 // 构造函数,接收一个GivenVertex实例和新属性的值 MyVertex(GivenVertex originalVertex, int newPosition) { this->V = originalVertex; this->position = newPosition; } // 可以添加其他方法来封装对V的操作,例如: int getOriginalColor() { return V.getClr(); } }; int main() { // 创建一个原始的GivenVertex实例 GivenVertex originalGV = GivenVertex(); // 使用组合模式创建我们的MyVertex int pos = 7; MyVertex myExtendedVertex = MyVertex(originalGV, pos); // 验证新属性和原始属性是否正确 assert(myExtendedVertex.V.getClr() == 3); // 访问原始属性 assert(pos == myExtendedVertex.position); // 访问新属性 std::cout << "Original Color: " << myExtendedVertex.V.getClr() << ", New Position: " << myExtendedVertex.position << std::endl; return 0; }
在上述代码中,MyVertex 通过持有 GivenVertex 的一个实例 V,成功地将 GivenVertex 的功能和我们需要的 position 属性结合起来。当我们创建一个 MyVertex 对象时,它会包含一个 GivenVertex 对象,并且我们可以通过 myExtendedVertex.V 访问 GivenVertex 的原始功能,通过 myExtendedVertex.position 访问新添加的属性。
继承是面向对象编程中扩展功能的一种常见方式。如果原始类不是私有嵌套类,并且允许被继承,那么创建一个子类并在其中添加新属性也是一个可行的方案。
核心思想: 创建一个新类 MyVertex,它继承自 GivenVertex,并在 MyVertex 中添加新的属性 position。
局限性:
示例代码(假设 GivenVertex 可被继承):
#include#include // 假设这是可被继承的原始顶点类 class GivenVertex { private: int color = 3; public: GivenVertex() {} int getGivenClr() { // 更改方法名以避免与MyVertex中的getClr混淆 return color; } }; // 使用继承模式创建我们的MyVertex class MyVertex : private GivenVertex { // 这里使用私有继承 public: int position; MyVertex(int newPosition) { this->position = newPosition; } // 提供一个公共方法来访问父类的功能 int getMyClr() { return getGivenClr(); // 通过父类方法访问原始属性 } }; int main() { int pos = 7; MyVertex myExtendedVertex = MyVertex(pos); // 验证新属性和原始属性 assert(myExtendedVertex.getMyClr() == 3); // 访问原始属性 assert(pos == myExtendedVertex.position); // 访问新属性 std::cout << "Original Color (via MyVertex): " << myExtendedVertex.getMyClr() << ", New Position: " << myExtendedVertex.position << std::endl; return 0; }
在这个继承示例中,MyVertex 私有继承自 GivenVertex。私有继承意味着 GivenVertex 的公共和保护成员在 MyVertex 中变为私有。因此,为了从外部访问 GivenVertex 的功能(如 getGivenClr()),MyVertex 必须提供一个公共的“转发”方法(如 getMyClr())。
重要提示: 考虑到原始问题中“私有嵌套类”的限制,继承方案在大多数情况下是不可行的。组合模式是更通用且更符合原始约束的解决方案。
在选择扩展现有对象的方式时,请考虑以下因素:
原始类的可访问性和可修改性:
性能要求(O(1) 最差情况):
关系建模:“is-a” vs. “has-a”:
代码复杂性与维护:
当需要在不修改原始代码、满足O(1)最差情况检索,且原始对象为私有嵌套类等严格限制下为对象添加新属性时,组合模式(通过创建新类来包装原始对象并添加新属性)是最推荐且最稳健的解决方案。它能够有效规避私有嵌套类的限制,并保证属性检索的性能。
尽管继承模式在面向对象编程中是扩展功能的基础,但其适用性受到原始类可访问性和可继承性的严格限制。在面临不可修改或私有嵌套类时,应优先考虑组合模式,以实现灵活、高效且符合约束的扩展。