设计模式六大原则
设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
任何不可维护的代码,都是等待过时的代码。设计模式就是从长期开发中总结出来的,用以提高类的内聚性、降低类间的耦合性、提高代码的可扩展性和可维护性的方法。
职责单一原则
-
核心
每个类都应该有一个单一的功能,并且该功能应该由这个类完全封装起来。也就是高内聚。 -
简单的例子
public interface UserService { public void login(String username, String password); public void register(String email, String username, String password); public void logError(String msg); public void sendEmail(String email); }
这段代码很显然存在很大的问题,UserService 既要负责用户的注册和登录,还要负责日志的记录和邮件的发送,并且后者的行为明显区别于前者。这就相当于一个程序员既要编代码,中午还要给公司全体员工做午饭,并且公司的卫生也由他负责。
-
进行更改
UserService:
public interface UserService { public void login(String username, String password); public void register(String email, String username, String password); }
LogService:
public interface LogService { public void logError(String msg); }
EmailService:
public interface EmailService { public void sendEmail(String email); }
-
好处
职责单一原则给我带来最直观的感受是:类的复杂度降低了,并且,当我们想发邮件却不知道实现哪个类的时候,这种设计模式可以快速的帮我们定位到具体哪个类可以实现这个功能。
开闭原则
-
核心
软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的。 -
简单的例子
Rectangle:
// 矩形 public class Rectangle { public double getWidth() { return width; } public double getHeight() { return height; } }
AreaCalculator:
// 面积计算器 public class AreaCalculator { public double area(Rectangle shape){ return shape.getWidth() * shape.getHeight(); } }
上面代码完全可以完成矩形面积的计算,但是,这时有一个新的需求,让我们计算圆形的面积,我们可以这样更改 AreaCalculator 代码,来满足这个需求:
Circular:
// 圆形 public class Circular { public double getRadius(){ return radius; } }
更改后的 AreaCalculator:
public class AreaCalculator { public double area(Object shape){ if(shape instanceof Rectangle) { Rectangle rectangle = (Rectangle) shape; return rectangle.getWidth() * rectangle.getHeight(); } else if (shape instanceof Circular) { Circular circular = (Circular) shape; return circular.getRadius() * circular.getRadius() * Math.PI; } else { throw new RuntimeException("There is no such type."); } } }
这么更改完成,完全没有问题。但是在真实的生产环境中,情况更为复杂,更改涉及的部分较多,那样就可能导致牵一发动全身。并且,以前编写的经过测试的一些功能需要重新测试,甚至导致某些功能不可用。
-
进行改进
Shape:
public interface Shape { public double area(); }
Rectangle:
public class Rectangle implements Shape{ public double getWidth() { return width; } public double getHeight() { return height; } public double area() { return getWidth() * getHeight(); } }
这样,当需求变更,需要计算圆形面积的时候,我们只需创建一个圆形的类,并实现 Shape 接口即可:
public class Circular implements Shape { public double getRadius(){ return radius; } public double area() { return getRadius() * getRadius() * Math.PI; } }
计算三角形面积、四边形面积... 的时候,我们只需让它们去实现 Shape 接口即可,无需修改源代码。
-
好处
一般编写完的代码,都是经过精心设计和测试过的,如果我们对其进行修改,就需要重新测试,这个测试可能涉及到所有依赖这个方法的类,如果大量修改源代码的话,那就是个可观的工程。
所以,开闭原则可以在保证我们代码的质量的前提下,实现功能的扩展,减少开发难度。
里氏替换原则
- 核心
在程序里,把父类都换成它的子类,程序的行为没有变化。 - 思考
里氏替换原则是开闭原则的基石,正是因为里斯替换原则才可以在不修改父类源码的情况下,实现功能的扩展。
如果子类无法实现父类的全部功能,比如鸟类有个 fly() 方法,企鹅也是鸟类的一种,但是企鹅如果继承鸟类的话,就必须得实现 fly() 方法,这时就产生了一种"畸形",这种情况应该断开继承关系。如果强行继承的话,子类并无法完全替代父类,程序就有可能因此出现故障,比如一只企鹅在天上翱翔。
依赖倒置原则
-
核心
针对借口编程,不针对实现编程。 -
简单的例子
IntelCPU:
public class IntelCPU { public int add(int a, int b) { return a + b; } }
Mainboard:
// 主板 public class Mainboard { // 装配英特尔 CPU public void setCPU(IntelCPU cpu) { this.cpu = cpu; } }
当某一天,CPU 需要更换的时候,我们只能装配英特尔 CPU。
虽然这个例子比较简单,但是在实际的开发中,我们经常会被眼前的需求所蒙蔽,而不去思考拓展性,导致每次来个新需求,都要违背开闭原则。 -
进行改进
CPU:
public interface CPU { public int add(int a, int b); }
IntelCPU:
public class IntelCPU implements CPU { public int add(int a, int b) { return a + b; } }
AmdCPU:
public class AmdCPU implements CPU { public int add(int a, int b) { return a + b - b + b; } }
Mainboard:
public class Mainboard { public void setCPU(CPU cpu) { this.cpu = cpu; } }
- 好处
依赖倒置原则的好处很明显,当需求变更的时候,我们可以很灵活的进行扩展,而不用破坏开闭原则。
接口隔离原则
-
核心
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。 -
思考
这个原则跟单一职责原则很像,都是为了精细化管理,将功能尽可能的细化。这样,当某个功能出现问题的时候,可以快速的定位问题,并且在最小范围内修复,功能的扩展也是一样的。
但是,接口也不能无限的小,那样会产生大量的接口,造成设计过于负责。
迪米特法则
-
核心
降低类间的耦合性,如果两个类不必彼此通信,那么,这两个类就不要发生直接的作用。 -
简单的例子
Phone:
public class Phone { public void seeMovie(Movie movie) { String title = movie.getTitle(); long totalTime = movie.getTotalTime(); // ... } }
电影和我们的电话并没有直接的关系,这两个类的耦合度过高,也就是,电话类需要知道电影类的具体实现细节,这两个类之间没有必要进行直接的通信。
-
进行改进
MovieApp:
public interface MovieApp { public void seeMovie(Movie movie); }
Phone:
public class Phone { private MovieApp movieApp; public void setMovieApp(MovieApp movieApp) { this.movieApp = movieApp; } public void seeMovie(Movie movie) { movieApp.seeMovie(movie); } }
-
好处
低耦合、低耦合、低耦合...
网友评论