输入在技术上有效但不满意时,异常与空结果集


108

我正在开发要公开发布的库。它包含用于对对象集进行操作的各种方法-生成,检查,分区并将这些集投影到新形式。如果相关,它是一个C#类库,其中包含LINQ样式的扩展IEnumerable,将作为NuGet软件包发布。

该库中的某些方法可能会被赋予无法满足的输入参数。例如,在组合方法中,存在一种生成可以从m个项目的源集合构造的n个项目的所有集合的方法。例如,给定集合:

1,2,3,4,5

并要求2的组合将产生:

1,2
1,3
1,4
等...
5,3
5,4

现在,显然可以要求做一些无法完成的事情,例如给它提供3个项目的集合,然后要求4个项目的组合,同时设置说只能使用每个项目一次的选项。

在这种情况下,每个参数都是单独有效的:

  • 源集合不为null,并且包含项
  • 要求的组合大小为正非零整数
  • 请求的模式(仅使用每个项目一次)是有效选择

但是,将这些参数的状态放在一起时会引起问题。

在这种情况下,您是否希望该方法引发异常(例如InvalidOperationException)或返回空集合?在我看来,任何一种都有效:

  • 如果只允许每个项目使用一次,则不能从m个项目的集合(其中n> m个)中产生n个项目的组合,因此该操作被认为是不可能的。InvalidOperationException
  • n> m时可以从m个项目中生成的大小为n的组合的集合为空集;无法产生任何组合。

空集的参数

我首先担心的是,当您处理大小可能未知的数据集时,异常会阻止惯用的LINQ样式方法链接。换句话说,您可能想要执行以下操作:

var result = someInputSet
    .CombinationsOf(4, CombinationsGenerationMode.Distinct)
    .Select(combo => /* do some operation to a combination */)
    .ToList();

如果您的输入集大小可变,则此代码的行为是不可预测的。如果少于4个元素.CombinationsOf()时引发异常someInputSet,则此代码有时会在运行时失败,而无需进行一些预检查。在上面的示例中,此检查是微不足道的,但是如果您在较长的LINQ链中途调用它,则可能会变得乏味。如果它返回一个空集,result则将为空,您可能会很满意。

例外的论点

我的第二个担心是,返回一个空集可能会隐藏问题-如果您在LINQ链的中途调用此方法,并且它悄悄地返回一个空集,那么稍后可能会遇到问题,或者发现自己为空结果集,鉴于您肯定在输入集中有某些内容,这种情况的发生可能并不明显。

您会期望什么,您对此有何看法?


66
由于空集在数学上是正确的,因此很可能在您获得空集时就是您想要的。通常选择数学定义和约定是为了保持一致性和方便性,以便使事情顺利进行。
asmeurer

5
@asmeurer选择它们是为了使定理一致且方便。并未选择它们来简化编程。(这有时是一个附带好处,但有时它们也会使编程变得更困难。)
jpmc26 2016年

7
@ jpmc26“选择它们是为了使定理一致且方便”-确保您的程序始终按预期运行,实质上等于证明了一个定理。
artem

2
@ jpmc26我不明白为什么您提到函数式编程。证明命令式程序的正确性是很有可能的,始终是有利的,并且也可以非正式地完成-编写程序时只需三思而后行,并使用常识即可,并且可以减少测试时间。在一个样本中得到统计证明;-)
artem

5
@DmitryGrigoryev 1/0(未定义)在数学上比1/0(无穷大)更正确。
asmeurer '16

Answers:


145

返回空集

我期望有一个空集,因为:

当我只能使用每个数字一次时,存在3个集合中的4个数字的0个组合


5
从数学上讲,是的,但这也很可能是错误的根源。如果期望这种类型的输入,最好要求用户在通用库中捕获异常。
Casey Kuball

56
我不同意这是“极有可能”导致错误的原因。例如,假设您从一个大型输入集中实现了一个幼稚的“ matchmaknig”算法。您可能需要两个项目的所有组合,找到其中一个“最佳”匹配项,然后删除这两个元素并从新的较小的集合开始。最终,您的集合将为空,并且不再需要考虑任何对:此时的异常是阻塞性的。我认为有很多方法可以结束类似的情况。
amalloy '16

