7

我已经为这个设计问题苦苦挣扎了一段时间。我将尽我所能解释我正在尝试做什么以及我所看到的各种方法,我正在尝试什么以及为什么。

我在一个科学计算环境中工作,我反复处理相同类型的对象。想象一个包含太阳系的星系,每个太阳系都包含行星系统,每个行星系统都包含卫星。为此,我认为这种情况是一种“有”的情况,因此我使用组合来让银河系进入它的太阳系,让每个太阳系进入可以进入它们的卫星的行星系统:每个类别作为自己的班级。

通常情况下,我正在处理的各种问题包含有关这些对象的不同类型的数据。而且,随着不同类型的数据可用,我可以对我的对象做某些事情。所以,当我有可用的数据类型 1 时,我创建了以下类

class GalaxyOne { /* … */ };
class SolarSystemOne { /* … */ };
class PlanetOne{ /* … */ };
class MoonOne{ /* … */ };

当我有可用的数据类型 2 时,我创建

class GalaxyTwo { /* … */ };
class SolarSystemTwo { /* … */ };
class PlanetTwo{ /* … */ };
class MoonTwo{ /* … */ };

每个类的复合方面是通过使用容器变量来处理的,例如包含指向所包含类的指针的向量。例如,在每个 Galaxy 类中都可以找到。

class GalaxyTwo{ /* … */
protected:
std::vector<SolarSystemTwo*> solarSystems;
/* … */
};

当我想将类组合成高阶类时,困难就来了。

class GalaxyOneTwo: public GalaxyOne, public GalaxyTwo{
/* Additional methods */
};

但是,这会在 solarSystems 向量中产生一个模糊问题,因为 GalaxyOneTwo 将具有来自 GalaxyOne 和 GalaxyTwo 的版本。此外,它继承的向量包含指向非 SolarSystemOneTwo 类型对象的指针,这将是必需的。所以,我想我可以创建一个模板类,我的所有对象都从我放置所有容器变量的地方继承。

template<MoonT,PlanetT,SolarSystemT>
    class PrimitiveGalaxy {
    private:
        std::vector<SolarSystemT*> solarSystems
};

class GalaxyOne: public PrimitiveGalaxy <MoonOne,PlanetOne,SolarSystemOne>{
    /* No longer defining any container variables for composition */
};

这种方法非常适用于那些基本的 Galaxy 类型(GalaxyOne 和 GalaxyTwo)。然而,每当我尝试创建组合星系类型时,我都会遇到各种模棱两可的问题。

class GalaxyOneTwo: public GalaxyOne, public GalaxyTwo, public     PrimitiveGalaxy<MoonOneTwo,PlanetOneTwo,SolarSystemOneTwo>{
    /* … */
};

如果我在 GalaxyOneTwo 中定义的任何方法中使用 solarSystems,我会感到模棱两可,因为它被定义了三次,一次是通过 GalaxyOne 和 GalaxyTwo 的每个继承版本,第三次是通过 GalaxyOneTwo。

我可以通过具体和使用来摆脱这种歧义

PrimitiveGalaxy::solarSystems

每次都引用正确的向量,但这是不可取的,因为它需要大量额外的输入和语法。

我考虑过在每个星系类型和原始星系之间使用友谊,但这需要类似程度的冗长写作。

我有一种预感,命名空间可以简化我的代码编写,但我不确定如何定义命名空间,以便在为 GalaxyOneTwo 定义的命名空间中,对 solarSystems 的任何引用都是对 PrimitiveGalaxy::solarSystems 的引用

编辑:

请注意,GalaxyOne 和 GalaxyTwo 之间的唯一区别不是 solarSystems 中包含的类的类型。由于每个类别处理与星系相关的不同数据,因此存在许多差异。因此,我创建了不同的类,这些类将具有不同的状态变量、用于这些状态变量的 getter 和 setter,以及用于计算和打印数据的方法。solarSystems 是给我带来问题的功能的一个示例,因此这就是我在这里所描述的。创建 GalaxyOneTwo 时,它将使用用于 GalaxyOne 和 GalaxyTwo 的相同数据,因此我想继承它们的所有变量和方法。而且因为数据可以以不同的方式组合,我需要在 GalaxyOneTwo 中为此创建新的方法。这些是导致我使用继承的许多差异中的一些。话虽如此,允许组合的容器变量给我带来了问题。在每个 solarSystem 类中都会有一个类似的向量,让他们可以访问他们的行星,依此类推。

编辑:

对于一个专门针对我的设计哲学的问题(而不是这个问题强调试图解决我当前的设计尝试),请参阅以下链接: Guidance in created design for multiple-inheritance composite classes in c++

