~写好的面向对象代码
本文?a >java.net上的一?a >博客Q作?a >Curtis Cooley对编写好的面向对象代码有些徏议,希望对大安有所帮助?2008.10.08最后更?
获取l验没有捷径。编写好的面向对象代码需要经验,但这儿有三种做法能帮你在一开始就很顺利,即便你是老顽固:
1. 使用试驱动开?TDD)~写你所有的代码
2. 遵@单法?/a>
3. 告之而非问之
使用TDD~写所有代?/strong>
?a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/tdd-is-design-activity.html">试先行~写的代码与按测试后行编写的代码是极Z同的代码。按试先行~写的代码是松耦合与高聚合的。当某个属性或U有Ҏ需要暴露给试E序Ӟ按测试后行编写的代码怼打破装Q因cdƈ不是Z试而设计的。如果你首先~写试代码Q你的依赖将会更好,你的代码是松耦合与高聚合的。后面会有更多关于测试能帮助你设计更佳代码的内容?br />
遵@单法?/strong>
代码是简单的Q只要当它:
1. 执行了所有的试
2. 不包含重?br />
3. 表达了所有的意图
4. 使用最的cdҎ
注意到我用的是个被排序了的列表是很重要的。顺序是重要的。只有一个main()Ҏ的的GodClass[1]不会是简单的。这个类可能执行了所有的试Q但在Q何比"Hello, world!"更复杂的E序中,它肯定包含了重复Qƈ且也没有表达出全部的意图?br />
我努力用简单法则去xIf问题。我不知道如何用简单法则去L某h~写重量U的If代码。有人可能会提出不同意见Q我也尝试过Q但q样的重量If代码实无法表达意图。但当你阅读如下代码?/span>
if (mobile.getType() == MobileTypes.STANDARD) {
alert();
}
实难以看出其中的意图。这些代码无论处于哪个方法的上下文环境中Q我们都能知道,如果mobile是STANDARDcd的话Q那么就报警。而你所需要的更多意图呢?
我还有一点儿灉|昄。如果有那样的代码,那么在其它地方肯定还会有更多那样的代码。这些代码可能就像:
if (mobile.getType() == MobileTypes.GAS) {
registerGasReading();
}
?/span>
if (mobile.getType() == MobileTypes.TEXT) {
sendTextMessage();
}
?/span>
if (mobile.getType() == MobileTypes.LOCATION) {
notifyLocation();
}
你看出来了吗Q我是看出来了。它q反了规?Q有很多地方都违反了规则2Qƈ且是一U最坏的情Ş。这D代码有多处重复。重复将极难发现。所以,请帮助防止这U情形的发生Q我已包含其中了?br />
告之而非问之
a之,告之而非问之意指不要先问一个对象的状态,然后才让它去工作。而应该告之对象如何去工作。这意味着之前所有的那些If例子应该变ؓQ?/span>
mobile.alert();
?/span>
mobile.registerGasReading();
?/span>
mobile.sendTextMessage();
?/span>
mobile.notifyLocation();
现假N布该E序中的一些If语句块有重复的实现。在"重量UIf"版本的程序中Q可能很隑֏现它们;但在"告之而非问之"版本的程序中Q所有的实现都在Mobile中。所有的实现都在一处,q就便于察觉q根除问题?br />
們你的试E序也能帮助你保持代码的z?/span>
public interface Alarm {
void alert(Mobile mobile);
}
public class Siren implements Alarm {
public void alert(Mobile mobile) {
if (mobile.getType == MobileTypes.STANDARD) {
soundSiren();
}
}
}
public class TestSiren extends TestCase {
public void test_alert() {
LocationMobile mobile = new LocationMobile();
Siren siren = new Siren();
siren.alert(mobile);
assert(sirenSounded());
}
}
如果你密切地們试E序Q它可能会问你,"Z么你需要一个LocationMobileL试Siren呢?"的确Qؓ什么呢Q看hQSiren应该q不知道LocationMobile吧?/span>
public class LocationMobile {
private Alarm alarm;
public LocationMobile(Alarm alarm) {
this.alarm = alarm;
}
public void alert() {
alarm.alert(); // alert on Alarm no longer needs a mobile
}
}
public class TestLocationMobile() extends TestCase {
public void test_alert() {
Alarm alarm = EasyMock.createMock(Alarm.class);
alarm.alert();
EasyMock.replay(alarm);
Mobile mobile = new LocationMobile(alarm);
mobile.alert();
EasyMock.verify(alarm);
}
好像我只是交换了依赖关系。Alarm不再依赖MobileQ现在是Mobile依赖Alarm。但如果你仔l地观察q个试E序Q你会发现真正的依赖关系是,Siren知晓了LocationMobile。一个具体类依赖另一个具体类Q这q反?a >依赖反{原则(DIP)。第二个例子pLocationMobile依赖Alarm接口。具体类依赖抽象Q这满DIP了?br />
如果你用TDDQƈ遵@单法则和告之而非问之原则ȝ写所有的代码Q你处于成Z个更好的面向对象E序员的道\上了。好的面向对象代码易于阅dl护Q但难以~写Q至,在开始时是这L。你写的多Q你׃变得好Q也会获得更多的l验。同Ӟq些实践l验也会使你在自q道\上受益匪?br />
译注
[1]GodClass(上帝c?指包含了太多内容的类?br />

]]>