我的同事国, 布尔作方法的论点是不可接受的.他们应代替枚举。在第一次我没有看到任何效益,但是他给了我一个例子。

什么是容易理解?

file.writeData( data, true );

enum WriteMode {
  Append,
  Overwrite
};

file.writeData( data, Append );

现在我知道了!;-)
这绝对是一个例子,其中一枚举,作为第二个参数使代码更具可读性。

那么,你的意见,对这个主题?

有帮助吗?

解决方案

布尔表示“是/否”选择。如果你想表示“是/否”,那么使用布尔值,它应该是不言自明的。

但如果它是两个选项之间的选择,两者都不是明显是或否,那么枚举有时可能更具可读性。

其他提示

Enums还允许将来进行修改,您现在需要第三种选择(或更多)。

使用最能模拟问题的那个。在您给出的示例中,枚举是更好的选择。但是,有时布尔值会更好。这对你更有意义:

lock.setIsLocked(True);

enum LockState { Locked, Unlocked };
lock.setLockState(Locked);

在这种情况下,我可能会选择布尔选项,因为我认为它非常明确且毫不含糊,而且我很确定我的锁定不会有两个以上的状态。尽管如此,第二种选择是有效的,但不必要的复杂,恕我直言。

我认为你自己几乎已经回答了这个问题,我认为最终的目的是让代码更具可读性,在这种情况下,枚举就是这样做的,IMO总是最好看一下最终目标而不是一揽子规则,也许是认真思考它更像是一个指导原则,即代码中的枚举通常比通用的bool,int等更具可读性,但规则总会有例外。

请记住Adlai Stevenson在古巴导弹期间向联合国大使佐林提出的问题危机

  

“你在世界的法庭上   现在的意见,你可以回答   是或否。你否认[导弹]   存在,我想知道我是否   我正确理解你了......我是   准备等到我的答案,直到   地狱冻结了,如果这是你的   。决策"

如果您的方法中的旗帜具有这样的性质,您可以将其固定为二元决策,并且该决定永远不会变成三-way或n-way决定,去布尔值。适应症:您的旗帜名为 isXXX

如果出现模式切换,请不要将其设为布尔值。在首先编写方法时,总有多一个模式

一个多模式的困境有:困扰Unix,文件或目录今天可能拥有的权限模式会导致模式的奇怪双重含义,具体取决于文件类型,所有权等。

对我而言,使用布尔值或枚举都不是一个好方法。 Robert C. Martin在他的清洁代码提示#12:消除布尔参数中非常清楚地捕获了这一点

  

布尔参数大声声明该函数不止一件事。它们令人困惑,应该被淘汰。

如果方法不止一件事,你应该写两种不同的方法,例如在你的情况下: file.append(data) file.overwrite(data)

使用枚举不会使事情更清楚。它没有改变任何东西,它仍然是一个旗帜论点。

