继承层次结构与多重继承(C ++)

时间:2011-05-08 12:57:07

标签: c++ oop inheritance hierarchy

好吧,我在考虑过去几天的设计决定,因为我仍然不能偏爱另一个,我想也许别人有个主意。

情况如下:我有几个不同的接口类抽象几个通信设备。由于这些设备的性质不同,因此界面也不同,因此并不真正相关。让我们称之为 IFooDevice IBarDevice 。随着时间的推移可能会添加更多设备类型语言是C ++。

由于其他组件(从现在开始称为客户端)可能想要使用其中一个或多个设备,因此我决定提供 DeviceManager 类来处理运行时对所有可用设备的访问。由于设备类型的数量可能会增加,我想平等对待所有设备(从经理的角度来看)。但是,客户端将请求某种设备类型(或基于某些属性的设备)。

我想到了两种可能的解决方案:

第一种是某种间歇层次结构。所有设备都将子类化一个通用接口 IDevice ,它将提供管理和设备查询所需的(虚拟)方法(如 getProperties() hasProperties(),...)。 DeviceManager 然后有一个指向 IDevice 的指针集合,在某些时候,从 Base Derived 的演员阵容将是必要的 - 使用管理器中的模板方法或客户端的请求之后。

从设计的角度来看,我认为分离管理设备和特定设备本身界面的问题会更加优雅。因此,它将导致两个不相关的接口: IManagedDevice ,例如的 IFooDevice 即可。真实设备需要从两者继承以便“成为”特定设备类型并且是可管理的。经理只会管理指向 IManagedDevice 的指针。但是,如果客户端想要使用管理器提供的设备,则在某些时候需要在现在不相关的类之间进行转换(例如,从 IManagedDevice 转换为 IFooDevice )。

我必须在这里选择两个邪恶中较小的一个吗?如果是这样的话会是哪一个?或者我会错过什么?

修改

关于“管理”部分。这个想法是让库提供各种不同的通信设备(客户端)应用程序可以使用和共享。管理仅仅归结为实例的存储,注册新设备和查找某个设备的方法。为任务选择“正确”设备的责任取决于客户端,因为它最了解它对通信的要求。为了重用并共享可用设备(并且我指的是真实实例而不仅仅是类),我需要一个到所有可用设备的中央访问点。我不太喜欢经理本身,但在这种情况下,这是我唯一可以做到的。

5 个答案:

答案 0 :(得分:1)

我认为访客模式是更好的选择。

答案 1 :(得分:1)

我认为汤姆建议的内容可能会有所改变,以满足您的需求:

class IManagedDevice
{
    IDevice* myDevice;

    /* Functions for managing devices... */
};

在这种情况下,IDevice是一个所有设备都从中继承的空接口。它没有带来任何实际好处,只需使类层次结构处理更加可忍受。

然后,您可以通过某种设备类型ID询问特定设备(IFooDeviceIBarDevice)。

如果您只需要一个通用代码来管理设备,然后将每个设备传递到适当的位置,我认为您可以使用以下内容:

class IDevice
{
    virtual void Handle() = 0;
};

class IFooDevice : public IDevice
{
    virtual void Handle()
    {
        this->doFoo();
    }

    virtual void doFoo() = 0;
}

class IBarDevice : public IDevice
{
    virtual void Handle()
    {
        this->doBar();
    }

    virtual void doBar() = 0;
}

管理员调用Handle函数。

答案 2 :(得分:1)

我想我会想要一个简单的解决方案,为Device提供基类,负责在全局设备列表中注册设备,然后使用静态方法查找它们。类似的东西:

struct Device
{
    static Device *first;  // Pointer to first available device
    Device *prev, *next;   // Links for the doubly-linked list of devices

    Device() : prev(0), next(first)
    {
        if (next) next->prev = this;
        first = this;
    }

    virtual ~Device()
    {
        if (next) next->prev = prev;
        if (prev) prev->next = next; else first = next;
    }