11
实际上,您不知道这是否是用户眼中的错误。如有必要,用户应检查空集。if(result.Any())DoSomething(result.First()); 其他DoSomethingElse(); 大大优于尝试;抓{DoSomethingElse();} {result.first()DoSomething的()}
Guran

4
@TripeHound最终决定了这一点:就开发工作量,程序性能和开发效率而言,要求开发人员使用此方法进行检查然后将其引发,比引发他们不希望的异常要小得多。程序流程简单。
anaximander '16

6
@Darthfett尝试将其与现有的LINQ扩展方法进行比较:Where()。如果我有一个子句:Where(x => 1 == 2)我没有收到异常,我得到了一个空集。
Necoras '16

79

如有疑问,请问其他人。

您的示例函数在Python中有一个非常相似的函数:itertools.combinations。让我们看看它是如何工作的:

>>> import itertools
>>> input = [1, 2, 3, 4, 5]
>>> list(itertools.combinations(input, 2))
[(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]
>>> list(itertools.combinations(input, 5))
[(1, 2, 3, 4, 5)]
>>> list(itertools.combinations(input, 6))
[]

对我来说感觉很好。我期待可以迭代的结果,但我得到了一个。

但是,显然,如果您要问一些愚蠢的事情:

>>> list(itertools.combinations(input, -1))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: r must be non-negative

因此,我想说的是,如果所有参数都经过验证,但结果是一个空集,则返回一个空集,那么您并不是唯一一个这样做的人。


就像@Bakuriu在评论中所说的那样,对于SQL类似的查询也是如此SELECT <columns> FROM <table> WHERE <conditions>。只要<columns><table><conditions>都很好地形成形成,并参考现有的名称,你可以建立一组互相排斥的条件。结果查询将不产生任何行,而不会引发InvalidConditionsError


4
拒绝投票,因为它在C#/ Linq语言空间中不是惯用语言(请参见sbecker的答案,以了解该语言如何处理类似的越界问题)。如果这是一个Python问题,那对我来说将是+1。
詹姆斯·斯内尔

32
@JamesSnell我几乎看不到它与越界问题的关系。我们不是按索引来选择元素以对其进行重新排序,而是要选择n集合中的元素来计数选择元素的方式。如果在某个时刻选择元素时没有剩余元素,那么就没有(0)种方式n从所述集合中选择元素。
Mathias Ettinger

1
不过,对于C#问题,Python并不是一个好兆头:例如C#允许1.0 / 0,Python不允许。
德米特里·格里戈里耶夫

2
@MathiasEttinger Python关于如何以及何时使用异常的指南与C#完全不同,因此将Python模块的行为用作指南对于C#而言不是一个好的指标。
Ant P

2
除了选择Python,我们还可以选择SQL。当结果集为空时,对表进行格式正确的 SELECT查询是否会产生异常?(剧透:不!)。查询的“格式正确”仅取决于查询本身和一些元数据(例如,表是否存在并具有此字段(具有此类型)?)查询格式正确并执行后,您会期望(可能为空)有效结果或异常,因为“服务器”有问题(例如db服务器故障或其他原因)。
巴库里

72

用外行的话来说:

  • 如果有错误,则应引发异常。这可能涉及分步执行操作,而不是在单个链接调用中进行操作,以便确切地知道错误发生的位置。
  • 如果没有错误,但是结果集为空,则不引发异常,返回空集。空集是有效集。

23
+1、0是一个完全有效且确实非常重要的数字。在许多情况下,查询可能会合法地返回零命中,因此引发异常会非常烦人。
凯莉安·弗斯

19
好的建议,但是您的答案清楚吗?
伊万

54
对于这个答案是否暗示OP应该throw还是应该返回一个空集的问题,我仍然没有一个明智的选择
James Snell

7
您的回答说我可以执行任何操作,具体取决于是否存在错误,但是您没有提及是否将示例场景视为错误。在上面的注释中,您说“如果没有问题”,我应该返回一个空集,但是同样,不满足条件可以被认为是一个问题,并且您继续说,我不应该在应该提出异常的情况下提出。那我该怎么办?
anaximander '16

3
啊,我明白了-您可能误会了;我没有链接任何东西,我正在写一个我希望在链接中使用的方法。我想知道假设使用此方法的未来开发人员是否希望在上述情况下抛出该错误。
anaximander

53

我同意Ewan的回答,但想添加一个具体的理由。

您正在处理数学运算,因此坚持使用相同的数学定义可能是一个不错的建议。从数学的角度的数量ř一个的-Sets Ñ -set(即nCr的)的所有r被良好限定> N> = 0的零。因此,从数学的角度来看,返回空集将是预期的情况。


9
这是个好的观点。如果库是更高级别的库,例如选择颜色组合以制成调色板,则抛出错误是有意义的。因为您知道没有颜色的调色板就不是调色板。但是没有条目的集合仍然是一个集合,数学确实将其定义为等于空集合。
曼队长

1
比伊万斯的答案好得多,因为它引用了数学实践。+1
user949300 '16

24

我发现一种确定是否使用异常的好方法,就是想象人们正在参与交易。

以获取文件内容为例:

  1. 请获取文件“不存在.txt”的内容

    一种。“这里是内容:空字符集”

    b。“ Erm,有一个问题,该文件不存在。我不知道该怎么办!”

  2. 请获取文件内容“存在但为空.txt”

    一种。“这里是内容:空字符集”

    b。“ Erm,有问题,该文件中没有任何内容。我不知道该怎么办!”

毫无疑问,有些人会不同意,但是对于大多数人来说,“ Erm,有问题”在文件不存在时有意义,而在文件为空时返回“空字符集”。

因此,将相同的方法应用于您的示例:

  1. 请给我所有4件物品的所有组合 {1, 2, 3}

    一种。没有,这里是空的。

    b。有一个问题,我不知道该怎么办。

同样,如果将例如null作为一组商品提供,那么“有问题”将是有意义的,但是“上述为空”似乎是对上述要求的明智回应。

如果返回空值掩盖了问题(例如,文件丢失,a null),则通常应改用异常(除非您选择的语言支持option/maybe类型,否则它们有时更有意义)。否则,返回一个空值可能会简化成本,并且更符合最小惊讶原则。


4
这个建议是好的,但不适用于这种情况。一个更好的例子:1月30日之后几日?:1 2月30日之后几日?:0三十日之后几日?:例外30号工作了多少小时:2月3日:例外
古兰经

9

因为它是针对通用库的,所以我的直觉是让最终用户选择。

就像我们已经拥有Parse()TryParse()可以使用的一样,我们可以根据函数需要的输出选择使用哪个选项。与争论选择单一版本的函数相比,花费更少的时间编写和维护函数包装程序以引发异常。


3
+1是因为我一直喜欢选择的想法,并且因为这种模式倾向于鼓励程序员验证自己的输入。仅仅存在TryFoo函数就很明显,某些输入组合会导致问题,如果文档解释了这些潜在问题,编码人员可以通过捕获异常来检查它们并以更清洁的方式处理无效输入。或响应一个空集。或者他们可以偷懒。无论哪种方式,决定都是他们的。
aleppke '16

19
不,空集是数学上正确的答案。做其他事情正在重新定义公认的数学,这不应该一时兴起。在处理过程中,如果指数等于零,我们是否应该重新定义指数函数以引发错误,因为我们决定不喜欢任何将数字升至“零”次幂等于1的约定?
通配符

1
我正要回答类似的问题。LINQ的扩展方法Single(),并SingleOrDefault()立即涌现在脑海中。如果结果为零或> 1,则Single会引发异常,而SingleOrDefault不会引发异常,而是返回default(T)。也许OP可以使用CombinationsOf()CombinationsOfOrThrow()
RubberDuck

1
@Wildcard-您正在谈论同一输入的两个不同结果,这不一样。OP仅对选择a)结果或b)抛出不是结果的异常感兴趣。
詹姆斯·斯内尔

