目录
[TOC]
SOLID
由5个原则组成
S(单一职责)
英文:Single Responsibility Principle
如何判断是否满足单一:
主观判断
- 不同应用场景、不同需求阶段下,判断的依据不同,需要动态分析
客观判断(也有很大主观性)
- 类、属性、函数、代码过多(过多这个词就有很大主观性,需要分析和一定经验)
- 依赖过多、私有方法过多
- 类很难取名或者类中的大量方法都是操作某几个属性
O(开闭)
英文:Open Closed Principle
如何理解和实现“多扩展开放,对修改关闭”:
- 代码可以让其他人不通过修改原有代码,就能够增加新的功能,这就是扩展性强的表现;平时多注意和学习扩展、抽象、封装这些知识,常说的设计模式,很多也是为了满足开闭而出现的
L(里式替换)
英文:Liskov Substitution Principle
在继承关系中,子类的设计要满足可以替换掉父类,且不改变原有的逻辑和正确性
I(接口隔离)
英文:Interface Segregation Principle
调用者不应依赖到不需要的接口
D(依赖反转)
英文:Inversion Of Control
- 控制反转:原来由自己控制整个流程执行,交给框架后由框架来进行控制
- 依赖注入:不通过new创建对象,而通过构造函数或函数传参等方式传递给类使用
- 依赖反转:高层模块不应依赖底层模块,而是应该通过抽象进行互相依赖,抽象不应依赖具体实现细节,具体实现细节依赖抽象
KISS
英文:Keep It Short and Simple
尽量保持简单
YAGNI
英文:Yor Ain't Gonna Need It
不要过度设计
DRY
英文:Don't Repeat Yourself
不要写重复代码
LOD
英文:The Least Knowledge Principle
迪米特法则/最小知识原则:
不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口
设计模式
创建型(4)
单例模式
饿汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class IdGenerator { private AtomicLong id = new AtomicLong(0); private IdGenerator() { } private static final IdGenerator instance = new IdGenerator(); public static IdGenerator getInstance(){ return instance; } public long getId(){ return id.incrementAndGet(); } }
|
优点:可以将初始化提前,帮助提早发现问题
懒汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class IdGenerator { private AtomicLong id = new AtomicLong(0); private IdGenerator() { } private static IdGenerator instance = null; public static synchronized IdGenerator getInstance(){ if( instance == null){ return new IdGenerator(); } return instance; } public long getId(){ return id.incrementAndGet(); } }
|
懒汉式(双重检测)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class IdGenerator { private volatile AtomicLong id = new AtomicLong(0); private IdGenerator() { } private static IdGenerator instance = null; public static IdGenerator getInstance(){ if( instance == null){ synchronized (IdGenerator.class){ if(instance == null){ return new IdGenerator(); } } } return instance; } public long getId(){ return id.incrementAndGet(); } }
|
优点:锁的范围更小,并发量更大
上面的id
加上了volatile
修饰符,作用是防止指令重排序,参数 = new 对象()
有三步
- 分配空间(对象的大小)
- 空间赋值(对象的内容)
- 将空间地址赋值给参数(对象的空间地址给参数)
而在实际执行过程中,可能1直接到3,然后在2执行前,就使用了其中的方法,导致运行错误
新版本JDK内部已经将1、2、3进行了原子化处理,不用再加volatile
修饰符了
静态内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class IdGenerator { private volatile AtomicLong id = new AtomicLong(0); private IdGenerator() { } private static class SingleHolder{ private static IdGenerator instance = new IdGenerator(); } public static IdGenerator getInstance(){ return SingleHolder.instance; } public long getId(){ return id.incrementAndGet(); } }
|
优点:和懒汉式(双重检测)
功能一样,但是更简洁
枚举类
1 2 3 4 5 6 7
| public enum IdGenerator { INSTANCE; private AtomicLong id = new AtomicLong(0); public long getId(){ return id.incrementAndGet(); } }
|
优点:更简洁!
枚举类实现单例的原理
工厂模式
简单工厂
例如:设计一个消息发送功能模块,根据不同行为,创建不同的发送服务进行调用
前置步骤-创建接口
1 2 3
| public interface SendInterface { void send(String receiver,String message); }
|
前置步骤-创建不同实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class EmailService implements SendInterface{ @Override public void send(String receiver, String message) { System.out.println("发送给"+receiver+",信息:"+message); } }
public class InnerService implements SendInterface{ @Override public void send(String receiver, String message) { System.out.println("发送给"+receiver+",信息:"+message); } }
public class SmsService implements SendInterface{ @Override public void send(String receiver, String message) { System.out.println("发送给"+receiver+",信息:"+message); } }
|
前置步骤-逻辑处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Notifacation { public void send(String type,String receiver,String message){ SendInterface sendInterface = null; if("email".equals(type)){ sendInterface = new EmailService(); }else if("sms".equals(type)){ sendInterface = new SmsService(); }else if("inner".equals(type)){ sendInterface = new InnerService(); }else { throw new RuntimeException("发送类型错误"); } sendInterface.send(receiver,message); } }
|
上面代码是我实际处理消息模块的实现思路,根据不同的type
动态调用不同的实现类
简单工厂-不带缓存
上面逻辑处理
的部分,可以将对象的创建独立出来,就变成简单工厂的方式了,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class Notifacation { public void send(String type,String receiver,String message){ SendInterface sendInterface = createSendInterfaceFactory(type); sendInterface.send(receiver,message); } private SendInterface createSendInterfaceFactory(String type){ if("email".equals(type)){ return new EmailService(); }else if("sms".equals(type)){ return new SmsService(); }else if("inner".equals(type)){ return new InnerService(); }else { throw new RuntimeException("发送类型错误"); } } }
|
简单工厂-带缓存
上面处理每次调用会创建新的对象,调用比较频繁会占用大量资源,可以创建后缓存起来,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Notifacation { private static final Map<String,SendInterface> cacheMap = new HashMap<>(); static { cacheMap.put("email",new EmailService()); cacheMap.put("sms",new SmsService()); cacheMap.put("inner",new InnerService()); } public void send(String type,String receiver,String message){ SendInterface sendInterface = createSendInterface(type); sendInterface.send(receiver,message); } private SendInterface createSendInterface(String type){ return cacheMap.get(type); } }
|
工厂方法
作用:将创建实现类抽离出去
创建工厂方法接口
1 2 3
| public interface SendInterfaceFactory { SendInterface createSendInterface(); }
|
创建实现类工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class EmailServiceFactory implements SendInterfaceFactory{ @Override public SendInterface createSendInterface() { return new EmailService(); } }
public class InnerServiceFactory implements SendInterfaceFactory{ @Override public SendInterface createSendInterface() { return new InnerService(); } }
public class SmsServiceFactory implements SendInterfaceFactory{ @Override public SendInterface createSendInterface() { return new SmsService(); } }
|
逻辑处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class Notifacation { public void send(String type,String receiver,String message){
SendInterfaceFactory sendInterfaceFactory = null; if("email".equals(type)){ sendInterfaceFactory = new EmailServiceFactory(); }else if("sms".equals(type)){ sendInterfaceFactory = new SmsServiceFactory(); }else if("inner".equals(type)){ sendInterfaceFactory = new InnerServiceFactory(); }else { throw new RuntimeException("发送类型错误"); } SendInterface sendInterface = sendInterfaceFactory.createSendInterface(); sendInterface.send(receiver,message); } }
|
可以看到,这里只是将类的创建抽象出去,但是没有干掉if-else
逻辑处理(优化-去掉if-else)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Notifacation { private static final Map<String,SendInterfaceFactory> mapFactory = new HashMap<>(); static { mapFactory.put("email",new EmailServiceFactory()); mapFactory.put("sms",new SmsServiceFactory()); mapFactory.put("inner",new InnerServiceFactory()); } public void send(String type,String receiver,String message){ SendInterfaceFactory sendInterfaceFactory = createSendInterfaceFactory(type); SendInterface sendInterface = sendInterfaceFactory.createSendInterface(); sendInterface.send(receiver,message); } private SendInterfaceFactory createSendInterfaceFactory(String type){ return mapFactory.get(type); } }
|
抽象工厂
作用:对多种情况下的优化,例如出现需要根据配置文件的方式进行消息的发送
1 2 3 4 5
| public interface SendInterfaceFactory { SendInterface createSendInterface(); SendConfigInterface createSendConfigInterface(); }
|
增加后,所有工厂类需要实现此抽象方法,即每个工厂类都可以创建两个处理相同事情,但是不同的处理方式的实现类
建造者模式
解决了什么问题
原代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class Person { private String name; private Integer age; private String address; private Boolean isStudent; public Person(String name, Integer age, Boolean isStudent) { this.name = name; this.age = age; this.isStudent = isStudent; } public String getAddress() { return address; } public void setAddress(String address) { if(address == null || address.length() > 20){ throw new RuntimeException("地址参数设置错误"); } this.address = address; } }
|
对于一般实体类的属性值设置,采取的方式是:1.必传字段通过构造函数传入
2.非必填字段通过
存在的问题:
- 如果必传字段很多,则构造函数会太长,而如果不通过构造函数传入,而是通过set方法赋值,则在没调用set方法的情况下,必传字段无法进行校验
- 如果属性值有依赖关系,例如
isStudent
是true,则必须传地址,也没有办法处理
- 如果要求对象是不可变对象时,即创建后,不能暴露出set方法
通过建造者模式优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| public class Person { private String name; private Integer age; private String address; private Boolean isStudent;
public Person(PersonBuilder personBuilder) { this.name = personBuilder.name; this.age = personBuilder.age; this.address = personBuilder.address; this.isStudent = personBuilder.isStudent; }
public static class PersonBuilder{ private static final Boolean DEFAULT_IS_STUDENT = true; private String name; private Integer age; private String address; private Boolean isStudent = DEFAULT_IS_STUDENT;
public Person build(){ if(this.isStudent == true){ if(address == null){ throw new RuntimeException("学生必须有地址"); } } return new Person(this); }
public PersonBuilder setName(String name) { if(!StringUtils.hasText(name)){ throw new RuntimeException("姓名必须不为空"); } this.name = name; return this; }
public PersonBuilder setAge(Integer age) { if(age == null){ throw new RuntimeException("年龄必须不为空"); } this.age = age; return this; }
public PersonBuilder setAddress(String address) { this.address = address; return this; }
public PersonBuilder setStudent(Boolean student) { isStudent = student; return this; } }
}
|
使用:
1 2 3 4 5 6
| Person person = new Person.Builder() .setName("张三") .setAge(18) .setStudent(true) .setAddress("我有地址") .build();
|
lombok形式
再次优化成lombok的注解@Builder
的形式
在Person
类中增加如下方法
1 2 3
| public static Person.PersonBuilder builder(){ return new Person.PersonBuilder(); }
|
使用:
1 2 3 4 5 6
| Person person = Person.builder() .setName("张三") .setAge(18) .setStudent(true) .setAddress("我有地址") .build();
|
可以看到,只要再给静态内部类的set相关方法名换一下,就可以做到和lombok的@builder注解完全一致的效果
@Accessors的使用
Accessors
注解有三个属性
fluent
:会将set前缀去掉,例如原来方法名是setName
,加上后就变成name
chain
:set方法将返回this代替原本的void
使用@Accessors
和@Setter
配合可以很好的简化原来静态内部类的代码,如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class Person { private String name; private Integer age; private String address; private Boolean isStudent;
public Person(PersonBuilder personBuilder) { this.name = personBuilder.name; this.age = personBuilder.age; this.address = personBuilder.address; this.isStudent = personBuilder.isStudent; } public static Person.PersonBuilder builder(){ return new Person.PersonBuilder(); } @Setter @Accessors(fluent = true,chain = true) public static class PersonBuilder{ private static final Boolean DEFAULT_IS_STUDENT = true; private String name; private Integer age; private String address; private Boolean isStudent = DEFAULT_IS_STUDENT;
public Person build(){ if(this.isStudent == true){ if(address == null){ throw new RuntimeException("学生必须有地址"); } } return new Person(this); } } }
|
简化后使用方式:
1 2 3 4
| Person person = Person.builder() .name("张三") .age(18) .build();
|
原型模式
说明
利用已有对象进行复制的方式,来创建新对象,达到节省空间及时间的方式,叫做原型模式,需要理解下浅拷贝和深拷贝。在复制时可以采取浅拷贝老对象得到新对象,新对象指向老对象指向的的散列值,然后新对象对需要更新的内部值进行移除,再添加(移除指向散列值的引用,再添加新引用,则不会影响到老对象指向的散列值),而如果直接修改,会对老对象指向的散列值造成影响,不可取。
结构型(7)
代理模式
作用:将业务代码与非业务代码进行分离
常用场景:监控、统计、鉴权、限流、事务、幂等
详细介绍请点击此处
桥接模式
- 理解方式一:
将抽象和实现解耦,让它们可以独立变化
- 理解方式二:
通过组合代替继承关系,避免继承层次的指数级爆炸
实例
JDBC驱动实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://119.91.255.95/blog?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true";
Connection connection = DriverManager.getConnection(url);
Statement statement = connection.createStatement();
String queryStr = "select * from user";
ResultSet resultSet = statement.executeQuery(queryStr);
while (resultSet.next()){ resultSet.getString(1); resultSet.getInt(2); }
|
装饰器模式
适配器模式
门面模式
组合模式
享元模式
行为型(11)
观察者模式
模板模式
策略模式
职责链模式
状态模式
迭代器模式
访问者模式
备忘录模式
命令模式
解释器模式
中介模式