我遇到的两个原因是坏事:

  1. 因为有些人会写下这样的方法:

    ProcessBatch(true, false, false, true, false, false, true);
    

    这显然很糟糕,因为混合参数太容易了,你不知道通过查看你指定的内容。只有一个布尔并不是太糟糕。

  2. 因为通过简单的是/否分支控制程序流可能意味着你有两个完全不同的函数,这些函数以一种笨拙的方式包装成一个。例如:

    public void Write(bool toOptical);
    

    真的,这应该是两种方法

    public void WriteOptical();
    public void WriteMagnetic();
    

    因为这些代码可能完全不同;他们可能不得不进行各种不同的错误处理和验证,甚至可能不同地格式化传出数据。你不能仅仅通过使用 Write()甚至 Write(Enum.Optical)来判断它(当然你可以使用这些方法中的任何一种来调用内部方法WriteOptical / Mag如果你愿意的话。

  3. 我想这只是取决于。除了#1之外,我不会对它做太大的交易。

枚举更好,但我不会将布尔参数称为“不可接受的”。有时候抛出一个小布尔值并继续前进会更容易(想想私有方法等)。

布尔可能在具有命名参数的语言中可能没问题,例如Python和Objective-C,因为名称可以解释参数的作用:

file.writeData(data, overwrite=true)

或:

[file writeData:data overwrite:YES]

我不同意这是一个好的规则。显然,在某些情况下,Enum会提供更好的显式或冗长的代码,但通常情况下它似乎无法实现。

首先让我举个例子: 编写好代码的程序员责任(和能力)并没有因为使用布尔参数而受到严重损害。在您的示例中,程序员可以通过编写以下来编写详细的代码:

dim append as boolean = true
file.writeData( data, append );

或者我更喜欢更一般的

dim shouldAppend as boolean = true
file.writeData( data, shouldAppend );

第二: 你给出的Enum例子只是“更好”。因为你正在通过一个CONST。最有可能在大多数应用程序中,至少有一些(如果不是大多数时间)传递给函数的参数是VARIABLES。在这种情况下,我的第二个例子(给出具有好名字的变量)要好得多,Enum会给你带来很少的好处。

枚举有一个明确受益,但是你应该不只是去换你所有的布尔与枚举。有很多地方true/false实际上是最好的方式来表示是怎么回事。

然而,利用他们作为方法的论点是一位犯罪嫌疑人,只是因为你看不到而没有挖掘的事情是什么,他们都应该这样做,因为他们让你看到什么true/false 实际上意味着

性(特别是与C#3对象的初始化程序)或者关键词参数(a la红宝石或python)是一个更好的路要走,你愿意否则使用一个布尔的论点。

C#如:

var worker = new BackgroundWorker { WorkerReportsProgress = true };

例如红宝石

validates_presence_of :name, :allow_nil => true

蟒蛇例

connect_to_database( persistent=true )

我唯一能想到的其中一个布尔的方法的论点是正确的做法是在爪哇,你没有任一特性或者关键词的论点。这是其中一个原因,我讨厌java:-(

虽然在许多情况下,枚举比布尔值更具可读性和可扩展性,但绝对规则“布尔值是不可接受的”。是愚蠢的。它缺乏灵活性和适得其反 - 它没有留下人类判断的空间。它们在大多数语言中都是基本的内置类型,因为它们很有用 - 考虑将它应用于其他内置类型:例如说“永远不要使用int作为参数”。会发疯的。

此规则只是一个样式问题,而不是错误或运行时性能的潜在问题。一个更好的规则是“出于可读性的原因,更喜欢枚举到布尔”。

查看.Net框架。布尔值被用作很多方法的参数。 .Net API并不完美,但我不认为使用布尔值作为参数是一个大问题。工具提示总是为您提供参数的名称,您也可以构建此类指南 - 填写方法参数的XML注释,它们将出现在工具提示中。

我还应该补充说,有一种情况,你应该明确地将布尔值重构为枚举 - 当你的班级或你的方法参数中有两个或更多的布尔值时,并且并非所有状态都有效(例如它无效)让他们都设置为真。)

例如,如果您的类具有类似

的属性
public bool IsFoo
public bool IsBar

同时让两者都成立是错误的,你实际得到的是三种有效状态,更好地表达为:

enum FooBarType { IsFoo, IsBar, IsNeither };

一些规则,你的同事可能会更好地遵守:

  • 不是教条主义与你的设计。
  • 选择什么样的适合最适当的用户的代码。
  • 不要试图砸星形钉到每一个洞只是因为你喜欢的形状这一个月!

如果您不打算扩展框架的功能,则只能接受布尔值。 Enum是首选,因为您可以扩展枚举,而不会破坏函数调用的先前实现。

Enum的另一个优点是更容易阅读。

如果方法提出如下问题:

KeepWritingData (DataAvailable());

,其中

bool DataAvailable()
{
    return true; //data is ALWAYS available!
}

void KeepWritingData (bool keepGoing)
{
   if (keepGoing)
   {
       ...
   }
}

布尔方法参数似乎具有绝对完美的意义。

它取决于该方法。如果方法不会的东西,这是非常明显true/false事,那么它是好的,例如以下[虽然不我不是说这是最好的设计对于这种方法,它只是一个例子使用是显而易见的].

CommentService.SetApprovalStatus(commentId, false);

然而在大多数情况下,如你所说,最好是使用一枚举。有许多例子。净框架本身在这个《公约》没有得到遵守,但是因为他们推出了这样的设计准则的相当晚在周期。

它确实使事情变得更加明确,但确实开始大规模地扩展接口的复杂性 - 在纯粹的布尔选择中,例如追加/覆盖它似乎有点过分。如果你需要添加另一个选项(在这种情况下我想不到),你总是可以执行一个重构(取决于语言)

枚举肯定可以使代码更具可读性。还有一些事情需要注意(至少在.net中)

因为枚举的底层存储是一个int,所以默认值为零,所以你应该确保0是一个合理的默认值。 (例如,结构在创建时将所有字段设置为零,因此无法指定0以外的默认值。如果没有0值,则无法在不转换为int的情况下测试枚举,这将是糟糕的风格。)

如果您的代码专用于您的代码(从未公开曝光),那么您可以在这里停止阅读。

如果您的枚举以任何方式发布到外部代码和/或保存在程序之外,请考虑明确编号。编译器会自动从0开始编号,但是如果重新排列枚举而不给它们赋值,则最终会出现缺陷。

我可以合法地写

WriteMode illegalButWorks = (WriteMode)1000000;
file.Write( data, illegalButWorks );

为了解决这个问题,任何使用您无法确定的枚举的代码(例如公共API)都需要检查枚举是否有效。你这样做是通过

if (!Enum.IsDefined(typeof(WriteMode), userValue))
    throw new ArgumentException("userValue");

Enum.IsDefined 的唯一警告是它使用反射并且速度较慢。它也遇到了版本问题。如果您需要经常检查枚举值,那么最好不要使用以下内容:

public static bool CheckWriteModeEnumValue(WriteMode writeMode)
{
  switch( writeMode )
  {
    case WriteMode.Append:
    case WriteMode.OverWrite:
      break;
    default:
      Debug.Assert(false, "The WriteMode '" + writeMode + "' is not valid.");
      return false;
  }
  return true;
}

版本控制问题是旧代码可能只知道如何处理您拥有的2个枚举。如果添加第三个值,Enum.IsDefined将为true,但旧代码不一定能处理它。糟糕。

使用 [Flags] 枚举可以获得更多乐趣,而且验证代码略有不同。

我还要注意,为了便于携带,你应该在枚举上使用call ToString(),并在重新读取时使用 Enum.Parse() ToString() Enum.Parse()都可以处理 [Flags] 枚举,因此没有理由不使用它们。请注意,这是另一个陷阱,因为现在你甚至无法在不破坏代码的情况下更改枚举的名称。

所以,有时候当你问自己时,你需要权衡上述所有内容我可以只用一个布尔侥幸逃脱吗?

恕我直言,对于任何可能有两种以上选项的情况来说,枚举似乎是明显的选择。但是肯定会出现需要布尔值的情况。在这种情况下,我会说使用一个bool可以工作的枚举将是一个使用7个单词时4个单词的例子。

当你有一个明显的切换时,布尔有意义,这只能是两件事之一(即灯泡的状态,打开或关闭)。除此之外,以这样一种方式写它是很好的,这样很明显你传递的东西 - 例如磁盘写入 - 无缓冲,行缓冲或同步 - 应该这样传递。即使你现在不想允许同步写入(因此你只限于两个选项),为了了解他们乍一看他们做的事情,值得考虑使它们更加冗长。

也就是说,您也可以使用False和True(布尔值0和1)然后如果您需要更多值,请将函数展开以支持用户定义的值(例如,2和3),以及您的旧0 / 1值将很好地移植,因此您的代码不应该破坏。

有时,使用重载来模拟不同的行为会更简单。从你的例子继续是:

file.appendData( data );  
file.overwriteData( data );

如果您有多个参数,每个参数都允许一组固定的选项,则此方法会降级。例如,打开文件的方法可能有多种文件模式(打开/创建),文件访问(读/写),共享模式(无/读/写)的排列。配置总数等于各个选项的笛卡尔积。当然,在这种情况下,多次过载是不合适的。

在某些情况下,枚举可以使代码更具可读性,尽管在某些语言(例如C#)中验证确切的枚举值可能很困难。

通常,布尔参数作为新的重载附加到参数列表中。 .NET中的一个例子是:

Enum.Parse(str);  
Enum.Parse(str, true); // ignore case

后者的重载在.NET框架的更高版本中可用,而不是第一个。

如果你知道只有两个选择,布尔可能没问题。枚举可以通过不会破坏旧代码的方式进行扩展,尽管旧库可能不支持新的枚举值,因此无法完全忽略版本控制。


修改

在较新版本的C#中,可以使用命名参数,IMO可以使命令更清晰,就像枚举一样。使用与上面相同的例子:

Enum.Parse(str, ignoreCase: true);

我同意Enums是一个很好的方法,在你有2个选项的方法中(只有两个选项,你可以在没有枚举的情况下具有可读性。)

e.g。

public void writeData(Stream data, boolean is_overwrite)

喜欢Enums,但布尔也很有用。

这是旧帖子上的一个较晚的条目,并且在页面上这么远,没有人会读它,但是因为没有人说过它......

内联注释在解决意外的 bool 问题方面有很长的路要走。最初的例子特别令人发指:想象试图在函数declearation中命名变量!它就像

void writeData( DataObject data, bool use_append_mode );

但是,为了举例,让我们说这是宣言。然后,对于一个原因不明的布尔参数,我将变量名称放在内联注释中。比较

file.writeData( data, true );

file.writeData( data, true /* use_append_mode */);

这实际上取决于论证的确切性质。如果它不是yes / no或true / false,则枚举使其更具可读性。但是对于枚举,您需要检查参数或具有可接受的默认行为,因为可以传递基础类型的未定义值。

在您的示例中使用枚举而不是布尔值确实有助于使方法调用更具可读性。但是,这可以替代我在C#中最喜欢的愿望项,在方法调用中命名参数。语法如下:

var v = CallMethod(pData = data, pFileMode = WriteMode, pIsDirty = true);

将是完全可读的,然后您可以执行程序员应该做的事情,即为方法中的每个参数选择最合适的类型,而不考虑它在IDE中的外观。

C#3.0允许构造函数中的命名参数。我不知道他们为什么不能用方法做到这一点。

布尔值仅限 true / false 。所以它不清楚它代表什么。 Enum 可以有有意义的名称,例如 OVERWRITE APPEND 等等。所以枚举更好。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top