4
SingleSingleOrDefault(或FirstFirstOrDefault)是一个完全不同的故事@RubberDuck。从我以前的评论中拿起我的例子。这是完全罚款豆没有到查询“什么即使素数大于二存在吗?” ,因为这是数学上合理且明智的答案。无论如何,如果您问“大于2的第一个偶数是什么?” 没有自然的答案。您只是无法回答问题,因为您要输入一个数字。它不是没有(不是一个数字,而是一个集合),而不是0。因此,我们抛出异常。
Paul Kertscher '16

4

您需要验证调用函数时提供的参数。实际上,您想知道如何处理无效的参数。多个参数相互依赖的事实并不能弥补您验证参数的事实。

因此,我会投票支持ArgumentException,该异常为用户提供了必要的信息以了解发生了什么问题。

例如,检查public static TSource ElementAt<TSource>(this IEnumerable<TSource>, Int32)Linq中的 功能。如果索引小于0或大于或等于source中的元素数,则抛出ArgumentOutOfRangeException。因此,就调用方提供的可枚举而言,对索引进行了验证。


3
+1表示语言中类似情况的示例。
詹姆斯·斯内尔

13
奇怪的是,您的榜样让我开始思考,并使我想到了一个强有力的反例。如前所述,该方法被设计为类似于LINQ,以便用户可以将其与其他LINQ方法链接在一起。如果您new[] { 1, 2, 3 }.Skip(4).ToList();得到一个空集,这让我认为返回空集也许是更好的选择。
anaximander '16

