设计模式
23种设计模式:
创建型模式:
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
结构型模式:
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
行为型模式:
模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、
策略模式、责任链模式、访问者模式
一:单例模式:Singleton
实现方式:懒汉模式(LazySingleton)、饿汉模式(HungrySingleton)
懒汉模式(LazySingleton)需要考虑的问题:
-
线程安全问题
-
double check加锁优化
-
编译器(JIT),CPU有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile关键字进行修饰,对于volatile修饰的字段,可以防止指令重排序
饿汉模式(HungrySingleton):
只有在真正主动使用对应的类时,才会出发初始化如(当前类是启动类即main函数所在类,直接进行new操作,访问静态属性、方法静态方法,用反射访问类,初始化一个类的子类等)
类加载的初始化阶段就完成了实例的初始化。本质上就是借助于jvm类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)
类加载过程:
-
加载二进制数据到内存中,生成对应的Class数据结构
-
连接:a.验证,b.准备(给类的静态成员变量赋默认值),c.解析
-
初始化:给类的静态变量赋初始值
使用内部类模式:实际上是懒汉模式和饿汉模式的结合,是一种延迟加载机制,在真正调用的时候才会去创建实例
使用枚举实现单实例:
单例模式常见场景:
Windows的任务管理器
Windows的回收站
项目中,读取配置文件的类,一般也只有一个对象,没必要每次都取new对象读取
网站的计数器一般也采用单例模式,可以保证同步
数据库连接处的设计一般也是单例模式
在servlet编程中,每个servlet也是单例的
在spring中,每个bean默认就是单例的
OOP七大原则
-
开闭原则:对扩展开发,对修改关闭
-
里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
-
依赖倒置原则:要面向接口编程,不要面向实现编程
-
单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性
-
接口隔离原则:要为各个类建立它们需要的专用接口
-
迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话
-
合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
二:工厂方法模式(FactoryMethod)
作用:
实现了创建者和调用者的的分离
详细分类:
简单工厂模式(静态工厂模式):用来生产同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码)
工厂方法模式:用来生产同一等级结构中的固定产品(支持增加任意产品)
抽象工厂模式:围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂
工厂模式核心:
实例化对象不使用new,用工厂方法代替
将选择实现类,创建对象统一管理和控制。从而将调用者跟实现类解耦
工厂方法模式复杂度要比简单工厂模式大
根据设计原则:使用工厂方法模式
根据实际业务:使用简单工厂模式
三:抽象工厂模式
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
适用场景:
客户端(应用层)不依赖于产品实例如何被创建、实现等环节
强调一系列相关的产品对象(属于同一产品族)一起使用,创建对象需要大量的重复代码
提供一个产品类的库,所有的产品以同样的接口实现,从而是的客户端不依赖于具体的实现
优点:
具体产品在应用层的代码隔离,无需关心创建的细节
将一个系列的产品统一到一起创建
缺点:
规定了所有可能被创建的产品集合,产品族中扩展新的产品困难;
增加了系统的抽象性和理解难度
四:建造者模式(Builder)
建造者模式也属于创建型模式,它提供了一种创建对象的最佳模式。
定义:将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表述
主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象
用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
例子:
工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的)
五:原型模式
通过克隆实现原型模式
结构性模式:从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题
六:适配器模式
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
角色分析:
目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口
需要适配的类:需要适配的类或适配者的类
适配器:通过包装一个需要适配的对象,把原接口转换成目标对象
分为对象适配器(组合)、类适配器(继承),对象适配器更好
七:桥接模式(Bridge)
桥接模式是将抽象部分与它的实现部分分离,使他们都可以独立地变化。它是一种对象结构型模式,有称为柄体(handle and body)模式或接口(interface)模式
好处分析:
桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本。
桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来。
劣势分析:
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
场景:
java语言通过java虚拟机实现了平台的无关性
AWT中的Peer架构
JDBC驱动程序也是桥接模式的应用之一
八:代理模式(Proxy)
角色分析:
抽象角色:一般会使用接口或者抽象类来解决
真实角色:被代理的角色
代理角色:代理真实角色,代理真实角色后一般会做一些附属操作
客户:访问代理对象的人
代理模式的好处:
可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
公共业务交给代理角色,实现了业务的分工
公共业务发生扩展的时候,方便集中管理
缺点:一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会降低
静态代理
动态代理:基于接口、基于类
需要了解两个类:
Proxy
InvocationHandler
动态代理的本质就是使用反射机制
动态代理的好处:
可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
公共业务交给代理角色,实现了业务的分工
公共业务发生扩展的时候,方便集中管理
一个动态代理类代理的是一个接口,一般就是一类业务
一个动态代理类可以代理多个类,只要实现同一个接口即可
九:享元模式(Flyweight Patterm)
定义:运用共享技术有效地支持大量细粒度的对象
优点:如果系统有大写类似的对象,可以节省大量的内存及CPU资源
十:门面模式(Facade)
定义:为子系统中的一组接口提供一个一致的接口,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
应用场景:
需要使用复杂子系统的有限但直接的接口时,请使用Facade模式
想要将子系统组织成层是,请使用Facade
十一:装饰者模式(Decorator)
定义:在不改变原有对象的基础上,将功能附加到对象上
应用场景:扩展一个类的功能或给一个类添加附加职责
优点:
不改变原有对象的情况下给一个对象扩展功能
使用不同的组合可以实现不同的效果
符合开闭原则
十二:策略模式(Strategy)
定义:定义了算法族,分别封装起来,让他们之间可以互相替代,此模式的变化独立于算法的使用者
十三:模板方法模式(Template Method)
定义:定义一个操作的算法骨架,而将一些步骤延迟到子类中,Template Method使得子类可以不改变一个算法的机构即可重定义该算法的某些特定步骤。
十四:观察者模式(observer)
定义:定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者都会收到通知并更新
应用场景:当更改一个对象的状态可能需要更改其他对象,并且实际的对象集事先未知或动态更改时,请使用观察者模式
优点:
符合开闭原则
可以在运行时简历对象之间的关系
十五:责任链模式(Chain of Responsibility)
定义:为请求创建了一个接收者对象的链
处理者(Handler)需要做成链式结构(包含Next),请求(Request)不用做成链式,处理者去找它需要的Request就可以了