我正在开发要公开发布的库。它包含用于对对象集进行操作的各种方法-生成,检查,分区并将这些集投影到新形式。如果需要的话,它是一个C#类库,在IEnumerable上包含LINQ样式的扩展,将以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个项目的组合,其中n> m该操作被认为是不可能的,因此InvalidOperationException
当n> m为空集时,可以从m个项目中生成大小为n的组合的集合;无法产生任何组合。

空集的参数

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

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


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

关于异常的参数

我的第二个担心是返回空set可能会隐藏问题-如果您在LINQ链的中途调用此方法,并且它悄悄地返回一个空集,那么稍后可能会遇到问题,或者发现自己有一个空结果集,而可能不会很明显,假设您在输入集中确实有一些东西,这是怎么发生的。

您希望得到什么,您对此有何看法?

评论

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

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

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

@ jpmc26我不明白为什么您提到函数式编程。证明命令式程序的正确性是很有可能的,始终是有利的,并且也可以非正式地完成-在编写程序时只需三思而后行并使用常识即可,这样您就可以减少测试时间。经对一个;-)样本的统计证明
@DmitryGrigoryev未定义的1/0比1/0的无穷大在数学上更正确。

#1 楼

返回空集

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

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

评论


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

–Casey Kuball
16年11月30日在16:24

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

–合金
16-11-30在17:09



实际上,您不知道这是否是用户眼中的错误。如有必要,用户应检查空集。 if(result.Any())DoSomething(result.First());其他DoSomethingElse();比try {result.first()。dosomething();} catch {DoSomethingElse();}更好

–古兰经
16年1月1日在9:25



@TripeHound最终解决了这个问题:就开发工作量,程序性能和开发效率而言,要求开发人员使用此方法进行检查然后抛出,所产生的影响要比抛出他们不希望的异常产生的影响小得多。程序流程简单。

–anaximander
16年1月1日在14:12

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

– Necoras
16年1月1日在18:25

#2 楼

如有疑问,请问其他人。

您的示例函数在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>

评论


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

–詹姆斯·斯内尔(James Snell)
16年11月30日在14:15

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

–301_Moved_Permanently
16-11-30在14:27

不过,对于C#问题,Python仍然不是一个好兆头:例如C#允许1.0 / 0,Python不允许。

–德米特里·格里戈里耶夫(Dmitry Grigoryev)
16年1月1日在11:02

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

– Ant P
16 Dec 1'在13:26

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

–巴库留
16 Dec 5'在8:56

#3 楼

用外行的话来说:


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


评论


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

–基连·福斯(Kilian Foth)
16年11月30日在13:07

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

–伊万
16-11-30在13:51

对于这个答案是否暗示OP应该抛出或返回一个空集的问题,我仍然没有明智之举。

–詹姆斯·斯内尔(James Snell)
16年11月30日在14:19

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

–anaximander
16-11-30在14:21

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

–anaximander
16年11月30日在16:03

#4 楼

我同意Ewan的回答,但想添加一个特定的推理。

您正在处理数学运算,因此坚持使用相同的数学定义可能是一个不错的建议。从数学的角度来看,对于所有r> n> = 0,都很好地定义了n个集合的r个集合(即nCr)的数量。它为零。因此,从数学的角度来看,返回空集将是预期的情况。

评论


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

–曼上尉
16年1月1日在16:23

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

–user949300
16 Dec 3'在4:35

#5 楼

我发现确定是否使用异常的一种好方法是,想象有人参与事务。

以获取文件内容为例:



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

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

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


请获取文件的内容,“存在,但为空。 txt“

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

b。 “嗯,有个问题,这个文件里什么都没有。我不知道该怎么办!”


因此对您的示例应用相同的方法:



请给我{1, 2, 3}

a的4个项目的所有组合。没有,这是一个空集。

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


同样,如果将null作为一组商品提供,则“有一个问题”是有意义的,但是“这是一个空集”似乎是对上述请求的明智响应。

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

评论


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

–古兰经
16年1月1日在9:35

#6 楼

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

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

评论


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

–aleppke
16-11-30在15:45

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

–通配符
16年11月30日在17:51

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

–RubberDuck
16 Dec 1'在10:59

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

–詹姆斯·斯内尔(James Snell)
16年1月1日在23:08

Single和SingleOrDefault(或Firstand FirstOrDefault)是一个完全不同的故事@RubberDuck。从我之前的评论中拿起我的例子。最好不要查询“甚至存在大于两个的质数?”的查询,因为这在数学上是合理且明智的答案。无论如何,如果您问“大于2的第一个偶数是什么?”没有自然的答案。您只是无法回答问题,因为您要输入一个数字。它不是无(不是一个数字,而是一个集合),而不是0。因此,我们抛出异常。

