赞
踩
你在编写一个叫做split的函数,其用途是把输人行拆分为字段。下面的两个函数中,哪一个是更为正交的设计?
int Split1(File *fp) {}
int Split2(char *str) {}
// Split2,它专注于自己的任务,拆分输入行,同时忽略像输人行来自何处这样的细节,
// 这不仅使代码更易于开发,也使得代码更为灵活, Split2拆分的
// 行可以来自文件、可以由另外的例程生成、也可以通过环境传入
如果它不可能发生 用断言确保它不会发生。
无论何时你发现 自己在思考”但那当然不可能发生”,增加代码检查它。最容易的办法是使用断言。在大多数 C 和 C++ 实现中,你都能找到某种形式的检查布尔条件的 assert 或_assert宏。如果传入你的过程的指针决不应该是 NULL, 那么就检查它:
void WriteString(cbar *str)
{
assert(str ! = NULL);
...
}
不要用断言代替真正的错误处理。断言检查的是决不应该发生的事情。
假定你在改建你的房子,或是从头修建一所房子,典型的安排涉及到找一位 “总承包人” 你雇用承包人来完成工作,但承包人可能会、也可能不会亲自进行建造;他可能会把工作分包给好几个子承包人。但作为客户,你不用直接与这些子承包人打交道,总承包人会替你承担那些让人头疼的事情。
在软件中应遵循同样的模型。当我们要求某个对象完成特定服务时。我们想要它替我们完成该服务,我们不希望这个对象给我们一个第三方对象。我们必须对其加以处理才能获得所需服务。
假定你在编写一个类,生成科学记录仪数据图。你的数据记录仪分散在世界各地;每一个记录仪对象都含有一个地点对象,给出其位置及时区。你想要让你的用户选择记录仪,绘制其数据,并标上正确的时区,你可以编写:
void PlotDate(Date d, Selection s)
{
TimeZome tz = s.GetRecorder().GetLocation().GetTimeZone();
}
但现在绘制例程不必要地与三个类耦合在起——selection、recorder、 location。这种编码风格极大地增加了我们的类所依赖的类的数目。这为何是一件坏事?因为它增加了系统别的地方的一个无关改动影响你的代码的风险。例如,如果 Fred 对 location 做出改动,使它不再直接包含 TimeZone, 你也必须改动你的代码。
应该直接要求提供你所需的东西,而不是自行“挖通”调用层次:
void PlotDate(Date d, TimeZone tz)
对象间直接的横贯关系有可能很快带来依赖关系的组合爆炸。
有许多不必要的依赖关系的系统非常难以维护(而且很昂贵),往往高度地不稳定。
根据得墨忒耳法则,确定所示方法调用是否允许。
void ProcessTransaction(BankAccount acct, int) { Person *who; Money amt; amt.SetValue(l23.45); acct.SetBalance(amt); who = acct.GetOwner(); MarkWorkflow(who->name(), SET_BALANCE); } // ProcessTransaction 拥有 amt — 它是在栈上创建的。 acct 是传入的, // 所以既可以调用 SetValue, 也可以调用 SetBalance 。但 ProcessTransaction // 不拥有 who, 所以调用 who->name 违反了法则 得墨忒耳法则建议把这行代码改写成: // MarkWorkflow(acct.name(), SET_BALANCE); // ProcessTransaction 没有必要了解是 BankAccount 中的哪个子对象待有账户名——这一 // 结构知识不应通过 BankAccount 显露出来。相反,我们请求 BankAccount 提供账 // 户名。它知道自己把账户名放在何处(也许是在 person 中、在 business 中)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。