14
这不是语言语义问题,问题域的语义已经提供了正确的答案,即返回空集。对于在组合问题领域工作的人,做任何其他事情都违反了使您感到惊讶的原则。
Ukko

1
我开始理解用于返回空集的参数。为什么会有意义。至少对于此特定示例。
sbecker '16

2
@sbecker,这很重要:如果问题是“ ...有多少个组合”,那么“零”将是一个完全有效的答案。同样的道理,“什么组合这样......”有空集作为一个完全有效的答案。如果问题是“ …… 的第一个组合是什么?”,则只有在那时才例外,因为该问题无法回答。另请参阅Paul K的评论
通配符

3

您应该执行以下操作之一(尽管会不断出现基本问题,例如组合数为负):

  1. 提供两种实现,一种实现在输入加起来不合理时返回空集,另一种抛出。尝试调用它们CombinationsOfCombinationsOfWithInputCheck。或任何你喜欢的。您可以反转此名称,因此输入检查的一个是较短的名称,列表的一个是CombinationsOfAllowInconsistentParameters

  2. 对于Linq方法,请IEnumerable在您概述的确切前提下返回空值。然后,将这些Linq方法添加到您的库中:

    public static class EnumerableExtensions {
       public static IEnumerable<T> ThrowIfEmpty<T>(this IEnumerable<T> input) {
          return input.IfEmpty<T>(() => {
             throw new InvalidOperationException("An enumerable was unexpectedly empty");
          });
       }
    
       public static IEnumerable<T> IfEmpty<T>(
          this IEnumerable<T> input,
          Action callbackIfEmpty
       ) {
          var enumerator = input.GetEnumerator();
          if (!enumerator.MoveNext()) {
             // Safe because if this throws, we'll never run the return statement below
             callbackIfEmpty();
          }
          return EnumeratePrimedEnumerator(enumerator);
       }
    
       private static IEnumerable<T> EnumeratePrimedEnumerator<T>(
          IEnumerator<T> primedEnumerator
       ) {
          yield return primedEnumerator.Current;
          while (primedEnumerator.MoveNext()) {
             yield return primedEnumerator.Current;
          }
       }
    }

    最后,像这样使用:

    var result = someInputSet
       .CombinationsOf(4, CombinationsGenerationMode.Distinct)
       .ThrowIfEmpty()
       .Select(combo => /* do some operation to a combination */)
       .ToList();

    或像这样:

    var result = someInputSet
       .CombinationsOf(4, CombinationsGenerationMode.Distinct)
       .IfEmpty(() => _log.Warning(
          $@"Unexpectedly received no results when creating combinations for {
             nameof(someInputSet)}"
       ))
       .Select(combo => /* do some operation to a combination */)
       .ToList();

    请注意,在创建linq链时(而不是在枚举它之后的某个时间),发生投掷或操作行为时,需要使用不同于公共方法的私有方法。您希望它立即抛出。

    但是请注意,当然必须至少枚举第一项才能确定是否有任何项。这是我认为主要是由一个事实,即任何未来观众可以很容易有理由相信减轻潜在的缺点ThrowIfEmpty方法枚举至少一个项目,所以应该不会被它这样做感到惊讶。但是你永远不知道。您可以使它更明确ThrowIfEmptyByEnumeratingAndReEmittingFirstItem。但这似乎是巨大的杀伤力。

我认为#2相当不错!现在,调用代码具有强大的功能,下一个阅读该代码的人将完全了解它在做什么,并且不必处理意外的异常。


