如果可以避免,请勿使用任何一种。使用工厂函数返回选项类型,元组或专用求和类型。换句话说,用不同于保证默认值的类型表示可能默认值。
首先,让我们列出所需的内容,然后考虑如何用几种语言(C ++,OCaml,Python)表达它
- 在编译时捕获默认对象的不必要使用。
- 在读取代码时,可以很明显地看出给定值是否可能是默认值。
- 选择每种类型的明智的默认值(如果有)。绝对不要在每个呼叫站点都选择一个可能不同的默认值。
- 使静态分析工具或人类易于
grep
寻找潜在的错误。
- 对于某些应用程序,如果意外给定默认值,则程序应正常运行。对于其他应用程序,如果给出默认值,则程序应立即停止运行,最好是提供信息性的方式。
我认为Null对象模式和null指针之间的关系来自(5)。但是,如果我们能够尽早发现错误,那么(5)就没有意义了。
让我们按语言来考虑这种语言:
C ++
在我看来,C ++类通常应该是默认可构造的,因为它简化了与库的交互,并使在容器中使用该类更加容易。由于您无需考虑要调用哪个超类构造函数,因此它还简化了继承。
但是,这意味着您无法确定类型的值是否MyClass
处于“默认状态”。除了bool nonempty
在运行时放入一个或类似字段以显示默认值外,您所能做的最好的事情就是以强迫用户对其进行检查的方式生成的新实例MyClass
。
我建议使用工厂函数返回一个std::optional<MyClass>
,std::pair<bool, MyClass>
或R值参照std::unique_ptr<MyClass>
如果可能的话。
如果要让工厂函数返回某种不同于默认构造的“占位符”状态MyClass
,请使用std::pair
并确保将其记录为文档。
如果factory函数具有唯一的名称,则很容易grep
使用该名称并查找草率。但是,grep
对于程序员本应该使用工厂功能但没有使用工厂功能的情况来说,这很难。
OCaml
如果使用OCaml之类的语言,则可以只使用一种option
类型(在OCaml标准库中)或一种either
类型(不在标准库中,但可以轻松滚动自己的类型)。或一种defaultable
类型(我是用术语组成的defaultable
)。
type 'a option =
| None
| Some of 'a
type ('a, 'b) either =
| Left of 'a
| Right of 'b
type 'a defaultable =
| Default of 'a
| Real of 'a
defaultable
上面显示的A 比一对更好,因为用户必须进行模式匹配以提取,'a
并且不能简单地忽略该对的第一个元素。
defaultable
可以在std::variant
具有两个相同类型实例的C ++中使用与上面所示类型等效的类型,但是std::variant
如果两个类型相同,则部分API无法使用。std::variant
由于它的类型构造函数是未命名的,所以这也是一个奇怪的用法。
蟒蛇
无论如何,您都没有针对Python进行任何编译时检查。但是,由于是动态类型的,因此通常在任何情况下都不需要某种类型的占位符实例来放置编译器。
当您被迫创建默认实例时,我建议您只抛出一个异常。
如果那是不可接受的,我建议创建一个从工厂函数DefaultedMyClass
继承MyClass
并从工厂函数返回的。如果需要的话,这可以为您提供默认情况下“残存”功能方面的灵活性。