23种设计模式
一 单例模式
一共有8种单例方式
1 Yunyun yy = Yunyun.getInstance(); //推荐方法,有调用即初始化小瑕疵
方法:构造方法设为私有,别人只能调用该类getInstance单例实例化
private static final Yunyun Instance = new Yunyun();
private Yunyun(){}
public static Yunyun getInstance() {return Instance;}
2 与1相似用静态块
private static final Yunyun Instance;
static{
Instance = new Yunyun();
}
3 在1基础上懒加载,用时才初始化 (线程不安全,淘汰)
4 3基础上再 synchronized Yunyun getInstance()方法 (效率低下,淘汰)
5 3基础上 先判断if(Instance==NULL) 然后 synchronized(Yunyun.class).
有重排序问题,淘汰
6 经典方案,双重检查中间加锁方案
volatile变量,判断是否null -> synchronized -> 判断null
7 完美方案
定义静态内部类
private Yunyun(){}
private static class ZhaoYunyun{
private final static Yunyun Instance = new Yunyun();
}
public static Yunyun getInstance(){
return ZhaoYunyun.Instance;
}
//JVM保证了线程安全,class Yunyun只加载一次,所以内部类也只加载一次
//解决了方案1中的调用即初始化问题,加载Yunyun时,内部静态类不会运行
8 最完美方案 (个人感觉不咋地,纯粹装逼)
ThinkInJava中提供了使用枚举单例,枚举无法使用构造方法,还可以防止反序列化
public enum Yunyun{
Instance;
}
public class test(){
main(){
Yunyun.Instance.hasCode();
}
}
二 策略模式
个人感觉和接口意义很像,一个业务需求可能有很多种不同具体实现方式。
比如在Spring中以前讲过,BeanPostProcessor是一个接口,提供两个方法。其他不同类不同方式的实现这一个标准接口的两个方法,实现AOP这些功能,就是策略模式
Comparator 排序器。他本身不知道我们究竟会使用什么参数,什么比较规则来排序。所以他提供这个接口让我们自定义重写,实现自己需要的排序规则,这也是策略模式。
工厂模式
主要是工厂方法和抽象工厂
简单工厂
简单工厂可扩展性很差,不推荐,也不属于23种设计模式内。
public class MeetingFactory{
public Boy createBoy(){
return new Boy();
}
public Man createMan(){
return new Man();
}
//可扩展性很差
}
三 工厂方法
public interface Product(){
public void use();
}
public interface Factory{
public Product createProduct();
}
public class foodFactory implement Factory{
@Override
public Product createProduct(){
return new Food();
}
}
public class Food implement Product{
@Override
public void use(){
System.out.println("I am food");
}
}
工厂方法就是对每个新生产对象对应一个工厂
四 抽象工厂
public abstract class Food{
abstract void printName();
}
public abstract class Weapon{
abstract void shoot();
}
public abstract class AbstractFactory{
abstract Food createFood();
abstract Weapon createWeapon();
}
//把工厂和要生产的对象全部抽象化,这样可以实例化多个不同的工厂,
//多个不同的调用对象,可扩展性很好。
抽象工厂总结:
抽象工厂很容易扩展产品族。新写一个AbstractFactory实现类即可创建新的产品。但是有一大弊端,当我们在AbstractFactory新增一个方法时,所有该工厂实现类都要全部重新添加该方法,违反了开放封闭的原则。
抽象工厂和工厂方法区别?
抽象工厂是工厂方法的衍生,但不是就比工厂方法好。抽象工厂是在工厂方法的基
上同时对多个工厂又封装了一个整体工厂如上AbstractFactory。 在工厂方法中就
会有FoodFactroy和ProductFactroy,而在抽象方法中只有一个AbstractFactory
抽象工厂利于直接创建新的产品族,但是如果封装的这个AbstractFactory有新的对
象需要添加,所有实现这个AbstractFactory都需要重写。工厂方法缺点则是来一个
对象就需要一个工厂,产生的工厂数量太多。两者方法根据业务各自选择。
五 门面模式
内部关系复杂,一个门面来封装所有内部之间的关系(多个接口全部封装到一个门面
类中)给外部调用。
在Tomcat源码中,封装connector模块的request类就是门面模式
六 调停者模式
内部关系复杂,通过一个调停者使得所有内部之间不再交集,通过调停者来调节内
部关系。
如消息中间件MQ
调停者模式和门面模式可以是同一个对象
七 装饰者模式
一个对象想在另一个对象原有的基础上添加一些新的方法。
如果用extend,两者耦合度太高,加一次方法就需要继承一次,扩展性很差。
所以新的类中注入原有对象的变量,在新类的方法下调用原有对象的方法然后加入
自己需要的新装饰逻辑
应用:
这不就是Spring autowire变量注入后,通过这个变量写别的类吗。
和AOP思想也很像,只不过AOP是横向切面处理,装饰者是纵向处理业务
八 责任链模式
创建chain类,链中依次执行各个类处理
interface node{
public boolean execute(Msg m){
有问题时返回false表示停止向下处理
}
}
public class chains extends node{
add//链表之间互相添加
exectue//处理所有nodes
}
serlvet中Http处理
require 1,2,3顺序执行后
response 3,2,1顺序相应
class HTMLFilter implement Filter{
@Override
public boolean doFilter(Request request,Response response, FilterChain chain){
1 处理require
2 递归执行 chain.doFiler会递归执行下一个chain
3 处理response
}
}
九 观察者模式
当一个事件被触发则执行某些事情
//大概代码思路
interface Observer{
public void actionOnWakeUp(wakeUpEvent event);
}
public class Dad implements Observer{
feed(){
//逻辑处理
}
@Override
public void actionOnWakeUp(wakeUpEvent event){
feed();
}
}
class Child{
boolean cry = false;
List<Observer> observers = new ~;
wakeUp(){
//wakeUP事件触发,遍历list处理observers所有actionOnWakeUp
}
}
public class wakeUpEvent{
long time;
String location;
Child source;
}
十 组合模式
树状图结构专用
leaf extends Node{
print();
}
Branch extends Node{
List<Node> lists;
print();
add(Node n);
}
十一 享元模式
池概念,如线程池,常量池等,重复使用不实例化新对象
十二 代理模式
静态代理
A 接口{
move();
}
class B implement A{
move();
}
class Proxy implement A{
B;
move{
xxx;
B.move();
xxx;
}
}
动态代理
handler implement InvocationHandler{
@Override
invoke(Object proxy,Method method, Object[] args){
根据业务需要进行处理
method.invoke(object, args);//通过反射调用 被代理类的方法
return null;
}
}
//实例化代理对象
newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler handler)
在第二章Spring中专门见过AOP和动态代理
JDKProxy:InvocationHandler接口和Proxy类。 在BeanPostProcessor下利用
Java反射,重写postProcessAfterInitialization干预bean初始化实现的。真实
实现类不在单例池中,而是代理类注册在单例池中。
Cglib: 通过ASM(二进制字节码操作类库)直接修改二进制字节码实现生成动态代理
第十三 迭代器模式
动态扩展容器 list.add(xxx)
boolean hasNext();
xxx next();
十四 访问者模式
结构不变情况下,动态改变对于内部元素的动作
应用:
1 编译器中抽象语法树
accept(visitor) 根据传递的visitor判断是符号visitor还是数字visitor等,执
行不同语法解析操作
2 Cglib原理 ASM也是运用访问者模式
如动态生成字节码
ClassWriter cw = new ~;
ClassReader cr = new ~;
cw.accept(cr,0) //根据传入的cr,运用访问者模式动态生成新字节码
十五 建造者模式
当一个对象构造极其复杂时
把一个对象内部大量初始化变量分批构造
A对象中有a b c d e f g变量
新建B对象构造a b c d
新建C对象构造e f g
这样A对象只需要B和C,分批处理
十六 适配器模式
A和B不兼容,通过C作为一个适配器来协调A和B
Spring中DispatcherServlet适配器,适配请求和相应处理的handler
十七 桥接模式
分离抽象和具体,用聚合方法桥接抽象和具体,避免类爆炸
聚合方式类似autowired注入
这篇文章讲得很好
十八 命令模式
能够执行do,还能撤回undo命令的设计思想
abstract class Command{
abstract void do();
abstract void undo();
}
十九 原型模式
就是克隆创建新的对象,调用者不调用构造函数。
往往用于创建对象比较繁琐的场景
class Person implement Cloneable{
.....
@Override
public Object clone(){
return super.clone()
//这里是调用Object内部clone方法,克隆内存,==是相等的浅拷贝
}
}
所以引用类型需要深克隆,不然A和B都指向同一引用。当A值修改时,B值也莫名其妙
被改了。但是String不需要深克隆,因为String有常量池,A改了会引向新引用,B
值不变
该小段截取于该网站
在深克隆,可以通过序列化(Serialization)等方式来实现。序列化就是 将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一 个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现 Serializable接口,否则无法实现序列化操作。
二十 备忘录模式
记录状态,便于回滚
记录快照,存盘
class Menento implements Serializable{
public void save(){
File f = new File(...);
Object OutputStream oos = new ~(f);
oos.write(objects);
}
public File load(){
//加载文件
}
}
二十一 模版模式
子类继承父类模版后,重写父类方法然后使用
父类仅仅提供一个模版,提供构造函数
二十二 状态模式
根据状态来决定行为
如汽车类有open,close,running,stopped四个方法,在不同状态下。
四个方法有的不能执行,有的必须执行。
二十三 解释器模式
动态脚本解析
总体基本原则
可维护,复用,可扩展,灵活
面向对象六大指导原则
纯死记硬背,个人认为没有什么意义,记住23种设计模式灵活运用最重要