面向对象六大原则
指导思想
可维护性(Maintainability)
修改功能,需要改动地方越少,可维护性越好
可复用性(Reusability)
代码可以被重复使用,总结类库
可扩展性(Extensibility/Scalability)
添加功能无需修改原来代码
灵活性(Flexibility/Mobility/Adaptability)
代码接口可以灵活调用
原则
单一职责原则
一个类别太大,别太累,负责单一职责,高内聚地耦合
开闭原则
对扩展开放,对修改关闭,尽量不修改原来代码进行扩展,抽象多态是开闭原则的关键
里氏替换
所有使用父类的地方,必须能够透明的使用子类对象(子类可以完全替代父类)
依赖倒置原则
面向接口(抽象)编程,而不是依赖具体
接口抽离原则
每一个接口承担独立的角色,避免子类实现不需要的方法,提供接口暴露只需最小接口(只暴露用到的接口)
迪米特法则
对于一个对象,不要和联系不大的对象耦合,尽量只用自己的东西,低耦合
设计模式
1.单例模式(Singleton)
应用场景:只需要有一个实例存在
创建方法:(前提:将构造方法定义为private,然后提供获取实例的方法)
饿汉式,定义个static的实例直接初始化。推荐使用。
1
private static final SingletonObj INSTANCE = new SingletonObj();
线程安全,jvm保证,类加载时候就会实例化。
静态语句块初始化,和第一种一样
1
2
3
4private static final SingletonObj INSTANCE;
static {
INSTANCE = new SingletonObj();
}懒汉式,用的时候才初始化
1
2
3
4
5
6
7
8private static final SingletonObj INSTANCE;
public static SingletonObj getINSTANCE {
//并发时第一次可能new多个实例
if(INSTANCE == null){
INSTANCE = new SingletonObj();
}
return INSTANCE;
}针对3的并发问题,通过加synchronized加方法上控制并发
1
2
3
4
5
6
7
8private static final SingletonObj INSTANCE;
//方法上加锁导致并发效率降低
public static synchronized SingletonObj getINSTANCE {
if(INSTANCE == null){
INSTANCE = new SingletonObj();
}
return INSTANCE;
}针对第4种效率问题,创建对象时加锁,双判断,很多人觉得是最完美写法。
1
2
3
4
5
6
7
8
9
10
11
12
13//此处要加volatile保持可见性
private static final volatile SingletonObj INSTANCE;
public static synchronized SingletonObj getINSTANCE {
if(INSTANCE == null){
synchronized(XXX.class){
//此处如果不加判断,也会导致可能产生多个实例
if(INSTANCE == null){
INSTANCE = new SingletonObj();
}
}
}
return INSTANCE;
}静态内部类的方式,JVM保证线程安全,完美写法之一。
1
2
3
4
5
6
7private static class SingletonObjHolder {
private final static SingletonObj INSTANCE = new SingletonObj();
}
public static SingletonObj getINSTANCE {
//调用时候静态内部类才会加载
return SingletonObjHolder.INSTANCE;
}通过枚举类,最完美写法(缺点是用枚举有点奇怪),线程安全,还可以防止反序列化(反射创建,枚举类没有构造方法)。
1
2
3
4public enum SingletonObj{
INSTANCE;
public void method(){}
}
2.策略模式(Strategy)
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。在有多种算法相似的情况下,解决使用 if…else 所带来的复杂和难以维护。
应用场景:比如分享功能,目前有微信和QQ,后续可能还会增加微博等等,此时只要定义不同的策略即可。
1 | //java的Comparetor就是策略模式,此处也用比较方法举例 |
3.工厂模式(Factory)
定义:任何可以产生对象的方法或类,都可以称之为工厂,单例也是一种工厂
简单工厂,扩展性不好
1
2
3
4
5
6
7
8public class SimpleFactory{
public Car createCar(){
return new Car();
}
public Plane createPlane(){
return Plane();
}
}工厂方法模式,分别创建工厂,单独分开,方便单一工厂扩展
1
2
3
4
5
6
7
8
9
10public class CarFactory{
public Car create(){
return new Car();
}
}
public class PlaneFactory{
public Plane create(){
return new Car();
}
}抽象工厂,可以生产一系列的对象,对于一族的产品方便扩展,不方便单一扩展
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//此处也可以用接口,可以根据语义选择使用,比如可以实现多个就用接口,只能属于一个类就抽象类
//抽象工厂
public abstract class AbstractFactory{
abstract AbstractCar createCar();
abstract AbstractPlane createPlane();
}
//抽象类
public abstract class AbstractCar{
}
//抽象类
public abstract class AbstractPlane{
}
//具体工厂
public class Factory{
AbstractCar createCar(){
return new RedCar();
}
AbstractPlane createPlane(){
return new BluePlane();
}
}
//具体类
public class RedCar extends AbstractCar{
}
//具体类
public class AbstractPlane extends AbstractCar {
}
//比如还可以建立一个工厂创建白色的Car和黄色的plane
4.门面模式(Facade)
原本客户端是直接通过调用各个子系统的,通过门面模式,创建一个外观类,使得客户端直接调用外观类,来间接去操作相关子系统。
5.调停者(Mediator)
当各个系统互相各种调度,这时候可以抽出一个专门的系统,各个系统只跟这个系统调用。其实外部就是门面模式,内部就是调停者,应用场景消息中间件mq。
6. 责任链(chain of responsibility)
为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。filter就是用的责任链模式。
1 | //这里使用filter来模拟案例 |
spring中filterChain对request顺序和对response顺序是相反的,实现很巧妙,简化代码类似如下:
1 | //实现filter后需要重写此方法,传入三个参数 |
7.装饰器(Decorator)
允许向一个现有的对象添加新的功能,同时又不改变其结构。一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。此时,将具体功能职责划分,同时继承装饰者模式。
使用场景:将具体功能职责划分,同时继承装饰者模式。
1 | //创建一个接口 |
8. 观察者模式(Observer)
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
Observer,Listener,Hook,Callback 都是观察者模式。一般用作事件监听,消息队列。
观察者模式与责任链模式很类似,责任链模式是将请求传递下去,观察者模式是对观察者进行广播。
9.组合模式(Composite)
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
使用场景:树形菜单,文件、文件夹的管理
1 | abstract class Node { |
10.享元模式(flyWeight)
主要用于减少创建对象的数量,以减少内存占用和提高性能。池化思想,将对象放到池里,需要时从池里拿。java的String就是享元模式。应用场景一般是线程池,连接池。
11. 代理模式(Proxy)
静态代理: 直接创建一个代理类与被代理类实现同一个接口,在代理类中对同样方法进行操作
动态代理: 自动实现代理类,继承Proxy类,与被代理类实现同一接口
jdk : 通过反射实现,继承Proxy类,与被代理类实现同一接口
1
2//通过newProxyInstance生成代理类,指定classLoader,代理类实现的接口列表和对应的执行handler
Proxy proxy = (Proxy) Proxy.newProxyInstance(classLoader, interfaces, handler);cglib:需要添加引入jar,通过继承被代理类实现,不需要实现接口,所以类是final的无法使用(ASM可以,因为直接修改字节码文件)
1
2
3
4
5
6
7
8//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
Enhancer enhancer = new Enhancer();
//设置目标类的字节码文件
enhancer.setSuperclass(Dog.class);
//设置回调函数,相当于jdk动态代理的handler
enhancer.setCallback(new MyMethodInterceptor());
//这里的creat方法就是正式创建代理类
Proxy proxy = (Dog)enhancer.create();Instrument:在JDK5引入的,在class被加载时可以直接修改二进制文件
12.迭代器模式(Iterator)
用于容器的遍历
1 | //定义一个迭代器接口,要求每个容器都实现这个接口,实现通用遍历 |
13.访问者模式(Visitor)
在结构不变的情况下动态改变对于内部元素的动作,通过这种方式,元素的执行算法可以随着访问者改变而改变。代码耦合度很高。使用场景:1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。java的ASM是通过visitor实现的。
1 | //定义一个表示元素的接口 |
14.构建器模式(Builder)
分离复杂对象的构建和表示
1 | //当一个对象很大的时候可以分别构建 |
15.适配器模式(Adapter)
作为两个不兼容的接口之间的桥梁。可以联想电源适配器,转接头
将一个类的接口转换成另外一个希望的接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
1 | public class MediaAdapter implements MediaPlayer { |
16.桥接模式(Bridge)
将抽象部分和实现部分分离,使他们可以独立的变化。
1 | //创建桥接接口 |
17.命令模式(Command)
封装命令
1 | public abstract class Command { |
18.原型模式(Prototype)
jdk自带,实现cloneable接口,一般重写clone方法,jdk自带的clone方法克隆对象的引用类型还是指向的同一个地址(浅拷贝),重写clone方法使引用也克隆叫做深拷贝,属性也可以实现clone方法执行clone。
String类型不需要深拷贝,修改String是指向常量池中新的对象。但是如果用StringBuffer创建的就需要深拷贝。
19.备忘录模式(Memento)
备忘录模式提供的基本功能是:保存对象状态信息(快照)、撤销、重做和历史记录。
备忘录模式一般会提供两种接口:宽接口和窄接口。通过宽接口可以获取整个对象状态,会暴露备忘录对象的内部信息。通过窄接口,只能访问有限的,开发者限制了的信息,可以有效的防止信息泄露。
1 | //一般需要存盘,要实现序列化 |
20.模板方法(TemplateMethod)
模板方法就是钩子函数,回调函数
1 | public class Main{ |
21.状态模式(State)
一个类的动作根据状态不动有不同的类型反应
1 | public abstract class MMState { |
22.解释器(Intepreter)
动态脚本解析,比如写一个正则表达式解析器,python解析器,解析一门语言。