设计模式,将数据添加到类(第三方)而不进行修改

时间:2012-10-22 10:31:55

标签: c++ design-patterns

当我必须扩展类的行为而不修改它时,我经常使用设计模式visitor。它添加了类似成员的函数,而无需修改它所使用的类的核心。

或多或少以同样的方式,我需要扩展第三方类,但主要是数据,而不是行为。

在这种情况下,我经常使用与键MyClass*匹配的std :: map,其值为MyClassExtender。 MyClassExtender包含所有附加信息。

在这样做时,我偶然想知道是否有其他方法可以做到这一点,可能更常见或更“最佳实践”。我应该将此添加剂类称为扩展器吗? 是否有这种模式的名称......

Nota Bene:我可以简单地在一个新类中聚合MyClass *和MyClassExtender,但我需要经常给出一个MyClass *来访问MyClassExtender,所以st :: map非常方便。

2 个答案:

答案 0 :(得分:2)

为什么不把这个类子类化?继承是扩展类的方法,无论是行为还是状态。除非您只想将类的实例与其他数据相关联,否则它根本不会扩展,并且std :: map是正确的答案。

答案 1 :(得分:1)

所以 - 在带有扩展对象的struct中创建你的MyClass对象:

struct MyClassEx {
  MyClassExtension extension;
  MyClass object;
};

为了使其对不同类型更具鲁棒性 - 使用示例中的模板:http://ideone.com/mmfK83

以下解决方案的灵感来自std::shared_ptr/std::make_shared

template <typename Type>
struct LinkExtension;

template <typename Type>
struct TypeEx {
    using Extension = typename LinkExtension<Type>::Type;

    alignas(Type) uint8_t objectData[sizeof(Type)];
    alignas(Extension) uint8_t extensionData[sizeof(Extension)];

    Type* getObject() { return reinterpret_cast<Type*>(objectData); }
    const Type* getObject() const { return reinterpret_cast<const Type*>(objectData); }
    Extension* getExtension() { return reinterpret_cast<Extension*>(extensionData); }
    const Extension* getExtension() const { return reinterpret_cast<const Extension*>(extensionData); }

    template <class... Args>
    TypeEx(Args&&... args) 
    {
        new (objectData) Type(std::forward<Args>(args)...);
        new (extensionData) Extension();
    }
    ~TypeEx() 
    {
        getObject()->~Type();
        getExtension()->~Extension();
    }
    TypeEx(const TypeEx&) = delete;
    TypeEx& operator = (const TypeEx&) = delete;
};

还有一些辅助函数:

template <typename Type, class... Args>
Type* createObjectEx(Args&&... args)
{
   TypeEx<Type>* retVal = new TypeEx<Type>(std::forward<Args>(args)...);
   return retVal->getObject();
}

template <typename Type>
typename LinkExtension<Type>::Type& getObjectEx(Type* obj)
{
   static_assert(std::is_standard_layout<TypeEx<Type>>::value, "Oops");
   static_assert(offsetof(TypeEx<Type>, objectData) == 0, "Oops");
   TypeEx<Type>* retVal = static_cast<TypeEx<Type>*>((void*)obj);
    return *(retVal->getExtension());
}

template <typename Type>
const typename LinkExtension<Type>::Type& getObjectEx(const Type* obj)
{
    static_assert(std::is_standard_layout<TypeEx<Type>>::value, "Oops");
    static_assert(offsetof(TypeEx<Type>, objectData) == 0, "Oops");
    const TypeEx<Type>* retVal = static_cast<const TypeEx<Type>*>((const void*)obj);
    return *(retVal->getExtension());
}

template <typename Type>
void deleteObjectEx(const Type* obj)
{
    const TypeEx<Type>* objectEx = static_cast<const TypeEx<Type>*>((const void*)obj);
    delete objectEx;
}

如何将扩展链接到班级:

class MyClass {
public:
   virtual ~MyClass() = default; 
};
struct MyClassExtension {
    int a;
    int b;
};
template <>
struct LinkExtension<MyClass> {
    using Type = MyClassExtension;  
};

证明它有效:

void printExtension(MyClass* object);
int main() {
    MyClass* object = createObjectEx<MyClass>();
    MyClassExtension& extension = getObjectEx(object);
    extension.a = 1;
    extension.b = 2;
    printExtension(object);
    deleteObjectEx(object);

    TypeEx<MyClass> objectEx;
    objectEx.getExtension()->a = 3;
    objectEx.getExtension()->b = 4;
    printExtension(objectEx.getObject());
}

void printExtension(MyClass* object)
{
    MyClassExtension& extension = getObjectEx(object);
    std::cout << extension.a << ' ' << extension.b << std::endl;
}

如果您的编译器不支持可变参数模板,那么解决方案仍然可行,但需要更多的手工工作才能完成。