假设我有一个基类和派生类,其中派生类实现了一些其他特定于制造的功能:
class Device {
// Base class
}
class DeviceFromSpecificManufacture : public Device {
// Derived
}
当我的程序运行时,它要求用户从一系列可用设备中选择一个设备。在这一点上,使用基类对我来说很好,因为我只需要基本的设备功能(与制造商无关):
std::vector<std::shared_ptr<Device>> availableDevices = getAvailableDevices();
// User selects device here, resulting in:
std::shared_ptr<Device> selectedDevice = ...
问题是:在某个时候,我将只需要使用实现制造特定功能的类。
我可以做到这一点的一种方法是,当程序需要使用特定功能时,将我的基本实例转换为派生类型。
std::shared_ptr<DeviceFromSpecificManufacture> specificDevice = std::dynamic_pointer_cast<DeviceFromSpecificManufacture>(selectedDevice);
// Here I would need to confirm that the cast was successful (as there's no guarantee
// that selectedDevice is an instance of DeviceFromSpecificManufacture) - which
// makes this feel even more wrong.
有一个更好的方法吗?我不能将特定功能移至基类,因为它实际上并不适用于所有设备,仅适用于某些设备。
最佳答案
垂头丧气几乎总是设计中矛盾的征兆。您的矛盾就在这里:
[…] it's fine for me to use the base class as I only require basic device functionality […]
[…] I'm going to need to only work with the classes that implement manufacture specific functionality.
显然,仅了解基类是不合适的,因为突然之间
原来,您确实必须了解更具体的类型!?
通过使一段代码带有一个
Device
,您可以表达:此段代码可与任何Device
一起使用。如果这段代码必须随后将给出的Device
转换为低,并检查它是否实际上可以处理的Device
类型,那么我们必须问自己一个问题:如果这段代码实际上不能与任何种类的代码一起使用的Device
,为什么接受任何种类的Device
作为输入?如果给此代码一个不能使用的Device
,会发生什么?在实现中必须向下转换的组件会说一件事,然后再做另一件事……建议阅读:Liskov substitution principle。问题在于哪种设计可以在您的特定应用程序中工作取决于特定的应用程序。在不了解该应用程序的情况下,很难提出什么是修复设计的好方法。但是,这里有一些想法:
为什么将所有设备存储在同一集合中?为什么不将设备分别存储在单独的集合中?这样一来,您不仅可以向用户显示设备,还可以按类别显示设备。这也意味着您不会丢弃所需的信息。
另外,即使您不知道数据结构中所有对象的具体类型,对象本身也总是知道它们是什么。 Double Dispatch pattern(基本上是访客模式的一种版本)可能会让您感兴趣。
最后,每当看到
std::shared_ptr
时,都要问自己:这个对象确实有多个所有者吗?实际的共享所有权方案应该很少。就您而言,您似乎将设备存储在容器中。可能是,包含该容器的所有内容都是这些设备的唯一所有者。因此 std::unique_ptr
可能是一个更合适的选择……
关于c++ - 在这种情况下,我应该/如何避免垂头丧气?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59365859/