请解释你的意思。我的帖子中的内容“不像集合”如何表现?为什么这是一件坏事?
ErikE

您基本上与我的回答完全相同,而不是包装您遇到的用于查询If条件的查询,结果不会更改uu
GameDeveloper

我看不出我的答案与您的答案“基本相同”。在我看来,它们似乎完全不同。
ErikE '16

基本上,您只是在执行与“我的坏班级”相同的条件。但是您并没有告诉您^^。您是否同意,是否要强制查询结果中存在1个元素?
GameDeveloper 2016年

您将不得不为显然愚蠢的程序员说得更清楚,他们仍然不了解您在说什么。什么班不好?为什么不好?我什么都不执行。我允许类的用户来决定最终的行为,而不是类的创建者。这样一来,人们就可以使用一致的编码范例,其中Linq方法不会因为计算方面而实际上不会违反正常规则(例如,一次要-1个项目),因此不会随机抛出。
ErikE

2

我可以看到两个用例的参数-如果下游代码期望包含数据的集合,则异常是一个很好的选择。另一方面,如果可以预料的话,那么简单地使用一个空集就可以了。

我认为这是错误还是可接受的结果,取决于呼叫者的期望-因此,我会将选择权转移给呼叫者。也许介绍一个选择?

.CombinationsOf(4, CombinationsGenerationMode.Distinct, Options.AllowEmptySets)


10
当我了解到您要执行的操作时,我会尽量避免使用传入大量选项的方法来修改其行为。我对已经存在的模式枚举还不是100%满意;我真的不喜欢使用选项参数来更改方法的异常行为的想法。我觉得如果一个方法需要抛出异常,它就需要抛出;如果您想隐藏该异常,那是您决定调用代码,而不是某些事情,那么可以使用正确的输入选项来使我的代码起作用。
anaximander '16

如果调用者期望一个不为空的集合,则由调用者决定是处理一个空集合还是引发异常。就被呼叫者而言,空集是一个很好的答案。而且,您可以拥有多个呼叫者,对空集是否合适有不同的看法。
gnasher729

2

有两种方法可以确定是否没有明显的答案:

  • 编写代码时首先选择一个选项,然后再选择另一个。考虑哪一个在实践中最有效。

  • 添加“ strict”布尔参数以指示是否要严格验证参数。例如,Java SimpleDateFormat有一种setLenient方法可以尝试解析与格式不完全匹配的输入。当然,您必须决定默认值是什么。


2

根据您自己的分析,返回空集似乎很正确-您甚至已经将其标识为某些用户可能真正想要的东西,并且没有陷入禁止使用某些东西的陷阱,因为您无法想象用户曾经想要使用它那样。

如果您确实感觉到某些用户可能想强制非空返回,请给他们一种要求该行为的方法,而不是强加给所有人。例如,您可能会:

  • 使其成为对用户执行操作的任何对象的配置选项。
  • 将其设置为标志,用户可以选择将其传递给该函数。
  • 提供AssertNonempty他们可以放入连锁店的支票。
  • 做两个函数,一个断言非空,另一个不断言。

这似乎并没有提供任何对之前12个答案中提出和解释的观点的实质性建议
gnat

1

这实际上取决于您的用户期望获得什么。对于(某种程度上不相关的)示例,如果您的代码执行除法运算,则可能抛出异常或返回,Inf或者NaN被零除。然而,对与错都不是:

  • 如果您返回InfPython库,人们会因隐藏错误而袭击您
  • 如果您在Matlab库中引发错误,人们会因无法处理缺少值的数据而向您发起攻击

在您的情况下,我会选择对于最终用户来说最不令人惊讶的解决方案。由于您正在开发处理集合的库,因此,空集合似乎是用户希望处理的事情,因此返回它听起来很明智。但是我可能会弄错了:您对上下文的理解比这里的其他任何人都更好,因此,如果您希望用户依赖始终不为空的集合,则应立即抛出异常。

允许用户选择的解决方案(例如添加“ strict”参数)不是确定的,因为它们用新的等效问题替换了原始问题:“哪个值strict应为默认值?”


2
我考虑过在答案中使用NaN参数,并同意您的观点。在不知道OP领域的情况下,我们无法提供更多信息
GameDeveloper 2016年

0

在数学中,通常的概念是:在集合中选择元素时,您找不到任何元素,因此会得到一个空集。当然,如果您采用这种方式,则必须与数学保持一致:

