前言
不知道有没有小伙伴跟我一样,觉得设计模式并没有多么重要。
但是你们有没有遇到过这样一种情况:
当我们在功能迭代的时候,涉及到功能的变更和扩展的时候,看到自己或者别人的代码实现全部都耦合在一起的时候,会不会感受到一点头大呢。
介绍
举个现实中例子
我们建一套房子,有卧室,有厨房有卫生间等等,我们是不是在建造的时候需要设计一张图纸,如何在一个有效的空间里将这些更加合理的建造。如何不进行设计,这样的房子你愿意住进去么。可想而知,设计模式的重要性
当然设计模式也不是想当然,没有任何原则去进行设计的。
设计模式遵循这七大原则
- 单一职责
- 接口隔离原则
- 依赖倒转原则
- 开闭原则
- 里氏替换原则
- 迪米特原则(最少知道原则)
- 合成复用原则
七大原则
单一职责
定义
一个类或一个接口只负责一个职责,当需求需要将现有的职责划分为粒度更小的职责的时候,应该及时代码重
目的
最主要的目的就是降低类的复杂度
注意点
只有逻辑足够简单、才可以在代码级违反单一职责原则,只有类中的方法足够少,才可以在类中保持方法级别的单一职责原则,否则都要遵循类级别的单一职责原则
接口隔离原则
定义
客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。
简单来说,就是一个实现类不要实现他根本不需要的接口,保证一个最小接口
目的
避免接口过度臃肿,存在不需要的接口方法
举个例子
我们先来定义一个交通工具的接口,众所周知,交通工具有在天上飞的,地上跑的,水里游的。
interface Vehicle{
void fly();
void run();
void swim();
}
再来定义一个比亚迪(是不是要给我打广告费v)的汽车实现交通工具的接口,那么就要实现这个接口的所有方法
class BYD implements Vehicle{
@Override
public void fly() {
System.out.println("i am car,can't fly");
}
@Override
public void run() {
System.out.println("比亚迪跑起来");
}
@Override
public void swim() {
System.out.println("i am car,can't swim");
}
}
这时候大家有没有发现什么问题:
我们实现的接口有天上飞和水里游的,而BYD他只是一个车,根本就没有这些功能,目测二十年之内也不会有这些功能,所以我们需要重新设计来保证我们接口的隔离性原则
我们重新拆分细化一下接口,分别定义Car,Plane,Submarine接口
interface Car{
void run();
}
interface Plane{
void fly();
}
/*interface Submarine{
void swim();
}*/
再来他们对应的实现(就实现两个吧)
class BoYin implements Plane{
@Override
public void fly() {
System.out.println("开波音飞机");
}
}
class Geely implements Car{
@Override
public void run() {
System.out.println("吉利跑起来");
}
}
定义两个依赖上述接口的对象
class Pilot{
private String name;
public Pilot(String name) {
this.name = name;
}
public void drive(Plane plane){
System.out.print(name + ":");
plane.fly();
}
}
class Worker{
private String name;
public Worker(String name) {
this.name = name;
}
public void drive(Car car){
System.out.print(name + ":");
car.run();
}
}
最后代码测试一下结果
public static void main(String[] args) {
new Worker("工人").drive(new Geely());
new Pilot("飞行员").drive(new BoYin());
}
这样就简单实现了对象依赖的是最小接口,不需要实现不需要的接口
注意事项和细节
如果接口定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。所以我们需要根据业务情况去定义接口的粒度
依赖倒转原则
定义
简单来讲,就是对于类的依赖应该建立在高层模块,细节应该依赖抽象,也就是其的抽象或接口(面向接口编程)
目的
制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。也就是增加其扩展性能,利于程序扩展和优化。
几种写法
- 声明依赖对象
- 构造函数传递依赖对象
- setter方法传递依赖对象
举个例子
之前项目的例子:
有一个产品有很多状态,并且需求还在不断的增加状态,每种状态还有不同的处理逻辑,这个时候如果把所有的状态都丢在一个类的一个方法进行相关处理显然就很不合适,那这个时候就可以用到这个依赖倒转设计模式了
定义一个产品
@Data
@Builder
class Product {
private String name;
private Integer status;
private String other;
}
定义一个状态处理的接口
interface StatusHandle{
String doService();
}
生产状态处理实现类
class ProductionStatusHandleImpl implements StatusHandle{
@Override
public String doService() {
return "正在生产中";
}
}
出库状态处理实现类
class OutboundStatusHandleImpl implements StatusHandle{
@Override
public String doService() {
return "正在出库中";
}
}
定义产品处理方法,之后增加状态的处理我们只需要增加接口的实现类就行了,大大增加了系统的扩展性
private static String boxHandleService(Product product) {
StatusHandle statusHandle;
// 这块可以把他维护成一个map,每种状态对应一个处理实现类,通过状态获取对应的状态处理实现类
if (product.getStatus() == 1){
statusHandle = new ProductionStatusHandleImpl();
}else {
statusHandle = new OutboundStatusHandleImpl();
}
// ...............................................................
return boxStatusHandleService(statusHandle);
}
/**
* 将所有状态的处理交给接口,每个状态都分别对接口进行不同的实现,之后增加状态也只需要增加实现类
* @param statusHandle
* @return
*/
private static String boxStatusHandleService(StatusHandle statusHandle) {
return statusHandle.doService();
}
写个main方法测试下
public static void main(String[] args) {
System.out.println("小黑盒" + boxHandleService(Product.builder().name("小黑盒").status(1).build()));
System.out.println("小黑盒" + boxHandleService(Product.builder().name("小黑盒").status(2).build()));
}
注:这块的功能也可以用责任链模式做,之后咱们再说
注意点
继承时遵循里氏替换原则
预告
由于篇幅有限,下期说剩余的设计原则把