熟练掌握和应用面向对象设计(Object Oriented Design,OOD)7大原则,是初/中级JAVA工程师向高级/资深工程师进阶的一个必备技能,它可以大大提升程序的可复用性和可维护性,也是重构代码的一大利器。7大设计原则包括开闭原则、里氏替换原则、依赖倒转原则、单一职责原则、接口隔离原则、组合/聚合复用原则、迪米特法则。
面向对象可复用设计原则中最基础的原则便是开闭原则(Open-Closed Principle,OCP)。开闭原则指的是在设计一个对象(类、模块、函数)时,应遵循或做到对扩展开放、对修改关闭,其核心思想是面向接口/抽象进行编程。比如第四方支付平台,集成微信支付服务时,遵循开闭原则的话,正常的做法就是先抽象设计一个支付接口,然后微信支付类实现该支付接口。以后平台集成更多如支付宝、华为、苹果等支付服务时,只需要扩展一个对应支付方式的支付类即可,这样既扩展了新的支付服务,同时避免了修改现有的支付接口以及微信支付类,最大限度保证了新代码不会影响现有的业务代码。这就是对扩展(新对象)开放,对修改(现有对象)关闭。
里氏替换原则(Liskov Substitution Principle,LSP)要求设计一个对象时,代码中任何父类对象可以出现的地方,子类都可以出现,即可以使用子类来代替父类。实现开闭原则的核心思想是面向接口/抽象进行编程,其关键步骤是抽象化,而父类和子类的继承关系就是抽象化的具体体现,所以里氏替换原则是对实现抽象化的具体步骤的规范,它是对开闭原则的补充。比如教学平台中新增加了学生用户Student,和教师用户Teacher一样进入个人中心,都需要登录,在设计或重构用户登录服务类时,抽象的用户User类作为教师Teacher和学生Student的父类,此时需要遵循出现用户User的地方,都可以使用新扩展的学生Student代替,当然也必须也可以使用教师Teacher代替,这样设计出来的User类,对用户登录服务类来说,后续扩展更多的用户User的子类比如家长、辅导员时,可以保证符合开闭原则。
依赖倒转原则(Dependency Inversion Principle,DIP)指程序中要依赖抽象(接口/抽象类)而不是具体的实现(实现类)进行编程,即面向接口/抽象进行编程,这样可以最大程度降低模块之间的耦合。比如订单服务中,会调用(依赖)支付服务以完成订单的支付。此时的订单服务实现类代码中,声明或持有的应该是一个支付服务PayService接口,而不是具体实现类比如Alipay或WeChatPay,所有需要用到支付服务的地方,调用的都是PayService接口变量,即代码中都是接口,等到在运行时才实例化或注入具体的实现类,编写代码时不需要关心具体实现类,也就是面向接口/抽象编程。这样,在应用程序扩展或集成新的支付服务时,原来的订单服务代码可以不需要进行修改任何代码,这样也就符合了开闭原则。
单一职责原则(Single Function Principle,SFP)又称单一功能原则,它规范设计一个类应只实现单一或独立的职责即业务功能。职责分明的设计,可以提高程序的可维护性。如果多个独立的业务功能设计在同一类中,当一个或多个业务功能扩展或变动需要修改代码时,可能会带来冲突问题,降低可维护性。比如订单服务中,又包含了用户服务,重构用户服务的代码时,相当于重构订单服务的代码,这是不合理的,容易造成混淆。
接口隔离原则(Inteface Segregation Principle,ISP)指设计时不同的功能应定义在不同的接口上,避免实现类依赖不需要的接口,换个角度理解就是一个类应该依赖尽量少的接口或实现类,减少程序的冗余性和复杂性。
组合/聚合复用原则(Composition/Aggregation Principle,CARP)也叫做合成复用原则(Composition Reuse Principle,CRP),它指在一个新的对象中引入/注入现有的对象以达到功能复用和扩展的目的。简单讲就是尽量使用组合/聚合而不要使用继承。
迪米特法则(Low of Demeter,LoD)指设计一个对象时,尽可能少的与其它对象发生相互作用,即尽可能少的了解或依赖其它对象,降低耦合度。广义的迪米特法则,在类设计的具体操作上,主要通过以下三点来体现