20

面向对象编程的十大原则

 3 years ago
source link: http://developer.51cto.com/art/202006/618426.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【51CTO.com快译】

众所周知,面向对象的设计原则(Object-Oriented Design Principles)是面向对象编程(OOP)的核心。但是,如今有许多Java程序员在追求诸如 SingletonDecoratorObserver 等设计模式的同时,却忽略了面向对象的分析和设计。我们除了要学习诸如抽象、封装、多态和继承之类的基础知识,还需要了解面向对象的设计原则。据此,我们可以创建出简洁的模块化设计,以便后期轻松地开展测试,调试和维护。

不知您是否听说过OOP的SOLID设计原则(请参见-- https://javarevisited.blogspot.com/2018/02/top-5-java-design-pattern-courses-for-developers.html )?作为一种面向对象设计原则,它具体包括如下十个部分。

iYrYJzY.png!web

1.DRY(Don’t repeat yourself)

顾名思义,DRY表示不要编写重复的代码,应尽量使用抽象类(Abstraction)来抽象出目标事物。例如:如果您有两个以上的代码块,那么就应当考虑使其成为一个单独的方法。如果您多次用到某个硬编程(hard-coded)的值,那么就应当将它们设为public final constant(请参见-- http://javarevisited.blogspot.com/2011/12/final-variable-method-class-java.html )。显然,这样的面向对象设计原则将有益于后期的维护。

不过,在您使用标准化的代码去验证OrderId和SSN(译者注:美国社会安全号码),这两种不同的功能或事物时,您需要避免将它们联系得过于紧密,否则当OrderId更改其格式时,SSN的验证代码将会受阻。这就是我们常说的耦合。请不要合并任何使用了相似代码,但并实际联系的事物。您可以在Udemy(译者注:一家开放式的在线教育网站)上的“Java课程”中进一步学习 《软件架构和设计模式的基础知识》 ,以了解有关编写正确的代码和在设计系统时,需要遵循的最佳实践。

2.封装变更(Encapsulate What Changes)

资深码农经常会仰天长叹:“在软件领域,唯一不变的就是变化!”可见,您应当尽量封装那些您期望,或可能在将来变更的代码。此举的好处同样是易于后期的测试与维护。

如果您使用Java进行编程,那么请在默认情况下将各种变量和方法设为私有,之后可逐步增加访问权限。同时在Java中,有不少设计模式都使用到了封装。其中Factory设计模式(Factory design pattern,请参见--http://javarevisited.blogspot.com/2011/12/factory-design-pattern-java-example.html)就是一个很好的例子。它通过封装对象的代码创建,提供了在不影响现有代码的情况下,在后期引入新功能的灵活性。

在Pluralsight(译者注:一个软件开发的在线教育网站平台)上的《设计模式库》课程(请参见-- https://pluralsight.pxf.io/c/1193463/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fpatterns-library ),可谓最好的设计模式集。它同时提供了有关如何在真实环境中使用的建议。

3.开放式封闭设计原则(Open Closed Design Principle)

根据OOP的设计原则:“类、方法或功能都应当为新功能的扩展而开放,但是要为修改而封闭(防止他人更改已经测试过的代码)。”在理想情况下,我们只需要测试那些能够带来新功能的程序代码。这便是开放式封闭设计原则的目标。此处的Open-Closed正是SOLID设计原则中的字母“O”的缩略。

我们来看一个违反“开放式封闭设计原则”的Java示例:在某段代码中,GraphicEditor与Shape紧密结合,如果需要新的Shape,则需要在drawShape(Shape s)方法的内部,修改已通过测试的系统。显然,这样既容易出错,也不可取。

您可以在Udemy那里学习到 《面向对象设计和架构的SOLID原则》 的相关课程,以加深对该原则的理解。

4.单一责任原则(Single Responsibility Principle,SRP)

单一责任原则要求:导致类发生变更的原因不应多于一个,或者说在一个级别上应始终只实现一项功能。其好处在于:有效地减少了软件的各个组件与代码之间的耦合。此处的SRP便是SOLID设计原则中的字母“S”的缩写。

例如,如果您在Java的一个类中设置了一个以上的功能,那么就会导致两个功能之间产生耦合。只要您更改了一个功能,那么就可能破坏耦合,进而引发新的一轮测试,以避免在生产环境中出现任何异常。

您可以在Udemy那里学习到 《从0到1:设计模式》 的相关课程,以加深对该原则的理解。

5.依赖性注入或反转原则(Dependency Injection or Inversion Principle)

该原则要求:不要自行增加依赖性,请把它交给框架。例如:作为编写实际应用最流行的Java框架之一,Spring框架就提供了各种依赖性。该设计原则的优点在于:由DI框架注入的任何类,都易于使用模拟对象来进行测试,且易于后期维护。由于对象的创建代码集中于框架之中,因此客户端的代码则不会被散落在各处。该此Dependency Injection原则便是SOLID中字母“D”的缩写。

我们可以用多种方法来实现依赖项注入,例如:使用似于AspectJ的面向切面编程(Aspect Oriented Programming,AOP)框架,进行字节码检测;或通过使用Spring中的各种代理来实现。

我们来看一个违反了依赖性注入原则的Java代码示例:EventLogWriter与AppManager有着紧密的耦合关系。如果您需要使用其他的方式(例如:通过推送短信或邮件通知)来通知客户端的话,则需要更改AppManager类。为此,我们可以通过使用依赖关系反转原则,来予以解决。也就是说,为了避免AppManager去请求EventLogWriter,我们可以使用由框架注入或提供的AppManager。

您可以在Udemy那里学习到 《使用SOLID原则写更好的代码—速成班》 的相关课程,以加深对该原则的理解。

6. 优先使用(对象)组合,而非(类)继承(Favor Composition over Inheritance)

继承和合成是两种通用的方法,可用于重用已编写好的代码。两者虽然各有优、缺点,但是如果可能的话,您应当尽量使用组合而不是继承。毕竟组合比继承要灵活得多。

此处的组合是指:通过设置属性,以更改运行时(run-time)类的行为,并使用各种接口来组成一个类。这就是我们常说的多态性( polymorphism )。它可以随时、且灵活地替换成为更好的实现方式。

如果您有兴趣学习更多有关组合、继承、关联、聚合等面向对象编程的概念和知识,请在Coursera(译者注:一个与世界顶尖大学合作的大型公开在线课程项目)上的 《Java面向对象编程》 课程中深入学习。

7. Liskov替代原则(Liskov Substitution Principle,LSP)

根据Liskov替换原则,子类型必须可以替代父类型。也就是说,那些使用父类型的方法或函数,必须能够与子类的对象无障碍地协作。即:派生类或子类必须在父类的基础上增强功能,而不是减少功能。相反,如果一个类具有比其子类更多的功能,那么它就不应该支持这些更多的功能,也就是违反了LSP。其实,LSP与单一职责原则(Single responsibility principle)、以及接口隔离原则(Interface Segregation Principle,见下文)有着密切的相关性。而且,LSP正是SOLID中字母“L”的缩写。

我们来看一个违反了Liskov替换原则的Java代码示例:如果您设计了一个area(Rectangle r)方法来计算Rectangle的面积,那么当您传入Square时,由于Square并非真正的Rectangle,该代码就会产生中断。

如果您对更多真实环境的示例感兴趣的话,请在Pluralsight上的 《面向对象设计的SOLID原则》 课程中进行深入学习。

8.接口隔离原则(Interface Segregation Principle,ISP)

接口隔离原则规定:如果一个接口包含了多个功能,而某个客户端只需要其中的一项功能,那么我们就不应该去实现那些用不到的接口。

毫无疑问,接口设计是一项比较棘手的工作。毕竟我们一旦发布了某个接口,就无法在不破坏其现有实现的情况下,对其进行更改。ISP在Java中的另一个好处是:如果不同的类都需要使用某个接口去实现各种方法的话,不如用更少的方法去实现单一的功能。

如果您对接口编程感兴趣的话,请参考博文-- 《Java接口的实战用法》 ,以了解更多的信息。

9.为接口编程,而非为实现编程(Programming for Interface not implementation)

程序员应该始终为接口编程,而不是为实现而编程。根据该原则所创建的代码,将能够灵活地被用于接口的任何一种新的实现上。

确切地说,我们应该在各种变量上使用接口类型、方法的返回类型、以及类似于Java的参数类型。例如:您可以使用SuperClasstype来存储对象,而不必使用SubClass。同时,您可以用List numbers= getNumbers();来代替ArrayList numbers = getNumbers();。当然,诸如:《Java高效编程(Effective Java)》(请参见-- https://www.amazon.com/Effective-Java-3rd-Joshua-Bloch/dp/0134685997/?tag=javamysqlanta-20 )和《入浅出的设计模式(Head First design pattern)》(请参见-- http://www.amazon.com/dp/0596007124/?tag=javamysqlanta-20 )之类的Java书籍也是这么建议的。

如果您对提高程序的代码质量感兴趣的话,我建议您学习Udemy上的 《设计模式重构》 课程。该课程将教会您如何使用C#中的重构技术,以及设计模式来改进内部设计。

10.委托原则(Delegation Principles)

委托原则建议:请不要自己做所有的事,要学会将不同的实现委托给相应的类。该原则的经典示例是Java中的equals()和hashCode()方法(请参见-- http://javarevisited.blogspot.com/2011/02/how-to-write-equals-method-in-java.html )。事件委托(Event delegation)则是另一种示例,它将事件委托给处理程序(handlers)进行处理。可见,此设计原则的主要好处是:无需重复编写代码,便可轻松地修改程序的行为。

总结与其他资源

可以说,上述所有面向对象的设计原则,都会有利于程序代码的高内聚和低耦合性,也都有助于您编写出灵活、简洁的代码。您需要通过反复的练习,来实践这些理论原则,进而解决应用程序开发和软件工程中的各种常见问题。

与此同时,您可以从Apache和Google处寻找各种开源代码,以便学习Java和OOP的设计原则。另外,Java开发工具包(Java Development Kit,JDK)也包含有许多设计原则,例如:BorderFactory类中的Factory模式(请参见-- http://javarevisited.blogspot.sg/2011/12/factory-design-pattern-java-example.html#axzz51cvxH5kW )、java.lang.Runtime类中的Singleton模式(请参见-- https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html )、以及各种java.io类中的Decorator模式(请参见-- http://www.java67.com/2013/07/decorator-design-pattern-in-java-real-life-example-tutorial.html )。

如果您对于学习面向对象的原则和模式兴趣不减的话,我推荐您阅读《深入浅出学习面向对象的分析和设计( Head First Object-Oriented Analysis and Design )》一书。

当然,如果您想了解更多有关SOLID设计原则的具体内容,请参考如下实用的资源:

原标题:10 Coding Principles Every Programmer Should Learn ,作者:Javin Paul

【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】

【责任编辑:庞桂玉 TEL:(010)68476606】


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK