这绝对是主观的,但我想尽量避免它变成争论。我认为如果人们恰当地对待它,这将是一个有趣的问题。

这个问题的想法来自于我对“你最讨厌的语言的哪五件事?”问题的回答。我认为c#中的类在默认情况下应该是密封的——我不会把我的理由放在这个问题上,但我可能会写一个更完整的解释来回答这个问题。我对评论中的讨论热度感到惊讶(目前有25条评论)。

那么,你有什么有争议的观点?我宁愿避免那些基于相对较少的基础而导致相当宗教的事情(例如,大括号放置),但例如可能包括“单元测试实际上并没有多大帮助”或“公共字段确实是可以的”之类的事情。重要的是(至少对我来说)你的观点背后是有理由的。

请提出你的观点和理由——我鼓励人们投票给那些有充分论证和有趣的观点,不管你是否恰好同意这些观点。


当前回答

当为数据访问层创建单元测试时,数据应该直接从DB中检索,而不是从模拟对象中检索。

考虑以下几点:

void IList<Customer> GetCustomers()
{
  List<Customer> res = new List<Customer>();

  DbCommand cmd = // initialize command
  IDataReader r = cmd.ExecuteQuery();

  while(r.read())
  {
     Customer c = ReadFiledsIntoCustomer(r);
     res.Add(c);
  }

  return res;
}

在GetCustomers的单元测试中,对cmd.ExecuteQuery()的调用应该实际访问DB还是应该模拟它的行为?

我认为,如果以下情况成立,您不应该模拟对DB的实际调用:

存在一个测试服务器和模式。 模式是稳定的(意味着您不期望对其进行重大更改) DAL没有聪明的逻辑:查询是简单地构造的(config/stored procs) 去虹膜化的逻辑很简单。

从我的经验来看,这种方法的最大好处是你可以尽早与DB进行交互,体验“感觉”,而不仅仅是“外观”。它为您省去了许多事后的麻烦,并且是熟悉模式的最佳方法。

许多人可能会争辩说,一旦执行流跨越了进程边界,它就会成为一个单元测试。我同意它有它的缺点,特别是当数据库不可用,然后你不能运行UT。

然而,我相信在许多情况下这应该是一件有效的事情。

其他回答

您不应该停留在您发现的编写“有效”代码的第一种方法上。

I really don't think this should be controversial, but it is. People see an example from elsewhere in the code, from online, or from some old "Teach yourself Advanced Power SQLJava#BeansServer in 3.14159 minutes" book dated 1999, and they think they know something and they copy it into their code. They don't walk through the example to find out what each line does. They don't think about the design of their program and see if there might be a more organized or more natural way to do the same thing. They don't make any attempt at keeping their skill sets up to date to learn that they are using ideas and methods deprecated in the last year of the previous millenium. They don't seem to have the experience to learn that what they're copying has created specific horrific maintenance burdens for programmers for years and that they can be avoided with a little more thought.

事实上,他们似乎甚至没有意识到做一件事可能有不止一种方法。

I come from the Perl world, where one of the slogans is "There's More Than One Way To Do It." (TMTOWTDI) People who've taken a cursory look at Perl have written it off as "write-only" or "unreadable," largely because they've looked at crappy code written by people with the mindset I described above. Those people have given zero thought to design, maintainability, organization, reduction of duplication in code, coupling, cohesion, encapsulation, etc. They write crap. Those people exist programming in every language, and easy to learn languages with many ways to do things give them plenty of rope and guns to shoot and hang themselves with. Simultaneously.

But if you hang around the Perl world for longer than a cursory look, and watch what the long-timers in the community are doing, you see a remarkable thing: the good Perl programmers spend some time seeking to find the best way to do something. When they're naming a new module, they ask around for suggestions and bounce their ideas off of people. They hand their code out to get looked at, critiqued, and modified. If they have to do something nasty, they encapsulate it in the smallest way possible in a module for use in a more organized way. Several implementations of the same idea might hang around for awhile, but they compete for mindshare and marketshare, and they compete by trying to do the best job, and a big part of that is by making themselves easily maintainable. Really good Perl programmers seem to think hard about what they are doing and looking for the best way to do things, rather than just grabbing the first idea that flits through their brain.

如今,我主要在Java世界中编程。我见过一些非常好的Java代码,但我也见过很多垃圾代码,而且我还看到了更多我在开头描述的心态:人们选择了第一个看起来可以工作的丑陋代码块,而不理解它,也不考虑是否有更好的方法。

You will see both mindsets in every language. I'm not trying to impugn Java specifically. (Actually I really like it in some ways ... maybe that should be my real controversial opinion!) But I'm coming to believe that every programmer needs to spend a good couple of years with a TMTOWTDI-style language, because even though conventional wisdom has it that this leads to chaos and crappy code, it actually seems to produce people who understand that you need to think about the repercussions of what you are doing instead of trusting your language to have been designed to make you do the right thing with no effort.

我确实认为你可能会在另一个方向上走得太远:例如,完美主义完全忽略了你的真正需求和目标(通常是你的业务的真正需求和目标,通常是盈利能力)。但我不认为任何人都能成为一个真正伟大的程序员,除非学会投入一些高于平均水平的努力来思考寻找最好的(或至少是最好的一种)方法来编码他们正在做的事情。

布尔变量只能用于布尔逻辑。在所有其他情况下,使用枚举。


布尔变量用于存储只能有两个可能值的数据。使用它们所产生的问题经常被忽视:

程序员通常不能正确地识别某些数据何时应该只有两个可能的值 指导程序员做什么的人,比如程序经理或编写程序员遵循的规范的人,通常也不能正确地识别这一点 即使一段数据被正确地识别为只有两种可能的状态,这种保证在将来也可能不成立。

在这些情况下,使用布尔变量会导致令人困惑的代码,这通常可以通过使用枚举来避免。

例子

假设一个程序员正在为一家只销售轿车和卡车的汽车经销商编写软件。程序员为他的软件开发一个全面的业务需求模型。由于知道所销售的车辆只有轿车和卡车两种类型,他正确地识别出可以在Vehicle类中使用一个布尔变量来指示车辆是轿车还是卡车。

class Vehicle {
 bool isTruck;
 ...
}

这个软件是这样写的,当isTruck为真时,车辆是一辆卡车,当isTruck为假时,车辆是一辆汽车。这是在整个代码中执行多次的简单检查。

一切都很顺利,直到有一天,汽车经销商收购了另一家同样销售摩托车的经销商。程序员必须更新软件,使其能够正确地考虑到经销商的业务发生了变化。现在它需要识别车辆是汽车、卡车还是摩托车,这是三种可能的状态。

程序员应该如何实现这一点?isTruck是一个布尔变量,所以它只能保存两种状态。他可以将其从布尔类型更改为允许多种状态的其他类型,但这将破坏现有的逻辑,并且可能无法向后兼容。从程序员的角度来看,最简单的解决方案是添加一个新变量来表示车辆是否是摩托车。

class Vehicle {
 bool isTruck;
 bool isMotorcycle;
 ...
}

代码更改后,当isTruck为真时,车辆为卡车,当isMotorcycle为真时,车辆为摩托车,当两者都为假时,车辆为汽车。

问题

这种解决方案存在两个大问题:

The programmer wants to express the type of the vehicle, which is one idea, but the solution uses two variables to do so. Someone unfamiliar with the code will have a harder time understanding the semantics of these variables than if the programmer had used just one variable that specifies the type entirely. Solving this motorcycle problem by adding a new boolean doesn't make it any easier for the programmer to deal with such situations that happen in the future. If the dealership starts selling buses, the programmer will have to repeat all these steps over again by adding yet another boolean.

软件的业务需求发生变化,要求他修改现有代码,这不是开发人员的错。但是首先使用布尔变量使得他的代码不那么灵活,也更难修改以满足未知的未来需求(不那么“适合未来”)。当他以最快的方式实现更改时,代码变得难以阅读。使用布尔变量最终是一个不成熟的优化。

解决方案

首先使用枚举就可以避免这些问题。

enum EVehicleType { Truck, Car }

class Vehicle {
 EVehicleType type;
 ...
}

为了在这种情况下容纳摩托车,程序员所要做的就是将Motorcycle添加到evhicletype中,并添加新的逻辑来处理摩托车的情况。不需要添加新的变量。现有的逻辑不应该被打乱。不熟悉代码的人也很容易理解车辆的类型是如何存储的。

悬崖笔记

不要使用只能存储两种不同状态的类型,除非您绝对确定两种状态总是足够的。如果将来可能需要两个以上的状态,则使用枚举,即使布尔值可以满足现有需求。

远离Celko!!!!

http://www.dbdebunk.com/page/page/857309.htm

我认为使用代理主键比使用“自然”主键更有意义。


@ocdecio: Fabian Pascal给出了(在他的书《数据库管理中的实际问题》的第三章中,引用于你链接的页面的第3点)作为选择键的标准之一,那就是稳定性(它总是存在并且不会改变)。当一个自然键不具有这样的属性时,必须使用一个代理键,原因很明显,您在注释中暗示了这一点。

你不知道他写了什么,你也懒得去检查,否则你会发现你实际上同意他的观点。这里没有什么争议:他说的是“不要教条主义,根据情况调整一般的指导方针,最重要的是,思考,用你的大脑,而不是教条主义/食谱/大师的话”。

我的一个:

长开关语句是你的朋友。真的。至少在c#中是这样。

人们倾向于避免和不鼓励其他人使用长switch语句,因为它们“不可管理”和“具有糟糕的性能特征”。

好吧,在c#中,switch语句总是自动编译为哈希跳转表,所以如果你需要简单的分支到多个分支,实际上使用它们是性能方面的最佳选择。同样,如果case语句被智能地组织和分组(例如按字母顺序),那么它们就不是无法管理的。

System.Data.DataSet岩石!

在我看来,对于大多数业务应用程序,强类型数据集比自定义DDD对象更好。

推理:我们在自定义对象、LINQ to SQL、实体框架上绞尽脑汁地寻找工作单元,这增加了复杂性。使用一个好的代码生成器来生成数据层,工作单元位于对象集合(DataTable和DataSet)上——这并不神秘。