我们习惯将一些文章或者逻辑整理为一些使用简短的文字进行表达出它的完整意思,这样的目的就是为了减少语义多次表达,那么缺点就是需要大家都学习了解到这样的文字才能知道它完整的意思,不然就会导致理解误差:例如我们的成语,或者网络词汇以及我们将要讲到的设计模式等;
在很多文章中都有提到23中设计模式,这些设计模式都是由前人在开发设计的过程中总结取的名字,下面我们进行简单的对这些设计模式进行描述,原来做笔记的时候没有做笔记的样例,这个将在之后再进行补充;
单例模式-single
单例模式又分两种,懒汉式和饿汉式;
饿汉式,看其语义就是饥饿,通常就是直接创建一个静态对象,然后将类的构造函数只保留一个私有的,这样就不能通过new的方式再次创建该对象了(通过发射的方式还是可以创建)
/**
* 单例模式,饿汉式
* 优点:简单
* 缺点:在没有使用的时候也会先创建;
*/
public class SingleEhanshi {
private static SingleEhanshi INSTANCE = new SingleEhanshi();
private SingleEhanshi() {
}
public static SingleEhanshi getInstance() {
return INSTANCE;
}
}
懒汉式,看语义就是懒,为啥这么说呢,就是在没有用到的时候就永远不会创建对象,只是创建一个静态变量(默认为null对象),只有第一次用到了才会创建该类对象;
懒汉式的方式是使用的时候在创建对象;
存在高并发的时候可能创建多个:
解决方案:
- 添加锁,添加锁会影响性能,如果添加在方法上;
- 把锁放到方法里面使用快最小化影响,并且使用双校验判断;
- 使用静态内部类的方式;
- 使用枚举-属于饿汉式;
代码一:
/**
* 单例模式,懒汉式
*/
public class SingleLanHanshi1 {
private static SingleLanHanshi1 INSTANCE;
private SingleLanHanshi1() {
}
/**
* 多线程不安全
*
* @return
*/
public static SingleLanHanshi1 getInstance() {
if (INSTANCE == null) {
INSTANCE = new SingleLanHanshi1();
}
return INSTANCE;
}
/**
* 牺牲性能,这个锁会添加到类上;
*
* @return
*/
public static synchronized SingleLanHanshi1 getInstance1() {
if (INSTANCE == null) {
INSTANCE = new SingleLanHanshi1();
}
return INSTANCE;
}
/**
* 双重校验
* 推荐
*
* @return
*/
public static SingleLanHanshi1 getInstance2() {
if (INSTANCE == null) {
synchronized (SingleLanHanshi1.class) {
if (INSTANCE == null) {
INSTANCE = new SingleLanHanshi1();
}
}
}
return INSTANCE;
}
}
代码二:单例模式,懒汉式,使用静态内部类,利用jvm的类加装机制,静态类只会加载一次到内存中;
/**
* 单例模式,懒汉式,使用静态内部类
* 利用jvm的类加装机制,静态类只会加载一次到内存中
*/
public class SingleLanHanshi2 {
private SingleLanHanshi2() {
}
private static class CreateSingleLanHanshi2 {
private final static SingleLanHanshi2 INSTANCE = new SingleLanHanshi2();
}
public static SingleLanHanshi2 getInstance() {
return CreateSingleLanHanshi2.INSTANCE;
}
}
代码三:不仅解决了线程同步问题,还解决了反序列化的问题,不可以反射创建
/**
* 不仅解决了线程同步问题,还解决了反序列化的问题,不可以反射创建
*/
public enum SingleEhanshi2 {
INSTANCE;
}
策略模式-strategy
我的理解就是接口模式,不在意接口实现,只有用的时候选择什么实现就是什么策略;设计上,就是先把桥搭好,实际使用什么实现由客户端决定,或者由控制决定,实现了对修改关闭,对扩展开放的原则。策略就是一个个的实现相同接口的不同逻辑实现;
工厂模式-factory
工厂模式从语义上理解就是生产对象的一个模式,它又分为简单工厂,工厂方法,抽象工厂;
简单工厂就是创建对象的地方,不论是实体对象还是其他的对象;
优点:把创建对象的工作统一管理,之后改造只要修改一个地方就可以;
缺点:把所有对象放到一个地方那么就存在维护成本,需要动原来的代码;
对每一个需要创建的对象都创建一个对应的工厂方法;
优点:扩展性好,不需要动原来的代码,只需要对新的需求进行增加新代码;
缺点:增加了很多类;
抽象工厂模式的一个很好的使用场景就是创建产品族之类的对象,定义好一组“形容词”性的抽象类,然后不同类型的使用一组实现,在换肤场景下可以随意切换;
门面模式-facade
门面模式(facade)就是将流程化或者只关注入参和结果的场景使用,将一切封装在一个方法里面;
调停者模式-mediator
所有调用都经过这个调停者类进行跟其他方法进行调用;
优点:解耦了复杂的功能间的关系;
缺点:调停者会变得很庞大,维护成本增加;
装饰器模式-decorator
装饰器设计模式-decorator,就是构建需要构想的接口,然后做组合拆分;
优点:能够灵活组合或者顺序调配;
缺点:复杂业务下的装饰器会存在很多,如果不加以描述和严格设计,将会带来使用的麻烦(其他程序员不会使用)
责任链模式
责任链模式,就是将一类处理逻辑封装成多个单一的处理类,然后可以设计其执行顺序,以及是否需要往下执行;典型是案例有这个servlet的过滤器,定义个接口filter然后定义一个filterchain;
观察者模式-observer
观察者模式-observer:就是一个被触发的模式。例如目前的事件模式,发起一个事件就触发对应的逻辑;还有监听模式,监听某个变动,例如这个mq的生产者和消费,以及这个zk的监听器等。还有回调函数,就是做了某事后做某事;
组合模式-composite
组合模式-composite,简单的理解就是按照层级进行设置组合。单个使用和组合使用没有影响;
享元模式-flyweight-实际研发过程中不用,理论要知道
享元模式-flyweight,就像这个线程池,公用线程资源,减少某些方面的开销。具体需要另外了解,大概理论就是共用原来的对象。
代理模式-proxy
代理模式的比喻比如房产中介,客户去买房需要办理很多手续,那么客户本身不需要去做这些工作,那么就交给房产中介去做;
使用代理模式的目的就是不修改原本代码逻辑的情况下增加对其方法的做一些增强的功能,例如过滤,或者记录日志;
代理模式又分静态代理和动态代理;
静态代理:就是创建一个目标对象的同时创建一个对应的代理类;需要创建一个目标对象的一个接口,代理对象也要实现这个接口。
缺点:目标对象接口变更,代理类对象也需要跟着变更,不易维护,并且需要创建一一对应的代理对象;
动态代理,有两种实现,一个是基于接口实现的动态代理-jbk,一个是不基于接口实现的动态代理-cglib;
基于接口实现的动态代理-jbk,被调用的目标对象必须实现接口,在使用代理的时候需要将接口作为参数传入,接口列表,可能存在实现多个接口;
不基于接口实现的动态代理-cglib,需要被代理的类不能被final修饰,因为这种代理模式其实是会创建一个继承类;
迭代器模式-iterator
主要针对容器设计的设计模式,主要用途是遍历容器的内容;
访问者模式-visitor
暂时不理解
构建者模式-builder
在创建复杂对象的时候用起来可以灵活并且让代码结构开始起来舒服,例如链式设置属性,然后设置属性的方法返回自己本身就可以形成链式编程;
适配器模式-adapter,包装器模式-wrapper
适配器模式-adapter就是对现存类不匹配但是又要用到的一种转换;
桥接模式-brige
分离抽象和实现化,在抽象化中又持有实现化的引用?
命令模式-command
命令模式-command使得客户端和服务端耦合度降低,只要知道如何发布命令即可,不需要知道发布的命令谁来执行,对于客户端来说;具体的就是发布要做什么,一系列的动作;
原型设计模式-prototype
原型设计模式就是对象的深拷贝和浅拷贝的内容,需要对象实现cloneable接口以及重写clone方法;
备忘录模式-memento
可以回到某个状态的一种对象模式,相当于在某个时刻对数据进行留存,然后再处理数据的时候搞乱了,要回到那个干净的留存状态;
模板模式-templatemethod
将骨架构建好,具体逻辑实现在子类中实现;确定好做什么的时候,先做什么,再做什么;
状态模式-state
暂时不理解;