面向对象的设计来模拟通用设备

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 更改为 vector ,那么面试也不会对使用 vector 感到满意。 我们不知道究竟是什么让面试官不高兴,但std::vector&lt;Output&gt; 肯定是错误的,std::vector&lt;Output*&gt; 是好的。我正在考虑将问题作为重复问题关闭,但已经有一个答案可以帮助我更好地解释,所以我放弃了链接。 我将“vector 输出”更改为“vector 输出”以澄清混淆。面试官建议使用一些设计模式而不是矢量。 旁注:print(string) 应该是 const。在我看来,问题在于你没有与面试官沟通:“你想到了哪种模式?”或“应该针对vector 的哪个特定问题?”。我个人不赞成您使用vector 的理由。为什么不使用非类型模板参数std::size_t kOutputCountDevice&lt;1ULL&gt; 的模板特化来创建Device 模板类?此外,this-&gt;outputsvectorOutput* 不是 Output... 哦。那个构造函数是private 【参考方案1】:

vector&lt;Output&gt; 不允许继承,因为它直接存储 Outputs 而不是指针或引用。如果您将DisplaySpeaker 存储在向量中,它将是sliced。

由于每个设备的输出都是唯一的,我会将其设为唯一指针的向量:

std::vector<std::unique_ptr<Output>> outputs;

【讨论】:

如果我将 vector 更改为 vector 那么面试也不满意使用 vector。 @Ranju 你现在在接受采访吗?【参考方案2】:

我不认为面试官要求使用某种设计模式有任何建设性。设计模式是完成某事的工具;没有目标,设计模式就毫无意义。相反,他本可以说:“我们预计必须以最小的偏差构建许多类似的设备。您将如何实现这一目标?” 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类

前端 JavaScript 设计模式前奏--面向对象-Class类

面向对象设计与构造第二次总结作业

面向对象

类和面向对象