【问题标题】:oop design for handling objects polymorphically用于多态处理对象的 oop 设计
【发布时间】:2011-11-15 06:44:10
【问题描述】:

我有从一个基础派生的不同类型的对象(例如圆形、矩形、多边形...等等),并且我有一组可应用于这些对象的预定义操作(例如移动、旋转、复制...等等)。 在这种情况下我喜欢的解决方案是访问者模式。我有每个操作的访问者,其中包含所有类型的全套操作。这使我可以在不更改对象本身的情况下向对象添加新功能。操作肯定会比新的对象类型更频繁地更改和添加,所以这真的很棒,但是有一个问题。 上述所有内容都是作为一个库实现的,将来可能(并且将)由其他程序员扩展。程序员可以添加新类型的对象并实现或不实现操作。例如,可以添加支持移动但不支持旋转的“三角形”类。在这种情况下,程序员将无法扩展我的访问者来处理他的对象的移动操作。 我看到了另一种解决方案 - 具有 i_movable、i_rotatable、i_copyable 等接口。每个类都实现一个或多个此类接口,然后我检查我的库,如果指定的对象支持带有 dynamic_cast 的给定接口并应用操作。类似的东西

if (i_movable* m = dynamic_cast<i_movable>(obj)) m->move(10, 20);

但我不太喜欢这种解决方案。谁能提出更好的建议?

这对于 OOP 设计可能并不重要,但实现语言是 C++,所以我受限于 C++ 功能。

更新

如果没有人能想到更好的解决方案,请您至少为提议的解决方案投一票吗?

提前致谢。

【问题讨论】:

  • 您在这里用接口描述的几乎就是 COM 的工作原理。你能描述一下“我不喜欢这个”部分吗?您在哪一部分受 C++ 限制?据我所知,您回答了自己的问题。
  • 是的,它类似于 COM。我不喜欢我必须将所有功能放在一个类中。例如,我必须在三角形类本身中实现三角形的复制序列化、移动、旋转和所有其他操作。这使得类非常大并且难以支持(确实有很多不同的操作)。我也尽量避免强制转换。我并不是说我不会采用那种解决方案,我只是想知道是否有更好的方法。
  • 关于 C++ 限制:我并不是说我有某种好的解决方案,但我受到 C++ 的限制。刚刚提到不要混淆其他人,也不要让他们提出无法使用 C++ 实现的解决方案。
  • 我同意你的观点,但是你能写一些通用的代码来进行序列化、移动、旋转和其他需要的操作吗?
  • 好吧,我可以为某些操作编写通用代码,但不能为其他操作编写通用代码。例如,我可以编写move 函数,该函数适用于一组几何图形,但我不能编写通用序列化函数来复制和保存对象。

标签: c++ oop design-patterns polymorphism


【解决方案1】:

而不是这个:

if (i_movable* m = dynamic_cast<i_movable>(obj)) m->move(10, 20);

你可以使用一些模板元编程技巧(看看boost::is_base_of):

#include "boost/type_traits/is_base_of.hpp" 
#include <iostream>

struct i_move
{
    virtual void move(const float, const float ) = 0;
};
struct Triangle
{
    // not movable
};
struct Circle : i_move
{
    virtual void move(const float, const float )
    {
        // move me
    }
};

template< class D >
bool IsMovable( D&)
{
    return boost::is_base_of<i_move,D>::value;
}

int main()
{
    Circle a;
    Triangle b;
    std::cout<<std::boolalpha<<IsMovable(a)<<std::endl;
    std::cout<<std::boolalpha<<IsMovable(b)<<std::endl;
}

【讨论】:

  • 函数模板IsMovable不应该只有一个类型参数吗?表示可移动性的基类的类型应该总是相同的,对吧?
  • 所有这些对象都派生自一个基类(比如说几何),而你处理基类指针,所以我不能这样实现,我们需要在这里进行动态转换。
【解决方案2】:

如果我对您的理解正确,如果不支持该操作,则您什么也不做(或更一般地说,具有独立于相关对象的统一行为)。然后一个简单的解决方案是在基类中提供默认行为,然后如果派生类没有实现自己的版本,默认行为会自动启动而无需任何额外的机制。

如果我没记错的话,在 Borland 的 Turbo Vision 中,此类方法被称为“伪抽象”:它们并不是真正抽象的(不允许调用它们,并且没有严格需要在派生类中重写它们),但它们除非被覆盖,否则什么都不做。

【讨论】:

  • 是的。你没看错,但正如我已经说过的那样,大约有 50 种不同的操作,我不想把它们都放在一个基类中。我不想在一个类中有太多的功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-18
  • 2011-01-02
  • 2013-02-08
  • 2012-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多