设计模式的三种类型
- 创建型模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
- 结构型模式:适配器模式、装饰者模式、代理模式、桥接模式、组合模式、外观模式、享元模式。
- 行为型模式:观察者模式、策略模式、模版方法模式、命令模式、迭代器模式、中介者模式、备忘录模式、解释器模式、状态模式、职责链模式、访问者模式。
常用设计模式
单例模式(Singleton Pattern)(最常问的设计模式)
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。
使用场景:
- 要求生成唯一序列号的环境;
- 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
- 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
- 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式)
实现方式:
饿汉式
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
缺点:如果我们在Singleton类里面写一个静态的方法不需要创建实例,它仍然会早早的创建一次实例。没有lazy loading的效果,从而降低内存的使用率。
懒汉式(改进版)
public class Singleton {
private static volatile Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
if (instance != null) {
return instance;
}
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
优点:适用于多线程,只有 instance 初始化的时候会加锁,加锁很耗时,能避免就避免,可以转移到说明 volatile 的作用
静态内部类
public class Singleton {
private Singleton(){}
private static class SingletonHolder{
private final static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
工厂模式
用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
Product为抽象产品类负责定义产品的共性,实现对事物最抽象的定义。
Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的。
使用场景:jdbc连接数据库,硬件访问,降低对象的产生和销毁
具体工厂类
public class ConcreteCreator extends Creator{
public < T extends Product> TcreateProduct(Class<T>c){
Product product = null;
try{
product = (Product)Class.forName(c.getName()).newInstance();
}catch(Exceptione){
... //异常处理
}
return (T)product;
}
}
简单工厂模式:一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法。
多个工厂类:每个人种(具体的产品类)都对应了一个创建者,每个创建者独立负责创建对应的产品对象,非常符合单一职责原则。
代替单例模式:单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象。
延迟初始化:ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留。
代理模式(Proxy Pattern)
为其他对象提供一种代理以控制对这个对象的访问。
- Subject抽象主题角色抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
- RealSubject具体主题角色也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。
- Proxy代理主题角色也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
普通代理:
在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。
强制代理:
强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
动态代理:
根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”
装饰者模式(Decorator Pattern)
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。
使用场景:
- 需要扩展一个类的功能,或给一个类增加附加功能。
- 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
- 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。
Component抽象构件
Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象,如上面的成绩单。注意:在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当Component抽象构件。
ConcreteComponent具体构件
ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是它。
Decorator装饰角色
一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个private变量指向Component抽象构件。
具体装饰角色
ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,你要把你最核心的、最原始的、最基本的东西装饰成其他东西,上面的例子就是把一个比较平庸的成绩单装饰成家长认可的成绩单。
适配器模式(Adapter Pattern)
将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
-
Target目标角色
该角色定义把其他类转换为何种接口,也就是我们的期望接口,例子中的IUserInfo接口就是目标角色。
-
Adaptee源角色
你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新、靓丽的角色。
-
Adapter适配器
角色适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:把源角色转换为目标角色,怎么转换?通过继承或是类关联的方式。
观察者模式(Observer Pattern)
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
使用场景:
关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
事件多级触发场景。
跨系统的消息交换场景,如消息队列的处理机制。
注意:
广播链的问题在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次)。
异步处理问题观察者比较多,而且处理时间比较长,采用异步处理来考虑线程安全和队列的问题
-
Subject被观察者
定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
-
Observer观察者
观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。
-
ConcreteSubject具体的被观察者
定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
-
ConcreteObserver具体的观察者
每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑
被观察者(异步的话需要把 ArrayList 改为性能较低的 Vector 或 CopyOnWriteArrayList)
public abstract class Subject{
//观察者组
private List<Observer> obsList = new ArrayList<>();
public void addObserver(Observer o){
this.obsList.add(o);
}
public void delObserver(Observer o){
this.obsList.remove(o);
}
public void notifyObservers(){
for(Observer o : obsList){
o.update();
}
}
策略模式(Strategy Pattern)
定义一组算法,将每个算法都封装起来,并且使它们之间可以互换
使用场景:
多个类只有在算法或行为上稍有不同的场景。
算法需要自由切换的场景。
需要屏蔽算法规则的场景。
-
Context封装角色
它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
-
Strategy抽象策略角色
策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。各位看官可能要问了,类图中的AlgorithmInterface是什么意思,嘿嘿,algorithm是“运算法则”的意思,结合起来意思就明白了吧。
-
ConcreteStrategy具体策略角色(多个)
实现抽象策略中的操作,该类含有具体的算法。
扩展:
策略枚举
浓缩了策略模式的枚举
受枚举类型的限制,每个枚举项都是public、final、static的,扩展性受到了一定的约束,因此在系统开发中,策略枚举一般担当不经常发生变化的角色。
致命缺点:
所有的策略都需要暴露出去,由客户端决定使用哪一个策略。
public enum Calculator {
//加法运算
ADD("+"){
public int exec(int a , int b){return a + b ;}
},
//减法运算
SUB("-"){
public int exec(int a , int b){return a - b ;}
};
String value = "";
//定义成员值类型
public Calculator(String value){
this.value = value;
}
//获取枚举成员的值
public String getValue(){
return value;
}
//声明抽象方法
public abstract int exec(int a , int b);
}