如果在某些条件下类无法运行,如何禁用类API?

时间:2015-02-23 07:54:15

标签: c++ class c++11 factory

说我有以下内容:

class Processor
{
public:
    void Activate();
    /* some more interface functions*/
};

void main()
{
    Processor().Activate();
}

class Processor只是提供公共接口的任何类的示例。

问题

如果满足某些条件,如果课程Processor 可操作,该怎么办?例如,类Processor正在目录X上执行某些文件系统操作,如果目录X不存在,则根本无法运行。

问题

谁负责验证条件是否符合并且课程正在运作?


让我们将这些条件的评估封装到一个名为Enabled()

的逻辑函数中

建议1 - 来电者责任

void main()
{
    if (Enabled() )
        Processor().Activate();
}

在这种情况下,类Processor的发起者负责确保在启动课程之前满足这些条件。

缺点

  1. 来电者可能不知道条件是什么
  2. 这不能解决更大的问题,如果我们有其他来电者没有验证条件怎么办?

  3. 建议2 - 课堂责任

    class Processor
    {
    public:
    
        Processor()
        {
            // init m_bIsEnabled based on conditions
        }
    
        void Activate()
        {
            if (!m_bIsEnabled)
                return;
    
            // do something
        }
        /* some more interface functions*/
    private:
        bool m_bIsEnabled;
    };
    

    在这种情况下,如果未启用类,则禁用所有公共接口函数。

    缺点

    1. 如果类Processor具有众多接口函数,我们在每个函数的开头检查m_bIsEnabled的值是什么?
    2. 如果将来某些开发人员增强了界面并忘记检查m_bIsEnabled的值,该怎么办?
    3. 案例m_bIsEnabled == false
    4. 中每个函数返回的默认值是什么

      建议3 - 工厂

      using Processor_ptr = shared_ptr < Processor > ;
      
      class ProcessorFactory
      {
          static Processor_ptr create()
          {
              Processor_ptr p;
      
              p.reset(new Processor);
              if (!p->IsEnabled())
                  p.reset(nullptr);
      
              return p;
          }
      };
      
      
      class Processor
      {
      public:
          void Activate();
          bool IsEnabled();
          friend class ProcessorFactory;
      private:
          Processor();
          bool m_bIsEnabled;
      };
      

      这种方法是我最喜欢的方法,因为如果无法操作,我们会阻止类生成。

      缺点  1.也许是矫枉过正?


      问题

      哪种建议在最佳做法方面更可取?我们有其他建议吗?

3 个答案:

答案 0 :(得分:4)

我会选择#3选项,作为选项#2&amp; #1很难跟踪并强制开发人员在每个函数执行之前始终验证Active标志。此外,您可以通过返回一个实现接口但实际上什么都不做的空对象来扩展选项#3。

答案 1 :(得分:1)

我也是#3喜欢igalk的粉丝。但值得一提的是,这是RAII模式的典型情况(资源获取是初始化)。 在构造函数中未满足实例创建条件时抛出异常:

class Processor
{
public:    
    Processor()
    {
        //  based on conditions
        if(!folderexist)  // pseudocode
          throw(cannotcreate)
    }

//    void Activate() not needed anymore

    /* some more interface functions*/
private:
//    bool m_bIsEnabled; not needed anymore
};

这是已经使用异常的库中的常见模式。只要它们以正确的方式使用,我自己就没有异常问题。不幸的是,我经常看到用作longjumps的异常或保存一些代码行的快捷方式。 失败可以是实例的有效状态。在这种情况下,恕我直言,最好创建一个实例,并且有一个&#34;有效的&#34;旗帜(案例#2)。但大多数时候,创建的对象毫无价值,失败的信息只对创作者有意义。在这种情况下,RAII可能是更好的选择。如果您不想要或不能使用异常,工厂模式会以优雅的方式避免异常。

答案 2 :(得分:0)

实际上,可以采用任何或所有这些策略,具体取决于能够检测到问题的代码。

但一般情况下,我不鼓励使用&#34;启用&#34;或&#34;有效状态&#34;在对象中的标志。如果检测到问题,尽管存在问题,这样的标志仍然是继续的机会(例如忘记检查标志,忘记设置标志,不恰当地忽略标志的值)。程序员是人,而且随着代码变得越来越复杂,制造一个很难追踪的错误变得更容易。

如果调用者检测到问题,那么它不应该创建对象或(如果对象已经存在)它应该销毁该对象。然后它可以向呼叫者表明问题。

如果对象本身检测到问题,则应尽可能恢复,并向呼叫者发出指示。

无论哪种方式,这意味着,如果对象存在,它将保持有效状态,直到检测到问题,并且检测到问题将导致对象不存在。

需要给出的指示取决于问题的严重程度,但包括错误代码(调用者可以忽略)和异常(必须处理,并且如果程序需要更正问题)之间的各种方法是继续)。

BTW:main()返回int,而不是void