在遵循前面的面向对象设计原则基础上,前辈们总结出一些解决不同问题场景的设计模式,以四人帮的gof23最为知名。
本章只整理几个常用的设计模式。
24种设计模式(gof23+1):( * GOF=gang of four<四人帮>,指《设计模式》一书的四位作者)
- 创建型模式:
- 简单工厂模式(不包含在gof23中)
- 工厂模式
- 抽象工厂模式
- 单例模式
- 原型模式
- 创建者模式
- 结构型模式:
- 组合模式
- 装饰者模式
- 外观模式
- 适配器模式
- 代理模式
- 享元模式
- 桥接模式
- 行为型模式:
- 观察者模式
- 策略模式
- 状态模式
- 中介模式
- 模板方法
- 命令模式
- 备忘录模式
- 访问者模式
- 解释器模式
- 迭代器模式
- 职责链模式
下面是七个常用的设计模式的简单介绍,想深入了解可以点击链接。
1.简单工厂模式(不包含在gof23中)(Simple Factory Pattern)
又称为静态工厂方法(Static Factory Method)模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。<设计模式-简单工厂模式>
2.工厂方法模式(FACTORY Method METHOD)
又叫做虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。
工厂方法模式是在简单工厂的基础上再包了一层工厂,所有的工厂都是此工厂的子类。而产生对象的类型由子类工厂决定。
核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。<设计模式-工厂方法模式>
3. 抽象工厂模式(ABSTRACT FACTORY )
抽象工厂模式又称为Kit模式,属于对象创建型模式。
提供一个创建一系列相关相互依赖对象的接口,而无需指定他们具体的类。抽象工厂为不同产品族的对象创建提供接口。 能让具体的创建对象实例和客户端分离,客户端是通过他们的抽象接口操作实例 ,抽象工厂不太易于拓展,如果需要自增功能,或者自增产品,则需要至少修改三个类,而且实例化的代码是写死在程序中的 , 这样无法避免违背开放-关闭原则。(可以通过配置文件,结合反射的方式来解决)
使用场景:系统需要在不同产品族进行切换 。<设计模式-抽象工厂模式>
4.单例模式(SINGLETON)
单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式只应在有真正的“单一实例”的需求时才可使用。选择单例模式就是为了避免不一致状态。该模式主要目的是使内存中保持1个对象。<设计模式-单例模式>
/*懒汉模式加锁的话会降低效率,为了解决这个问题,写了下面的方法。
* 利用内部类来解决线程安全问题
*/
public class Singleton {
private static class SingelTonHodler{
private static final Singleton instance= new Singleton();
}
private Singleton(){
}
public static final Singleton getInstance(){
//线程安全
return SingelTonHodler.instance;
}
}
5.观察者模式:
例子:班长整理班级的事务,会把这些信息发送到群里,加入群,我们是作为观察者(订阅者),班长就是被观察者,任何信息发生了变化,我们都能即时的看到。
注意学习两个类:Observable--被观察者
setChanged();//记此 Observable 对象为已改变的对象
notifyObservers();//则通知其所有观察者
addObserver
Observer--观察者
update():只要改变了 observable 对象就调用此方法。
原理:
被观察者发生变化,观察者能第一时间知道这种变化
当然每个观察者可以有多个被观察者,每个被观察者可以有多个观察者
两者不是对立的 <设计模式-观察者模式>
Observable类
import java.util.Observable;
//被观察的类
public class SimpleObservable extends Observable{
private int data=0;
public int getData() {
return data;
}
public void setData(int data) {
if (this.data!=data) {//当被设置的值与原来不同,才会重新设置
this.data = data;
setChanged();//记此 Observable 对象为已改变的对象(Observable类的方法)
notifyObservers();//通知其所有观察者(Observable类的方法)
}
}
}
Observer类
import java.util.Observable;
import java.util.Observer;
//观察者类
public class SimpleObserver implements Observer{
@Override
public void update(Observable o, Object arg) {//重写Observer类的方法
System.out.println("Data属性被修改为:"+((SimpleObservable)o).getData());
}
}
测试类
import java.util.Scanner;
//测试类
public class SimpleTest {
public static void main(String[] args) {
SimpleObservable doc = new SimpleObservable();
SimpleObserver view= new SimpleObserver();
doc.addObserver(view);
while (true) {
System.out.println("请给data重写赋值");
doc.setData(new Scanner(System.in).nextInt());
}
}
}
结果
请给data重写赋值
12--------------------第一次输入值,设置data值
Data属性被修改为:12
请给data重写赋值
12--------------------第二次输入值,设置data值。就没有出现“Data属性被修改为:12”了
请给data重写赋值
6.代理模式:
定义:某一个对象提供一个代理对象吗,并且代理对象控制源对象的引用。 一个人或者一个机构代理一个人或者一个机构去完成某些事情。
举个例子:
妈妈在炒菜时,发现没有酱油,让儿子去打酱油,让买9块钱的酱油,但是儿子买得是8块的酱油,剩下的买辣条了。
如果妈妈一直都没有发现儿子在悄悄的买辣条就是
1—1 静态的代理模式
突然有一天,妈妈逛超时,发现那个只要8块钱,可能让老公或者奶奶去买酱油就是
1--多 动态的代理模式
静态代理:
是客户端和代理的一 一绑定的关系,实现起来比较简单。
动态代理:
换着人来买酱油
客户端和代理实例会进行动态的关联,这种称为动态代理。
动态代理的步骤:
1通过 InvocationHandler接口来定义自己的InvocationHandler
2同过Proxy.newProxyInstance()获取到动态的代理实体
3同过代理对象调用目标放法
举个例子:
有一个嫌疑人,被FBI给监视,包括聊qq ,打游戏,。。为了民众的安全考虑,进行必要的监控
代理可以用来做监视,但是他的功能远远的超过了监视(恐怖)
可以用来阻止方法的调用吗。伪造方法调用的参数,伪造方法调用的执行结果 <设计模式-代理模式>
7. 建造者模式(Builder Pattern)
该模式其实就是说,一个对象的组成可能有很多其他的对象一起组成的,比如说,一个对象的实现非常复杂,有很多的属性,而这些属性又是其他对象的引用,可能这些对象的引用又包括很多的对象引用。封装这些复杂性,就可以使用建造模式。
8. 门面模式/外观模式(Facade Pattern)
定义:外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。门面模式又称为外观模式,它是一种对象结构型模式。
这个模式个人感觉像是Service层的一个翻版。比如Dao我们定义了很多持久化方法,我们通过Service层将Dao的原子方法组成业务逻辑,再通过方法向上层提供服务。
- 为一个复杂子系统提供一个简单接口
- 提高子系统的独立性
- 在层次化结构中,可以使用Facade模式定义系统中每一层的入口
9. 策略模式(Strategy Pattern)
定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。
策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到最后一句话的目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口。
使用场景:
1.针对同一类型问题的多种处理方式,仅仅是具体行为有差别时;
2.需要安全地封装多种同一类型的操作时;
3.出现同一抽象类有多个子类,而又需要使用 if-else 或者 switch-case 来选择具体子类时。