4

4 回答 4

3

我认为你必须有 one class Galaxy, oneclass SolarSystem等。而你GalaxyOne的 ,GalaxyTwo SolarSystemOneSolarSystemTwo只是从这些类实例化的不同对象。

class SolarSystem { /* … */ };
class Planet{ /* … */ };
class Moon{ /* … */ };

class Galaxy{ /* … */
 public: // Galaxy(...?...){for (...) {read data??; solarSystem.push_back(createSolarSystem(data)); }
 void AddSolarSystem(SolarSystem* ss){solarSystem.push_back(ss);}
 protected:
   std::vector<SolarSystem*> solarSystems;
   /* … */
};

……

Galaxy GalaxyOne, GalaxyTwo;

如果我们无法使用这种简单的方法......让我们看看你的:

class GalaxyOneTwo: public GalaxyOne, 
                    public GalaxyTwo, 
                    public PrimitiveGalaxy<MoonOneTwo,PlanetOneTwo,SolarSystemOneTwo>{
    /* … */
 using PrimitiveGalaxy<MoonOneTwo,PlanetOneTwo,SolarSystemOneTwo>::solarSystems;
 GalaxyOneTwo(){solarSystems.reserve(10);}
};

在这里,您有三个私有向量:(使用using您使其可直接访问)

std::vector<SolarSystemOne*   > GalaxyOne::solarSystems;
std::vector<SolarSystemTwo*   > GalaxyTwo::solarSystems;
std::vector<SolarSystemOneTwo*> solarSystems; //GalaxyOneTwo::solarSystems;

这是你需要的吗?让它受到保护? 在此处输入图像描述

于 2013-02-25T19:57:37.577 回答
1

也许您可以更改数据模型以实现基于组件的某些东西。每个组件都可以包含您为不同类型提到的相同状态信息。你可以继承虚函数

class Galaxy
{
  // galaxy information

  // Virtual functions shared among galaxies
  virtual void sharedGalaxyFunction() = 0;

  // Vector containing all solar systems in this galaxy
  std::vector<SolarSystem*> solarSystems_;
};

class SolarSystem
{     
  // solar system info

  // Virtual functions shared among all solar systems
  virtual void sharedSolarSystemFunction() = 0;

  // Vector containing planets
  std::vector<Planets*> planets_;
};

// etc for planets...

然后,您可以继承不同类型的太阳系或星系来创建特殊情况并填写虚函数调用

class CoolSolarSystem : public SolarSystem
{
  // Special variables

  // Fill in the virtual function
  void sharedSolarSystemFunction();
};

然后,您可以使用指向您的特殊类型的指针填充不同基本类型内的容器。如果虚函数调用处理了足够多的信息,您应该能够避免转换回特殊类型。

于 2013-02-26T00:29:20.640 回答
0

您需要将“One”和“Two”版本之间的共同内容抽象为一组“Common”基类:

class GalaxyCommon {
    // all the common stuff between GalaxyOne and GalaxyTwo including
    std::vector<SolarSystemCommon *> solarSystems;
    ...
};

class GalaxyOne : public virtual GalaxyCommon {
    // all the stuff that is unique to model One
};

class GalaxyTwo : public virtual GalaxyCommon {
    // all the stuff that is unique to model Two
};

class GalaxyOneTwo : public virtual GalaxyOne, public virtual GalaxyTwo {
    // should be complete
};

请注意使用virtual-- required 以获得正常工作的多重继承。

于 2013-02-25T23:29:11.617 回答
0

因此,如果我正确理解您的问题:

  1. 有几种类型的星系在它们之间根本没有任何共同之处。
  2. 太阳和行星系统也是如此。
  3. 您希望组合这些不同类型的集合,但仍可以完全访问该类型的底层方法。

这听起来像是boost::any

所以现在你的 Galaxy 是:

#include <list>
#include <boost/any.hpp>
typedef std::vector<boost::any> collection;
class Galaxy
{
   collection SolarSystems;
   public:
   ...methods...
};
class SolarSystem
{
   collection PlanetarySystems;
   public:
   ...methods...
};
class PlanetarySystem
{
   collection Moons;
   public:
   ...methods...
};

当然,这并没有真正做任何事情,但确实允许您对它们进行分组。为了做任何有用的事情(调用方法),您仍然必须检测类型,将其转换为该类型,然后运行您需要的代码。

但是,这确实允许您将不同类型的对象组合在一起,我认为这就是您使用 GalaxyOneTwo 所追求的。这是有关如何执行此操作的更多信息。

于 2013-02-26T00:16:28.820 回答