【问题标题】: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

标签: c++ oop design-patterns object-oriented-analysis system-design


【解决方案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;
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 1970-01-01
      • 2016-01-19
      • 1970-01-01
      相关资源
      最近更新 更多