乱世之初在程序开发的过程中,我们有时会看到这样的Java类:公共方法有上百个,单个方法有上百行。不好。虽然他只能勉强维持当前功能的运行。但实际上它已经无法扩展功能了。我们能为他做的就是保守对待他,危楼添砖加瓦。虽然每个人都不愿意承认自己是混乱的制造者,但实际上,每一个巨人类的代码都是你我亲手或间接创造出来的。但是当有一天我们意识到这个类过于庞大,需要重构时,我们就需要一些方法论和指南来帮助我们判断。在实际开发过程中什么样的类好,哪些类写得好,哪些类写得不好,我们一眼就能判断出来。我们可能无法确切地说出原因,但我们可以感觉到。这样做的原因是,一个优秀的班级可能会完全展示它的能力。我们在写代码的时候,无时无刻不在和类打交道。类与类之间表达的逻辑之间的依赖和交互是我们关注的重点。所以,当我们看到一个类,不能轻易判断它是做什么用的,或者一不小心误判了它的实际功能,那么这个类就是有问题的。我们瞄准的以下几个角度可以帮助我们判断一个班级是否优秀。统一顺序我们在写代码的时候总是有各种各样的规范。规范的目的不是与实际编码人员对抗,主要目的是减少团队中的沟通成本。所以写class文件,首先要做的就是统一所有class中内容的排列顺序。这有两个好处:它减少了写入文件时位置考虑的成本。减少阅读代码时的理解成本。显然我们更关心的是第二点。具体来说,我们需要保证类的属性在一起,类的方法在一起,这样我们在阅读代码的时候才不会漏掉关键信息(很多时候,我们只是简单的浏览一下类的全貌)类,然后直接去寻找我们关心的)。同时,一般来说,我们写类的顺序是:publicstaticconstantsprivatestaticconstantsprivatestaticvariables(不应该有public变量)publicmethodspublic方法使用的私有方法一般来说,我们应该把属性放在上面类,方法放在类下面,私有方法放在调用的公共方法下面(见《如何写好一个方法》)。这样在阅读课内内容时,很容易从上到下一步步了解课的细节,符合我们自上而下的阅读习惯。SingleResponsibility有时我们会觉得上课时间可能太长了。我们有这样一个想法的原因有很多,其中一个比较重要的原因是:这个类同时承担了多种功能。我们进行面向对象编程的主要方式是以类的形式定义具有某些能力的对象,而这些能力就是类的方法。比如:一个可以播放音乐的音箱,那么我们可以创建一个音箱类,然后就有一个播放音乐的方法。目前,这个类很容易理解。我们有一个speaker类,然后通过speaker类实例化对象就可以得到一个speaker,调用speaker中的playback方法就可以播放音乐了。但是随着功能的扩展,可能我们的音箱变成了一个移动音箱(注意我们只有一个音箱),加上充电功能,所以我们在这个类中加入充电方法。随着功能的不断开发,我们可能会在speaker类中加入很多相关的东西,我们可能会在speaker类中加入:充电、显示时间、定时关机、随机播放等工作功能。不断迭代之后,这个类会变得很臃肿,但是判断它是否臃肿的条件是它是否有单一的职责。单一职责的概念虽然比较好理解,但在实际操作中并没有明确的界限,也就是说,全凭感觉。就像上面的音箱例子,如果只是充电和播放音乐的话,我们也可以写成一个类。但是如果有更多的功能,比如增加电量显示、涓流充电等功能,那么显然我们应该把这些方法放到一个电池类中,把电量属性放到里面。所以从实现的角度,我们可以用两种方法来帮我判断这个方法是否满足单一职责:是否能给方法起一个合适的名字,或者是否能用简短的一句话描述它的功能。在解释上,如果类不能用一个对象名来描述,而只能用一些通用的概念来描述(比如processor、executor、manager),说明这个类的功能并不单一(当然,如果代码规范本身是这样定义的,那就另当别论了)。而如果不能用一两句话来描述类的功能,或者不得不用很多“and”“or”之类的词来串联描述,就说明已经采取了太多的功能,我们应该拆除它。分享。内聚我们在做面向对象编程的时候,经常说类需要“高内聚低耦合”。如果我们把一个类中的方法对类中的属性进行操作称为方法和属性之间的关联,那么关联越多的类就越有内聚性。在极端情况下,如果类中的所有方法都使用所有类属性,则该类是最内聚的。实际情况并不总是那么理想,所以我们可以根据这个联想来判断课堂上的内容是否足够衔接。因此,如果写完类后发现有的方法只关联了一些属性,而有的方法关联了其他属性,就说明这两部分之间没有内聚性。那么我们可以把它拆分成两个类,这两个类会更有凝聚力:方法和属性作为一个整体相互依赖。当我们通过内聚来分析类时,我们可能会将一个大类拆分成多个子类。这样会增加类的数量,增加类的复杂度,同时也会增加整体的代码量。但是类本身可以通过包路径来分类,所以我觉得这样拆分比较合理。Extensible对于一些需要扩展的类,虽然它们可能满足单一职责和内聚特性。但是由于这个类本身的可扩展性,当出现新的业务需求时,我们会经常修改这个类,增加新的方法。这种修改带来的问题是,每次修改代码或者增加新的方法,都不能保证不影响原来的功能。虽然我们可以通过单元测试或者集成测试来验证我们的修改,但是这会增加我们的工作量。因此,对于可能频繁修改、有业务增加的方法类,我们需要为其预留可扩展性。我们可以通过实现统一的接口或者继承抽象类和父类的方法来获得多个具有不同扩展能力的子类,这些子类也可以通过策略模式或者责任链模式来组织。扩展一个类总是比直接修改它更好。这个可测试性的角度其实源于我的另外一个问题,就是在传统的三层架构下,是否需要为中间的服务层写一层接口。我原来的理解是,大多数情况下,我们不会为这个服务实现其他服务,所以单独写一个接口,加上相应的Impl,只会让我们在扩展方法的时候更方便。麻烦。但是现在我发现了一个直接的用途,那就是:支持单元测试。如果我们直接依赖具体细节,这会给我们的测试带来挑战。具体来说,如果我们依赖某个具体类的实现,那么当我们要测试细节的时候,我们就得调整细节以外的内容,可能包括:系统时间,数据库字段等。但是如果我们使用调用servce的接口,那么我们测试的时候直接写测试服务就可以达到预期的返回内容。这大大简化了测试的难度。我们通过接口降低系统之间的耦合度,让类之间的关系更好理解,也方便测试。而这也是我们的依赖倒置原则(DIP),我们编程是为了抽象,让它不依赖于具体的细节,这样当我们需要调整细节的时候,也不会影响到上层的内容。最后,我们在编码的过程中总是在编写类。本文介绍写类的一些注意事项。在编写过程中,我们主要需要关注类:内部属性和方法的顺序,类的职责是否单一,是否足够内聚,是否支持扩展,是否可以进行单元测试。虽然这不是所有需要注意的,但如果您能考虑一下就足够了。