–Paul Kertscher
16 Dec 2'在11:58

#7 楼

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

因此,我将投票给ArgumentException,该ArgumentException为用户提供了必要的信息,以使他们了解发生了什么问题。

作为示例,请检查Linq中的
public static TSource ElementAt<TSource>(this IEnumerable<TSource>, Int32)函数。如果索引小于0或大于或等于source中的元素数,则抛出ArgumentOutOfRangeException。这样就可以根据调用方提供的可枚举来验证索引。

评论


+1表示该语言中类似情况的示例。

–詹姆斯·斯内尔(James Snell)
16-11-30在14:21

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

–anaximander
16年11月30日在14:30

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

– Ukko
16年11月30日在15:48

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

–sbecker
16 Dec 1'6 at 6:36

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

–通配符
16年2月2日在18:34

#8 楼

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


提供两种实现,一种在输入时返回空集在一起是荒谬的,并且抛出。尝试将它们称为CombinationsOfCombinationsOfWithInputCheck。或您喜欢的任何东西。您可以将其取反,以便输入检查的名称为短名称,列表的名称为CombinationsOfAllowInconsistentParameters。对于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
16 Dec 3'在2:34

您基本上与我的回答相同,而不是包装您碰巧查询If条件的查询,结果不会更改u.u

–CoffeDeveloper
16 Dec 3'在2:35

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

– ErikE
16 Dec 3'在2:36

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

–CoffeDeveloper
16/12/3在2:38



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

– ErikE
16 Dec 3'at 2:39



#9 楼

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

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

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

评论


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

–anaximander
16年11月30日在14:25

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

– gnasher729
16年3月3日在20:11

#10 楼

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


编写代码时,首先选择一个选项,然后选择另一个。考虑在实践中哪种方法最合适。
添加一个“ strict”布尔参数,以表明您是否希望对这些参数进行严格验证。例如,Java的SimpleDateFormat具有setLenient方法来尝试解析与格式不完全匹配的输入。当然,您必须确定默认值是什么。


#11 楼

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

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


使它成为正在为用户执行操作的任何对象上的配置选项。
给它加一个标志,用户可以选择将其传递给函数。
进行一次AssertNonempty检查,使其可以放入链中。
执行两个函数,一个声明非空函数,另一个声明非空函数。


评论


在前面的12个答案中,这似乎并没有提供任何实质性的解释。

– gna
16年1月1日,下午6:35

#12 楼

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


如果您在Python库中返回Inf,人们会在您隐藏错误的情况下攻击您
如果在Matlab库中引发错误,人们会因为您无法处理缺少值的数据而向您发起攻击。

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

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

评论


我考虑过在答案中使用NaN参数,并同意您的观点。我们不知道OP的范围就无法提供更多信息

–CoffeDeveloper
16 Dec 3'在3:26

#13 楼

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

通用设置规则:


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

您的问题非常微妙:


您的函数输入可能必须遵守合同:如果任何无效的输入都应引发异常,就是这样,该函数无法在常规参数下运行。
可能是函数的输入必须表现得完全像集合,因此应该能够返回空设置。

现在,如果我在您的位置,我将采用“设置”方式,但带有较大的“ 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可以告诉我们更多有关其领域的信息,那么我们也许可以找到真正需要例外的地方(如果完全有可能根本不需要任何异常)。

评论


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

–CoffeDeveloper
16年1月1日在12:23

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

–CoffeDeveloper
16年1月1日在12:30

没有!你不应该在你的女班上抛出异常。您应该使用Builder模式,该模式强制您甚至无法获取FemaleClass的实例,除非它只有女性(它不是公开创建的,只有Builder类可以分发一个实例),并且可能至少有一个女性。它。然后,将雌性类作为参数的各处代码都不必处理异常,因为对象处于错误状态。

– ErikE
2016年12月3日在1:37



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

–CoffeDeveloper
16 Dec 3'at 2:07



反对票不是我的,但如果是,我将为此感到自豪。我小心翼翼地投票,但坚信不误。如果您的类的规则是必须在其中包含一个项目,并且所有项目都必须满足特定条件,那么让该类处于不一致状态是一个坏主意。将问题转移到其他地方正是我的意图!我希望问题在构建时而不是在使用时发生。使用Builder类而不是使用构造函数引发的问题是,您可以通过不一致在很多部分中建立非常复杂的状态。

– ErikE
16年3月3日,在2:11