通用设置规则:

  • Set.Foreach(谓词); //对于空集始终返回true
  • Set.Exists(谓词); //对于空集总是返回false

您的问题非常微妙:

  • 可能是函数的输入必须遵守合同在这种情况下,任何无效的输入都应引发异常,就是这样,函数无法在常规参数下工作。

  • 函数的输入可能必须完全像set一样,因此应该能够返回一个空set。

现在,如果我在您的位置,我将采用“设置”方式,但带有一个较大的“ BUT”。

假设您有一个集合,“按假设”应该只有女学生:

class FemaleClass{

    FemaleStudent GetAnyFemale(){
        var femaleSet= mySet.Select( x=> x.IsFemale());
        if(femaleSet.IsEmpty())
            throw new Exception("No female students");
        else
            return femaleSet.Any();
    }
}

现在,您的收藏不再是“纯粹的收藏”,因为您已经有了合同,因此应该例外地执行合同。

当以纯方式使用“集合”函数时,如果集合为空,则不应抛出异常,但是如果集合不再是“纯集合”,则应在适当的地方抛出异常

您应该始终做更自然,更一致的事情:对我而言,一套应该坚持一套规则,而非一套东西应该具有正确的思维规则。

在您的情况下,这样做似乎是一个好主意:

List SomeMethod( Set someInputSet){
    var result = someInputSet
        .CombinationsOf(4, CombinationsGenerationMode.Distinct)
        .Select(combo => /* do some operation to a combination */)
        .ToList();

    // the only information here is that set is empty => there are no combinations

    // BEWARE! if 0 here it may be invalid input, but also a empty set
    if(result.Count == 0)  //Add: "&&someInputSet.NotEmpty()"

    // we go a step further, our API require combinations, so
    // this method cannot satisfy the API request, then we throw.
         throw new Exception("you requsted impossible combinations");

    return result;
}

但这不是一个好主意,我们现在有一个无效状态,可以在运行时随机出现,但是隐式存在于问题中,因此我们无法删除它,请确保可以将异常移至某些实用程序方法中(这正是相同的代码,在不同的地方移动),但这是错误的,基本上,您可以做的最好的事情就是坚持常规的设置规则

实际上增加新的复杂性只是为了表明您可以编写linq查询方法似乎不值得解决您的问题,我很确定,如果OP可以告诉我们更多关于它的领域的信息,那么也许我们可以找到真正需要例外的地方(如果完全有可能,这个问题根本不需要任何例外。


问题是您正在使用数学定义的对象来解决问题,但是问题本身可能不需要数学定义的对象作为答案(实际上是在这里返回列表)。不管您如何解决,这都取决于更高级别的问题,这称为封装/信息隐藏。
GameDeveloper '16

您的“ SomeMethod”不再表现为集合的原因是,您正在“从集合中”询问一条信息,特别是查看注释部分“ && someInputSet.NotEmpty()”
GameDeveloper

1
没有!你不应该在你的女班上丢一个例外。您应该使用Builder模式,该模式强制您甚至不能获取的实例,FemaleClass除非它只有女性(它不是公开创建的,只有Builder类可以分发一个实例),并且其中可能至少有一个女性。这样一来,FemaleClass作为对象的代码就不必处理异常了,因为对象处于错误状态。
ErikE '16

您假设类是如此简单,以至于实际上您可以仅通过构造函数来强制执行其先决条件。你的论点是有缺陷的。如果您禁止再没有女性,那么或者您不能有一种方法来删除班上的学生,或者当剩下一名学生时,您的方法必须抛出异常。您只是将我的问题转移到其他地方。关键是总会有一些先决条件/功能状态无法“通过设计”来维护,并且用户将要中断:这就是为什么存在异常!这是很理论的东西,我希望那票不是你的。
GameDeveloper 2016年

反对票不是我的,但如果是,我将为此感到自豪。我认真投票,但坚信不误。如果您的类的规则是必须在其中包含一个项目,并且所有项目都必须满足特定条件,那么让该类处于不一致状态是一个坏主意。将问题转移到其他地方正是我的意图!我希望问题在构建时而不是在使用时发生。使用Builder类而不是使用构造函数引发的问题是,您可以通过不一致在很多部分中建立非常复杂的状态。
ErikE
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.