    private:
        // Taboo - the following are not implemented
        Device(const Device&);
        Device& operator=(const Device&);
 };

然后您可以从Device派生所有设备,它们将自动放置在构建的全局列表中,并在销毁时从全局列表中删除。

Device::first开始,然后device->next开始,您的所有客户都可以访问所有设备列表。通过dynamic_cast<NeededDeviceType*>(device)客户端可以检查设备是否与他们需要的设备兼容。

当然,在Device级别也可以导出在每种设备类型中实现的任何方法(例如,描述字符串,确保一个客户端独占使用的锁定方法等)。

答案 3 :(得分:0)

与设备通信时,我完全将设备和通信管理器分开。

我有一个基于Boost.Asio的简单通讯管理器。界面就像是

/** An interface to basic communication with a decive.*/
class coms_manager
{
public:
  virtual 
  ~coms_manager();
  /** Send a command. */
  virtual 
  void 
  send(const std::string& cmd) = 0;

  /** Receive a command.
   *  @param buffsize The number of bytes to receive.
   *  @param size_exactly True if exactly buffsize bytes are to be received.  If false, then fewer bytes may be received.
   */
  virtual 
  std::string 
  recv( const unsigned long& buffsize = 128, 
    const bool& size_exactly = false) = 0 ;

  /** Timed receive command.
   *  @param buffsize The number of bytes to receive.
   *  @param seconds The number of seconds in the timeout.
   *  @param size_exactly True if exactly buffsize bytes are to be received.  If false, then fewer bytes may be received.
   */
  virtual 
  std::string 
  timed_recv( const unsigned long& buffsize = 128, 
      const double& seconds = 5, 
      const bool& size_exactly = false) = 0;
};

然后我为tcp(以太网)和串行通信实现了这个接口。

class serial_manager : public coms_manager {};
class ethernet_manager : public coms_manager {};

然后,每个设备都包含(或指向)(而不是继承)coms_manager个对象 例如:

class Oscilloscope
{
  void send(const std::string& cmd)
  {  
     m_ComsPtr->send(cmd);
  }
private:
  coms_manager* m_ComsPtr;
};

然后,您可以通过更改指针指向的内容来交换通信方法。

对我来说,这没有多大意义(示波器是通过串行或通过以太网连接的,因此我实际上选择了

template<class Manager>
class Oscilloscope
{
  void send(const std::string& cmd)
  {  
     m_Coms.send(cmd);
  }
private:
  Manager m_Coms;
};

我现在用

Oscilloscope<serial_manager> O1(/dev/tty1);   // the serial port
Oscilloscope<ethernet_manager> O2(10.0.0.10); //The ip address

这更有意义。

至于你有关通用设备接口的建议。我也从那开始,但后来不确定它的实用性 - 我一直想知道我发送命令的确切设备,我既不需要也不想通过抽象接口工作。

答案 4 :(得分:0)

乍一看,如果需要管理所有设备,并且使用未知设备无法完成其他任何操作,第一种方法对我来说似乎没问题。一般设备的元数据(例如名称,......)通常是管理设备所需的数据。

但是,如果您需要将管理和设备功能之间的界面分开,则可以使用virtual inheritance IManagedDevice IFooDevice 都是同一具体设备的接口,因此它们都有一个共同的虚拟基础 IDevice

具体而言(run code)

#include <cassert>

class IDevice {
public:
  // must be polymorphic, a virtual destructor is a good idea
  virtual ~IDevice() {}
};

class IManagedDevice : public virtual IDevice {
    // management stuff
};

class IFooDevice : public virtual IDevice {
    // foo stuff
};

class ConcreteDevice : public IFooDevice, public IManagedDevice {
    // implementation stuff
};

int main() {
    ConcreteDevice device;
    IManagedDevice* managed_device = &device;
    IFooDevice* foo_device = dynamic_cast<IFooDevice*>(managed_device);
    assert(foo_device);
    return 0;
}
相关问题