面向对象的设计来模拟通用设备
Posted
技术标签:
【中文标题】面向对象的设计来模拟通用设备【英文标题】:Object oriented design to model generic device 【发布时间】:2022-01-23 18:22:57 【问题描述】:我在一次采访中被问到以下面向对象的系统设计问题。
有多种设备,如 Echo show、Echo dot、Echo tab、智能微波炉、Fire tv Stick 等。
回声秀 - 它有显示屏和扬声器。它适用于电源。 回声点 - 它有扬声器。它适用于电力供应。 回声标签 - 它有扬声器。它适用于电池供电。电池可以充电。 智能微波炉 - 它有屏幕显示。它适用于电力供应。 消防电视棒 - 它有扬声器。它适用于电力供应。所以基本上这些是 3 个类别,例如 - 扬声器/屏幕显示/扬声器和屏幕显示 有两类,如 - 电源/电池供应。 可以在任何这些设备上进行查询,例如打印状态。以下是每个设备的可能输出 -
回声显示 - “它正在充电”或“它没有充电”,具体取决于它是否连接到电源。此输出应出现在屏幕和扬声器上。 回声点 - “正在充电”或“未在充电”,具体取决于它是否连接到电源。此输出应来自扬声器。 Echo 选项卡 - “电池正在充电”或“电池未充电且电池电量为 70%”,具体取决于电池是否正在充电。此输出应来自扬声器。 智能微波炉 - “正在充电”或“未在充电”,具体取决于它是否连接到电源。此输出应出现在屏幕上。 Fire 电视棒 - “它正在充电”或“它没有充电”,具体取决于它是否连接到电源。此输出应来自扬声器。假设有用于在屏幕上说话和打印的内置类。如果我们将字符串传递给这些类对象,它们将完成各自的工作。
现在编写 4-5 个类来模拟这个场景。
设计应该是可扩展的,如果明天任何新设备都带有新的组合,那么它可以在不创建任何新类的情况下实现。所以你不应该为每个设备创建类。
这是我的面向对象解决方案,但面试官对此并不满意,尤其是vector<Output*> outputs
。他建议使用一些设计模式而不是矢量。你能想出更好的解决方案吗?
class Output
public:
virtual void print(string);
;
class Display : public Output
DisplayScreen obj;
public:
void print(string str) obj.print(str);
;
class Speaker : public Output
Audio obj;
public:
void print(string str) obj.print(str);
;
class PowerSupply
public :
virtual string get_status();
;
class BatteryPower : PowerSupply
bool isCharging;
int chargeLevel;
public :
string get_status();
;
class ElectricPower : PowerSupply
bool isCharging;
public :
string get_status();
;
class Device
vector<Output*> outputs;//I used vector because Echo show has both display and speaker
PowerSupply powersupply;
Device(vector<Output> &outputs, PowerSupply powersupply)
this->outputs = outputs;
this->powersupply = powersupply;
;
【问题讨论】:
@463035818_is_not_a_number 为什么要粘贴对象切片的链接?如果我将 vector 我们不知道究竟是什么让面试官不高兴,但std::vector<Output>
肯定是错误的,std::vector<Output*>
是好的。我正在考虑将问题作为重复问题关闭,但已经有一个答案可以帮助我更好地解释,所以我放弃了链接。
我将“vectorprint(string)
应该是 const
。在我看来,问题在于你没有与面试官沟通:“你想到了哪种模式?”或“应该针对vector
的哪个特定问题?”。我个人不赞成您使用vector
的理由。为什么不使用非类型模板参数std::size_t kOutputCount
和Device<1ULL>
的模板特化来创建Device
模板类?此外,this->outputs
是 vector
的 Output*
不是 Output
... 哦。那个构造函数是private
【参考方案1】:
vector<Output>
不允许继承,因为它直接存储 Output
s 而不是指针或引用。如果您将Display
或Speaker
存储在向量中,它将是sliced。
由于每个设备的输出都是唯一的,我会将其设为唯一指针的向量:
std::vector<std::unique_ptr<Output>> outputs;
【讨论】:
如果我将 vector我不认为面试官要求使用某种设计模式有任何建设性。设计模式是完成某事的工具;没有目标,设计模式就毫无意义。相反,他本可以说:“我们预计必须以最小的偏差构建许多类似的设备。您将如何实现这一目标?” insted “现在在随机设计模式上标记我们是金子。"。
除了技术问题John Kugelman answered here。我认为您可以使用多种设计模式,而不仅仅是传入设备和电源的向量。我脑海中的一些例子:
工厂模式
研究关键词:工厂模式
class DotFactory
DotFactory(/*maybe you need parameters*/);
Device* getDevice(/*more parameters e.g. speaker count*/);
建造者模式
研究关键词:构建者模式
根据复杂性和用例,您可以将设备构建器集成到设备中。为简单起见,我已经这样做了。
class Device
// Your stuff
vector<Output*> outputs;
PowerSupply powersupply;
Device(vector<Output*> &outputs, PowerSupply *powersupply);
// Builder functions
bool addPowerSupply(PowerSupply*);
bool addOutput(Output*);
// If outside your Device class also integrate:
Device* getBuiltDevice();
;
其他模式和组合(我希望能想出不止一个。也许有人可以编辑更多。)
设备的单一列表(大多数情况下是反模式)
根据用例,如果每个应用程序只需要一个,您还可以让任何东西(方法、容器、类等)保存您预定义的“设备”。
【讨论】:
【参考方案3】:我建议引入如下5个类,如下图:
PowerStatusProvider -> 用于电源状态等的接口/策略类 BatteryStatus -> 电池供电的所有事物的具体实现 WiredStatus -> 由电线供电的所有事物的具体实现 OutputHandler -> 使用提供的服务(屏幕、扬声器)将状态发布到任何端点 设备 -> 小工具的实际抽象我使用结构而不是类来缩短符号。电池电量通过依赖注入函子检索。 C++20 有 std::format,也许你的编译器也有。
// Assuming Screen and Speaker are provided in a common interface
struct OutputDevice
virtual void publish(std::string const & output) = 0;
virtual ~OutputDevice()=default;
using OutputDevice_sp = std::shared_ptr<OutputDevice>;
struct Screen : OutputDevice
void publish(std::string const & output) override
// ...
;
;
struct Speaker : OutputDevice
void publish(std::string const & output) override
// ...
;
;
/// Here goes the actual implementation
struct PowerStatusProvider
virtual std::string to_string(bool Connected) const = 0;
virtual ~PowerStatusProvider()=default;
;
struct BatteryStatus : PowerStatusProvider
BatteryStatus(std::function<double()> BatteryLevelProvider)
: mBatteryLevelProvider(BatteryLevelProvider)
std::string to_string(bool Connected) const override
if (Connected)
return "Battery is charging";
else
return std::format("Battery is not charging and battery level is %",mBatteryLevelProvider());
private:
std::function<double()> mBatteryLevelProvider;
;
struct WiredStatus : PowerStatusProvider
std::string to_string(bool Connected) const override
if (Connected)
return "Device connected";
else
return "Device not connected";
;
struct OutputHandler
OutputHandler(std::vector<OutputDevice_sp> Outputs)
: mOutputs(Outputs)
void handle(std::string const & output) const
for (auto &&output : mOutputs)
output->publish(output);
private:
std::vector<OutputDevice_sp> mOutputs;
;
using PowerStatusProvider_sp = std::shared_ptr<PowerStatusProvider>;
using OutputHandler_sp = std::shared_ptr<OutputHandler>;
struct Device
struct Device(std::string Name, PowerStatusProvider_sp PSP, OutputHandler_sp OH)
: mName(Name)
, mPSP(PSP)
, mOH(OH)
void update_status() const
mOH->handle(mPSP->to_string());
private:
std::string mName;
std::shared_ptr<PowerStatusProvider> mPSP;
std::shared_ptr<OutputHandler> mOH;
;
【讨论】:
以上是关于面向对象的设计来模拟通用设备的主要内容,如果未能解决你的问题,请参考以下文章
前端 JavaScript 设计模式前奏--面向对象-Class类
前端 JavaScript 设计模式前奏--面向对象-Class类