行为型模式
设计模式1(创建型)
设计模式2(结构型)
模板方法模式
命令模式
访问者模式
迭代器模式
观察者模式
中介者模式
备忘录模式
解释器模式
状态模式
策略模式
责任链模式
模板模式
模板模式(Template Pattern)使用有个抽象类公开定义它的方法模板;其子类根据自身需求可重写抽象方法。
模板模式相当于设定好了一个框架流程,子类作为具体实现,按照这个流程进行实现。对于相同的步骤可以使用具体的方法,而对于有差异的方法由子类具体实现。
优点:行为由父类控制、子类实现;提取了公共代码便于维护。
如下:
-
需求:画图形,有圆形和矩形。
-
AbsShape:抽象模板类,定义了画图形的模板方法,只有draw()方法根据每个形状的不同二具体实现;
-
Circle、Rectangle:抽象类的子类,重写了draw()方法以适应个自的图形;
Client:创建对象,执行画图形的操作。
AbsShape:
/**
* 抽象类模板
*/
public abstract class AbsShape {
// 画图形完整过程
public void drawShape(){
prepare();
draw();
fill();
done();
}
// 准备
public void prepare(){
System.out.println("prepare draw shape tools");
}
// 画具体图像,不确定,使用抽象方法
public abstract void draw();
// 填色
public void fill(){
System.out.println("fill color");
}
// 完成
public void done(){
System.out.println("draw shape down");
}
}
Circle:
/**
* 画圆
*/
public class Circle extends AbsShape{
@Override
public void draw() {
System.out.println("draw a circle");
}
}
Rectangle:
/**
* 画矩形
*/
public class Rectangle extends AbsShape{
@Override
public void draw() {
System.out.println("draw a rectangle");
}
}
Client:
public class Client {
public static void main(String[] args) {
// 画圆
AbsShape circle = new Circle();
circle.drawShape();
/**
* prepare draw shape tools
* draw a circle
* fill color
* draw shape down
*/
// 画矩形
AbsShape rectangle = new Rectangle();
rectangle.drawShape();
/**
* prepare draw shape tools
* draw a rectangle
* fill color
* draw shape down
*/
}
}
钩子方法
当对于一个子类不需要某一个方法时,使用钩子方法实现。改造如下:
1.添加钩子方法,并对需要的方法进行判断;
/**
* 抽象类模板
*/
public abstract class AbsShape {
// 画图形完整过程
public void drawShape(){
prepare();
draw();
// 根据钩子方法判断是否执行
if(isUse()){
fill();
}
done();
}
// 准备
public void prepare(){
System.out.println("prepare draw shape tools");
}
// 画具体图像,不确定,使用抽象方法
public abstract void draw();
// 填色
public void fill(){
System.out.println("fill color");
}
// 完成
public void done(){
System.out.println("draw shape down");
}
// 钩子方法
boolean isUse(){
return true;
}
}
2.子类对钩子方法重写;
/**
* 画圆
*/
public class Circle extends AbsShape{
@Override
public void draw() {
System.out.println("draw a circle");
}
// 返回false就会使既定方法不执行
@Override
boolean isUse() {
return false;
}
}
具体使用
在Spring的IOC中有具体的应用:ConfigurableApplication接口中定义抽象方法;其实现类是一个抽象类,实现该方法并定义了初始化的步骤(具体方法或抽象方法),抽象类的子类再根据需求实现或重写方法。
命令模式
命令模式(Command pattern)是对请求的封装,将请求分装成一个对象,使得对于不同的请求参数化。请求以命令的形式包裹再对象中,并传给掉调用对象。
模式中的角色:
- Command:命令接口,统一管理具有类似操作的命令;
- ConcreteCommand:命令的具体实现,聚合了命令的执行者(Receiver),调用Receiver的方法执行命令;
- Receiver:命令的执行者,接收者;
- invoker:命令对象的传入地点,通过该类将命令与请求结合起来给客户端调用。
优点:降低系统的耦合程度;便于添加新的命令,无需修改代码,只需要添加Command的实现类即可。
如下:
- 需求:对于数据库的事务操作,有提交(commit)、回滚(rollback),要使添加新的命令方便;
- SqlCommand:数据库操作命令接口,定义了抽象方法commit、rollback;
- DeleteCommand、UpdateCommand、InsertCommand:命令接口的实现类;
- DatabaseReceiver:命令的接收者,也是实际的操作者;
- Broker:方法的调用者,根据用户的命令,调用具体的方法;
- Client:客户端,用户根据需要传入相应的命令对象以执行。
SqlCommand:
/**
* sql操作命令
*/
public interface SqlCommand {
// 提交
void commit();
// 回滚
void rollback();
}
SqlCommand:
/**
* 插入操作命令
*/
public class InsertCommand implements SqlCommand{
// 聚合数据库操作类(命令执行者)
private DatabaseReceiver receiver;
public InsertCommand(DatabaseReceiver receiver) {
this.receiver = receiver;
}
// // 实现事务提交操作(插入)
@Override
public void commit() {
receiver.insert();
}
// 回滚命令
@Override
public void rollback() {
receiver.rollback();
}
}
UpdateCommand:
/**
* 更新命令
*/
public class UpdateCommand implements SqlCommand{
// 聚合数据库操作类(命令执行者)
private DatabaseReceiver receiver;
public UpdateCommand(DatabaseReceiver receiver) {
this.receiver = receiver;
}
// 实现事务提交操作(更新)
@Override
public void commit() {
receiver.update();
}
@Override
public void rollback() {
receiver.rollback();
}
}
DeleteCommand:
/**
* 删除命令
*/
public class DeleteCommand implements SqlCommand{
// 聚合数据库操作对象(命令执行者)
private DatabaseReceiver receiver;
public DeleteCommand(DatabaseReceiver receiver) {
this.receiver = receiver;
}
// 实现事务提交操作(删除)
@Override
public void commit() {
receiver.delete();
}
@Override
public void rollback() {
receiver.rollback();
}
}
DatabaseReceiver:
/**
* 数据库操作
*/
public class DatabaseReceiver {
public void insert(){
System.out.println("插入数据~");
}
public void update(){
System.out.println("更新数据~");
}
public void delete(){
System.out.println("删除数据~");
}
public void rollback(){
System.out.println("回滚,操作无效~");
}
}
Broker:
/**
* invoker类,用于调用命令
*/
public class Broker {
// 保存执行的命令
private List<SqlCommand> sql = new ArrayList<>();
// 调用并执行命令
public void executeCommand(SqlCommand command){
// 添加命令
sql.add(command);
// 执行
command.commit();
}
// 对事务回滚
public void undoCommand(SqlCommand command){
// 如果没有进行过对应的操作,回滚失败
if (!sql.contains(command)){
System.out.println("事务未提交无法回滚");
return;
}
command.rollback();
sql.remove(command);
}
}
Client:
public class Client {
public static void main(String[] args) {
// 创建数据库操作执行类
DatabaseReceiver databaseReceiver = new DatabaseReceiver();
// 操作命令
InsertCommand insert = new InsertCommand(databaseReceiver);
UpdateCommand update = new UpdateCommand(databaseReceiver);
DeleteCommand delete = new DeleteCommand(databaseReceiver);
// 创建命令调用类
Broker broker = new Broker();
// 1.插入操作
broker.executeCommand(insert);
// 2.插入错误,回滚
broker.undoCommand(insert);
// 3.更新操作
broker.executeCommand(update);
// 4.执行回滚删除操作
broker.undoCommand(delete);
/**
* output:
* 插入数据~
* 回滚,操作无效~
* 更新数据~
* 事务未提交无法回滚
*/
}
}
访问者模式
访问者模式(Visitor Patter)使用一个访问者类改变对实体类的执行算法。也就是对实体类的执行操作由访问者类来控制。
实现过程为:
- 客户端通过访问者类(visitor)访问元素——创建访问者对象,并将其传递元素;
- 元素接收访问之后再交由访问者操作——元素调用accept(Visitor v)方法接收访问者,并调用访问者的方法。
主要实现功能:将数据结构与数据操作分离。
优点:灵活、扩展性高。
缺点:依赖了实体类(见下面代码中的visitor类中的方法),违反了依赖倒置原则;实体类更变困难。
如下:
- 需求:一个家庭有4个人(father、mather、son、daughter);现有一个访问者访问这个家庭各个成员;各成员接受访问之后访问者得到信息;最后右访问者将家庭的成员信息展示出来。
- Person:家庭成员抽象类,有抽象放啊accept(),表示接收访问者访问;
- Father、Mather、Son、Daughter:家庭的具体成员,实现方法并调用访问者的访问方法访问自己;
- FamaryVisitor:访问者接口,定义访问家庭成员的方法,通过该方法展示该家庭的成员信息;
- ConcreteFamilyVisitor:具体访问者,具有具体的访问方法;
- Family:该类封装了访问者对该家庭的访问流程(也即访问者模式中的算法);
- Client:创建访问者,开始访问并展示信息
Person:
public abstract class Person {
// 接受访问的抽象方法,有子类实现
public abstract void accept(FamilyVisitor familyVisitor);
}
Father:
public class Father extends Person
// 实现接受访问的方法,并调用访问者方法访问该对象,下同
@Override
public void accept(FamilyVisitor familyVisitor) {
familyVisitor.visit(this);
}
}
Mather:
public class Mather extends Person{
@Override
public void accept(FamilyVisitor familyVisitor) {
familyVisitor.visit(this);
}
}
Son:
public class Son extends Person{
@Override
public void accept(FamilyVisitor familyVisitor) {
familyVisitor.visit(this);
}
}
Daughter:
public class Daughter extends Person {
@Override
public void accept(FamilyVisitor familyVisitor) {
familyVisitor.visit(this);
}
}
FamilyVisitor:
public interface FamilyVisitor {
// 访问家庭成员的额抽象方法
void visit(Father father);
void visit(Mather mather);
void visit(Son son);
void visit(Daughter daughter);
}
ConcreteFamilyVisitor:
public class ConcreteFamilyVisitor implements FamilyVisitor {
// 对家庭成员的各个访问方法,这里可以看到使用的是具体的类而不是抽象类
@Override
public void visit(Father father) {
System.out.println("visit father");
}
@Override
public void visit(Mather mather) {
System.out.println("visit mather");
}
@Override
public void visit(Son son) {
System.out.println("visit son");
}
@Override
public void visit(Daughter daughter) {
System.out.println("visit daughter");
}
}
Family:
public class Family {
// 成员集合
private List<Person> list;
// 成员初始化
public Family() {
this.list = Arrays.asList(new Father(), new Mather(), new Son(), new Daughter());
}
// 具体的算法,这里表示逐个访问
public void display(FamilyVisitor visitor){
for(Person p : list){
p.accept(visitor);
}
}
}
Client:
public class Client {
public static void main(String[] args) {
Family family = new Family();
ConcreteFamilyVisitor visitor = new ConcreteFamilyVisitor();
family.display(visitor);
}
}
通过上述的实例,可以看到:当有不同的访问者的时候只需要添加Visitor的实现类即可,而不同的访问者对该家庭的访问结果可能不同,比如某一个不在家等(即算法不同)。
迭代器模式
迭代器模式(Iterator Pattern),用于属顺序访问集合对象的元素(及遍历
),这设计模式不需要知道集合对象底层是用何种结构存储数据的,只需要使用其迭代器就可以遍历。通过将Iterator聚合至存储的实体类中,对外就可以获得其迭代器进行遍历。
迭代器模式需要使用:java.util.Iterator接口。
优点:增加迭代器方便,简化聚合类,在一个聚合上可以有多个遍历(见下面实例种的Client种的遍历);
如下实例:
- 需求:对于全国省份中城市的统计,再底层有两种方式的存储方式(数组和List),两种存储结构的遍历方式不同,要求使用一种遍历方式实现全省份中的城市的遍历。
- ProvinceIterator:实现了java中Interater方法;其初始化的时候需要提供给迭代类型的数据集合(数组或List);主要实现hasNext()方法和next()方法提供遍历;
- Province:省份接口,定义的对省份的基本操作方法:1.获取省份名称、2.添加城市、3.获取迭代器;
- ProvinceType1、ProvinceType1:两种不同存储方式省份实现类;
- City:城市对象类;
- Client:对省份实体类的初始化,并使用迭代器进行遍历。
ProvinceType1Iterator:
public class ProvinceType1Iterator implements Iterator {
// 数据集
private City city[];
// 当前位置
private int index;
// 数据集初始化
public ProvinceType1Iterator(City[] city) {
this.city = city;
}
// 判断是否还有剩余元素
@Override
public boolean hasNext() {
if(index > city.length || city[index] == null){
return false;
}
return true;
}
// 获取下一个元素,并使标记+1
@Override
public Object next() {
return city[index++];
}
}
ProvinceType2Iterator:
public class ProvinceType2Iterator implements Iterator<City> {
// 待遍历的数据集
private List<City> city;
// 当前遍历到的位置
private int index = 0;
// 初始化数据集
public ProvinceType2Iterator(List<City> city) {
this.city = city;
}
// 判断是否还有元素
@Override
public boolean hasNext() {
if(index > city.size()-1){
return false;
}
return true;
}
// 获取下一个元素,并使位置标记+1
@Override
public City next() {
return city.get(index++);
}
}
Province:
/**
* 省份信息
*/
public interface Province {
// 获取省份名称
String getName();
// 添加城市
void addCity(String name);
// 获取迭代器
Iterator getIterator();
}
ProvinceType1:
/**
* 第一种类型省份城市数据集合
* 使用数组存放
*/
public class ProvinceType1 implements Province{
// 省份名称
private String name;
// 城市数据集
private City[] cities;
// 当前最后元素位置,便于添加元素
private int index;
// 初始化省份信息
public ProvinceType1(String name) {
this.name = name;
cities = new City[15];
}
// 获取省份名称
@Override
public String getName() {
return name;
}
// 添加城市
@Override
public void addCity(String name) {
City city = new City(name);
cities[index++] = city;
}
// 获取第一种类型的省份的迭代器
@Override
public Iterator getIterator() {
return new ProvinceType1Iterator(cities);
}
}
ProvinceType2:
/**
* 第二种类型省份城市数据集合
* 使用集合存放
*/
public class ProvinceType2 implements Province{
// 名称
private String name;
// 城市数据集
private List<City> cityList;
// 初始化省份信息
public ProvinceType2(String name) {
this.name = name;
cityList = new ArrayList<>();
}
// 获取省份名称
@Override
public String getName() {
return name;
}
// 添加城市
@Override
public void addCity(String name) {
City city = new City(name);
cityList.add(city);
}
// 获取该省份的迭代器
@Override
public Iterator getIterator() {
return new ProvinceType2Iterator(cityList);
}
}
City:
public class City {
private String name;
public City(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "City{" +
"name='" + name + '\'' +
'}';
}
}
Client:
public class Client {
public static void main(String[] args) {
List<Province> list = new ArrayList<>();
ProvinceType1 zj = new ProvinceType1("浙江");
ProvinceType2 js = new ProvinceType2("江苏");
list.add(zj);
list.add(js);
zj.addCity("杭州");
zj.addCity("宁波");
zj.addCity("嘉兴");
js.addCity("苏州");
js.addCity("南京");
js.addCity("无锡");
printAll(list);
/**
* -------------浙江------------
* City{name='杭州'}
* City{name='宁波'}
* City{name='嘉兴'}
* -------------江苏------------
* City{name='苏州'}
* City{name='南京'}
* City{name='无锡'}
*/
printCities(js.getIterator());
/**
* City{name='苏州'}
* City{name='南京'}
* City{name='无锡'}
*/
}
// 打印全部省份的城市信息
public static void printAll(List<Province> list){
for (Province p : list){
System.out.println("-------------" + p.getName()+ "------------");
Iterator iterator = p.getIterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
// 打印某个省份的城市
public static void printCities(Iterator iterator){
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
通过上面的示例可以看到,对于两种不同的存储方式的实体,在使用迭代器模式(实现迭代器接口)后,可以被同时遍历出来(见Client类中的printAll()方法),同时在添加其他类型的存储方式时,也只需要创建其对应的Iterator即可使用,符合OCP原则。
JDK中的使用
典型的是ArrayList,看下面的UML图以及部分源码:
// 对外暴露的方法,直接获得当前arrayList对象的迭代器
public Iterator<E> iterator() {
return new Itr();
}
// 这是它的内部类,直接实现类Iterator接口,并实现方法
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
// 。。。。。other methods
}
通过源码发现:
-
ArrayLits中使用了迭代器,并且作为内部类直接放在其内部,所以它与上面的示例不同,示例是将迭代器聚合到类中在使用,这里是内部内类因此可以直接使用ArrayLits的原始数据:
// ArrayLits成员变量 transient Object[] elementData; // 上面源码中,直接操作之 Object[] elementData = ArrayList.this.elementData;
-
同理,可以看到List接口其他的实现类,也使用了迭代器。
观察者模式
观察者模式(Observer Pattern)体现的是对象间的一对多关系。比如班主任之于学生——当班主任发生变化的时候就会通知全部的学生;比如天气预报接口之于网站——天气变化会通知各个接入的网站进行更新。观察者模式就是这样的模式,当一个对象的状态方式改变,依赖于它的全部对象便会随之改变。
优点:实现观察者与被观察者的解耦;实现触发机制。
缺点:通知全部观察者效率不高且有些情况下不需要全部通知。
如下实例:
- 需求:有一气象站Subject实时发布最新气象数据,多家网站接入该气象站获取其数据,在气象站跟新数据后通知所有接入的网站实现同步更新。
- ISubject:被观察者接口,定义被观察者的基本操作:添加观察者;删除观察者;更新数据;同步推送;
- WeatherSubject:被观察者实现类,对方法实现,并设定自己管理的数据;
- Observer:观察者接口,定义观察者的基本操作,更新数据,和获得数据;
- Website1、Website2:具体的观察者,接入到被观察者,实现具体方法;
- Client:实现观察者接入到被观察者中;被观察者更新数据同步到观察者。
ISubject:
/**
* 主题接口
*/
public interface ISubject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void setData(float temperature, float humidity);
void notifyAllObserver();
}
WeatherSubject:
/**
* 主题实现类
*/
public class WeatherSubject implements ISubject{
// 数据信息
private float temperature;
private float humidity;
// 已接入的观察者Observer
private List<Observer> observerList;
public WeatherSubject() {
this.observerList = new ArrayList<>();
}
// 添加接入的观察者
@Override
public void addObserver(Observer observer) {
observerList.add(observer);
}
// 删除观察者
@Override
public void removeObserver(Observer observer) {
observerList.remove(observer);
}
// 更新主题中的信息,同时调用提醒方法
@Override
public void setData(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
// 调用提示方法
notifyAllObserver();
}
// 提示所有观察者数据更新,并将数据发送给他进行更新
@Override
public void notifyAllObserver() {
for(Observer o : observerList){
o.update(this.temperature, this.humidity);
}
}
}
Observer:
/**
* 观察者接口
*/
public interface Observer {
// 获取观察者信息
String getData();
// 跟新观察者信息
void update(float temperature, float humidity);
}
Website1:
/**
* 网站1
*/
public class Website1 implements Observer {
private float temperature;
private float humidity;
@Override
public String getData(){
return "temperature: " + this.temperature + " humidity: " + this.humidity;
}
@Override
public void update(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
}
}
Website2:
/**
* 网站2
*/
public class Website2 implements Observer{
private float temperature;
private float humidity;
@Override
public String getData(){
return "temperature: " + this.temperature + " humidity: " + this.humidity;
}
@Override
public void update(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
}
}
Client:
public class Client {
public static void main(String[] args) {
// 创建数据中心(气象站)
WeatherSubject subject = new WeatherSubject();
// 网站
Observer website1 = new Website1();
Observer website2 = new Website2();
// 网站接入气象站
subject.addObserver(website1);
subject.addObserver(website2);
// 气象站更新数据
subject.setData(25,60);
System.out.println("website1: " + website1.getData());
System.out.println("website2: " + website2.getData());
/**
* website1: temperature: 25.0 humidity: 60.0
* website2: temperature: 25.0 humidity: 60.0
*/
// 气象站更新数据
subject.setData(35,20);
System.out.println("website1: " + website1.getData());
System.out.println("website2: " + website2.getData());
/**
* website1: temperature: 35.0 humidity: 20.0
* website2: temperature: 35.0 humidity: 20.0
*/
}
}
在JDK中的Observable类中使用到。
中介者模式
中介者模式(Mediator Pattren)可以降低多个类之间通信复杂性。在子系统中的各个对象之间不直接通信,而是通过中介者进行间接通信,由中介者协调处理,以实现子系统之间的解耦。
使用场景:一个子系统中的多个类相互耦合,形成网状结构。
优点:降低类的复杂性和耦合性,符合迪米特原则。
缺点:由中介者协调,当中介者出现问题会导致整个系统出问题;且中介者会变得很大,难以维护。
如下:
- 需求:Person1向Person2发送消息,不能直接发送,而是通过中介者转发实现。如下图,可以看到Person与Person之间是没有联系的(解耦)。
- Mediator:中介者接口由MsgMediator实现,确定基本的方法:注册Colleague和接受中间消息;
- Clleague:抽象类,定义了子系统中的对象的基本方法:接收消息和发送消息;
- Person1、Person2:具体实现类。
- Client:实现Pserson之间的通信。
Mediator:
/**
* 中介者接口
*/
public interface Mediator {
// 将同事对象Colleague注册到中介者中
void register(String name,Colleague colleague);
// 中介者转发消息
void getMsg(String name,String s);
}
Msgmediator:
/**
* 中介者实体
*/
public class Msgmediator implements Mediator{
// 集合:存储Colleague对象
private Map<String,Colleague> map;
public Msgmediator() {
map = new HashMap<>();
}
// 添加Colleague对象
@Override
public void register(String name, Colleague colleague) {
map.put(name, colleague);
}
// 煮饭消息
@Override
public void getMsg(String name,String s) {
if (map.containsKey(name)){
map.get(name).getMsg(s);
}
}
}
Colleague:
/**
* Colleague抽象类
*/
public abstract class Colleague {
public abstract void getMsg(String s);
public abstract void sendMsg();
}
Person1:
/**
* Colleague实例1
*/
public class Person1 extends Colleague{
private Mediator mediator;
// 注册到中介者
public Person1(Mediator mediator) {
this.mediator = mediator;
mediator.register("person1", this);
}
// 接受消息
@Override
public void getMsg(String s) {
System.out.println("Person1 get msg : " + s);
}
// 发送消息
@Override
public void sendMsg() {
mediator.getMsg("person2","hello, i am person1");
}
}
Person2:
/**
* 实例2,同Person1
*/
public class Person2 extends Colleague{
private Mediator mediator;
public Person2(Mediator mediator) {
this.mediator = mediator;
mediator.register("person2", this);
}
@Override
public void getMsg(String s) {
System.out.println("person2 get msg : " + s);
}
@Override
public void sendMsg() {
mediator.getMsg("person1","hello, i am person2");
}
}
Client:
public class Client {
public static void main(String[] args) {
Msgmediator msgmediator = new Msgmediator();
Person1 person1 = new Person1(msgmediator);
Person2 person2 = new Person2(msgmediator);
person1.sendMsg();
person2.sendMsg();
/**
* person2 get msg : hello, i am person1
* Person1 get msg : hello, i am person2
*/
}
}
实例应用:MVC中:C(controller)是M(model)和V(view)的中介者。
备忘录模式
备忘录模式(Memento Pattern)用于保存一个对象的某个状态,当对象被修改之后可以通过备忘录来恢复之前的数据。
备忘录模式可以根据字面意思理解,就是一个备忘的副本,同时也将用户类与备忘录管理类解耦。
优点:提供了对象的状态恢复机制;
缺点:需要额外的资源区存储备忘录信息。
常见场景:撤销操作、回档机制等。
如下:
- 需求:创建一个游戏类,游戏为章节类游戏(状态);玩家可以对当前游戏进度进行保存(可多次保存),当需要回档的时候,只需要选择该备忘录即可。
- Game:游戏类,有一个状态——当前游戏进度(章节),提供保存进度方法和读取存档方法;
- GameMemento:备忘录类,保存了某个时刻Game类的状态;
- GameCraeTaker:备忘录管理类,保存了各个时候的备忘录,对外提供添加备忘录和读取备忘录的方法;
- Cient:玩家创建游戏,并在需要的章节创建进行保存,并在合适的时候读档。
Game:
/**
* 游戏类
*/
public class Game {
// 游戏状态
private String chapter;
public Game() {
}
// 将当前状态添加到备忘录
public void addMemento(GameCareTaker gameCareTaker){
gameCareTaker.addMemento(new GameMemento(this.chapter));
}
// 回档
public void recover(GameCareTaker gameCareTaker,int mementoNum){
GameMemento memento = gameCareTaker.getMemento(mementoNum);
this.chapter = memento.getChapter();
}
public String getChapter() {
return chapter;
}
public void setChapter(String chapter) {
this.chapter = chapter;
}
}
GameMemento:
/**
* 备忘录类,封装了相关的信息
*/
public class GameMemento {
private String chapter;
public GameMemento(String chapter) {
this.chapter = chapter;
}
public String getChapter() {
return chapter;
}
public void setChapter(String chapter) {
this.chapter = chapter;
}
}
GameCareTaker:
/**
* caretaker负责对对象的备忘管理
*/
public class GameCareTaker {
private List<GameMemento> mementos;
public GameCareTaker() {
this.mementos = new ArrayList<>();
}
public void addMemento(GameMemento memento){
mementos.add(memento);
}
public GameMemento getMemento(int mementoNum){
return mementos.get(mementoNum);
}
}
Client:
public class Client {
public static void main(String[] args) {
// 创建游戏
Game game = new Game();
GameCareTaker gameCareTaker = new GameCareTaker();
// 开始第一章
game.setChapter("第一章");
System.out.println("当前游戏在:" + game.getChapter());
// 保存备忘录
game.addMemento(gameCareTaker);
// 开始第五章
game.setChapter("第五章");
System.out.println("当前游戏在:" + game.getChapter());
// 保存到备忘录
game.addMemento(gameCareTaker);
// 开始第六章
game.setChapter("第六章");
System.out.println("当前游戏在:" + game.getChapter());
// 回档到第五章(第二次的备忘录)
game.recover(gameCareTaker, 1);
System.out.println("当前游戏在:" + game.getChapter());
}
}
/*
* 当前游戏在:第一章
* 当前游戏在:第五章
* 当前游戏在:第六章
* 当前游戏在:第五章
*/
解释器模式
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式。
该模式通过一个抽象类定义对一个表达式的解析,比如对四则运算表达式“a+b/c”的解析。
优点:扩展性好,对于添加功能的时候,只需要添加新的解释器即可,不许哟啊在原来的代码上进行添加修改。
缺点:使用的场景比较少;对于复杂的表达式难以维护。
状态模式
状态模式(State Parrten)中,类的操作根据类的状态改变而改变。
如:对于一个人,在青年状态时的操作为学习,在壮年状态时操作为工作,早老年状态时操作为养老等,根据实际的状态而变化其操作。
在状态模式中,有如下构成:
-
Context:上下文,用于保存该类的一个状态,在不同状态下,对于同一个方法具有不同的行为。
-
State:状态类,由一个抽象类派生出来具体的许多子状态;在抽象类中定义了一个对象的所有的可能操作;
子类状态对其操作方法根据自身的需要进行重写。
优点:封装了状态的转换;将一个类的所有状态分装到了一个类中,只需要修改该类的状态就可以改变其行为;
缺点:状态类增加,时系统的而复杂度增加。
如下:
- 需求:用户在某平台上需要上传一个视频作品,在此期间有两个状态(审核、通过);上传时的初始状态为审核,审核通过之后进入完成状态,才能进行播放。
- AbsState:状态抽象类,定义了所有状态可能的操作,具体状态根据实际情况重写相关方法;
- AuditState:审核状态,该状态下作品等待审核且不能播放;
- PassState:通过状态,该状态下作品可以被播放,不需要再被审核;
- ContributionContext:投稿上下文,该类中枚举了该稿件的所有状态,在不同状态下调用相同的方法会有不同的行为;
- Client:客户端,创建稿件,并根据其状态执行操作。
ContributionContext:
/**
* 投稿类的上下文
*/
public class ContributionContext {
private AbsState state;
// 枚举状态
AbsState auditState = new AuditState(this);
AbsState passState = new PassState(this);
// 初始化状态
public ContributionContext() {
this.state = auditState;
}
// 审核操作
public void audit(){
state.audit();
}
// 播放操作
public void play(){
state.play();
}
public AbsState getState() {
return state;
}
public void setState(AbsState state) {
this.state = state;
}
public AbsState getAuditState() {
return auditState;
}
public void setAuditState(AbsState auditState) {
this.auditState = auditState;
}
public AbsState getPassState() {
return passState;
}
public void setPassState(AbsState passState) {
this.passState = passState;
}
}
AbsState:
/**
* 状态抽象类
*/
public abstract class AbsState {
// 修改
public abstract void audit();
// 播放
public abstract void play();
}
AuditState:
/**
* 审核状态
*/
public class AuditState extends AbsState{
private ContributionContext context;
public AuditState(ContributionContext context) {
this.context = context;
}
@Override
public void audit() {
System.out.println("审核通过~");
// 审核通过,并切换状态
context.setState(context.getPassState());
}
@Override
public void play() {
System.out.println("审核未通过,不能播放~");
}
}
PassState:
/**
* 审核通过状态类
*/
public class PassState extends AbsState{
private ContributionContext context;
public PassState(ContributionContext context) {
this.context = context;
}
@Override
public void audit() {
System.out.println("已审核,不许需要再次审核~");
}
@Override
public void play() {
System.out.println("正在播放~");
}
}
Client:
public class Client {
public static void main(String[] args) {
ContributionContext context = new ContributionContext();
// 直接播放
context.play();
// 审核
context.audit();
// 再审核
context.audit();
// 播放
context.play();
/**
* 审核未通过,不能播放~
* 审核通过~
* 已审核,不许需要再次审核~
* 正在播放~
*/
}
}
策略模式
策略模式(Strategy Pattern)实现一个类的行为再运行时更改。该类通过在运行时修改其策略来实现行为的更改。
使用场景:如Person类有多个继承,不同的子类拥有与Person类相近的新为,在一些特别的类中可能会对某个方法进行重写;也有特殊情况下:一个类属于Person范畴,但是其重写Person的全部方法。对于这种情况单纯使用继承解决就不显得那么灵活,就可以使用策略模式。通过聚合或组合
的方式将策略分装到类中。
优点:行为变化灵活;可扩展性好;避免多重判断;
缺点:策略类多样不利于维护。
如下:
- 需求:有一个演员类,其行为是表演,但是表演的节目不确定需要根据实际情况灵活改变(唱歌、跳舞、书法)。
- Strategy:策略接口,定义了一个表演的抽象方法,具体表演类型有其实现类进行具体实现;
- Sing、Dance、Calligraphy:具体的策略类,实现的接口中的方法。
- Actor:演员类,通过聚合将策略分装到类中,根据策略的不同将执行不同的行为。也可以随时修改其策略;
- Client:客户端,创建演员类,并赋予策略使其执行相关的行为。
Strategy:
/**
* 策略接口
*/
public interface Strategy {
void performance();
}
Sing:
/**
* 唱歌表演
*/
public class Sing implements Strategy{
@Override
public void performance() {
System.out.println("唱歌~");
}
}
Dance:
/**
* 跳舞表演
*/
public class Dance implements Strategy{
@Override
public void performance() {
System.out.println("跳舞~");
}
}
Calligraphy:
/**
* 书法表演
*/
public class Calligraphy implements Strategy{
@Override
public void performance() {
System.out.println("书法~");
}
}
Actor:
/**
* 演员类
*/
public class Actor {
// 演员行为策略
private Strategy strategy;
public Actor(Strategy strategy) {
this.strategy = strategy;
}
// 根据策略的不同执行不同的方法
public void performance(){
if (strategy != null){
strategy.performance();
}
}
// 重新设置策略
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
}
Client:
public class Client {
public static void main(String[] args) {
// 创建一个演员,策略为Sing
Actor actor = new Actor(new Sing());
actor.performance();
// 设置策略为Dance
actor.setStrategy(new Dance());
actor.performance();
// 设置策略为Calligraphy
actor.setStrategy(new Calligraphy());
actor.performance();
/**
* output:
* 唱歌~
* 跳舞~
* 书法~
*/
}
}
JDK中的使用
Comparator接口的使用。
- Comaprator就是一个策略接口,其内部有一个方法——compare();
- 实现该接口之后重写compare()方法就是具体的策略,比如升序、降序等;
- 在集合List或Arrays中调用sort()方法时就会传入这个具体的策略,根据策略执行相关的行为。
责任链模式
责任链模式(Chain of Responsibility Patterb)为请求创建了一个接收者对象的链,当第一个对象不能处理该请求时会将该请求传给下一个接收者,再不行再传,直到找到合适的处理方式为止。
在责任链模式中是通过一个接收者中聚合了另一个接收者以实现链(或环)。
实际应用场景:servlet中的Filter链、拦截器等
优点:解耦,将请求对象与接收对象分离;使接收者处理的方式更灵活;在添加新的接收者的时候会很方便。
缺点:如果链很长,而且总是被链的最后的接收者接收,那么会消耗大量的资源,系统性能收到影响。
如下:
- 需求:找位置,对于三个用户(tom、jerry、lucy)都有自己特定的位置,每个人只能坐自己的位置,以此实现他们找到自己的位置坐下。
- Person:用户类,封装了用户的姓名信息,也就是责任链模式中的请求者;
- AbsSeat:作为接收者的抽象类,统一接收者的方法和属性(在类中维护了一个其他的接受者);
- TomSeat、JerrySeat、LucySeat:三个人个自的作为(接受者),每个作为只能坐下对应的用户;
- Client:客户端,创建用户、接收者并形成责任链,在将请求发送给责任链,开始寻找个自的座位。
Person:
/**
* 用户,接收者
*/
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
AbsSeat:
/**
* 接收者抽象类
*/
public abstract class AbsSeat {
// 下一个接收者
protected AbsSeat seat;
// 设置下一个接收者
public void setSeat(AbsSeat seat) {
this.seat = seat;
}
// 各个实现对象的具体逻辑
public abstract void sitDown(Person person);
}
TomSeat:
/**
* tom 的作为
*/
public class TomSeat extends AbsSeat{
@Override
public void sitDown(Person person) {
if(person.getName() != null && person.getName().equals("tom")){
System.out.println("tom find his seat");
}else{
// 如果来的不是tom,则寻找下一个对象是否匹配
seat.sitDown(person);
}
}
}
LucySeat:
/**
* lucy 的对象
*/
public class LucySeat extends AbsSeat{
@Override
public void sitDown(Person person) {
if(person.getName() != null && person.getName().equals("lucy")){
System.out.println("lucy find his seat");
}else{
seat.sitDown(person);
}
}
}
JerrySeat:
/**
* jerry 的作为
*/
public class JerrySeat extends AbsSeat{
@Override
public void sitDown(Person person) {
if(person.getName() != null && person.getName().equals("jerry")){
System.out.println("jerry find his seat");
}else{
seat.sitDown(person);
}
}
}
Client:
public class Client {
public static void main(String[] args) {
Person tom = new Person("tom");
// 创建对象并形成责任链
TomSeat tomSeat = new TomSeat();
LucySeat lucySeat = new LucySeat();
JerrySeat jerrySeat = new JerrySeat();
tomSeat.setSeat(lucySeat);
lucySeat.setSeat(jerrySeat);
jerrySeat.setSeat(tomSeat);
// tom开始寻找自己的位置
lucySeat.sitDown(tom);
// tom find his seat
// StackOverflowError
// 由于该责任链时一个循环,没有该用户的位置将会导致栈溢出,可设置判定条件结束循环
lucySeat.sitDown(new Person("amy"));
}
}
public abstract void sitDown(Person person);
}
**TomSeat:**
```java
/**
* tom 的作为
*/
public class TomSeat extends AbsSeat{
@Override
public void sitDown(Person person) {
if(person.getName() != null && person.getName().equals("tom")){
System.out.println("tom find his seat");
}else{
// 如果来的不是tom,则寻找下一个对象是否匹配
seat.sitDown(person);
}
}
}
LucySeat:
/**
* lucy 的对象
*/
public class LucySeat extends AbsSeat{
@Override
public void sitDown(Person person) {
if(person.getName() != null && person.getName().equals("lucy")){
System.out.println("lucy find his seat");
}else{
seat.sitDown(person);
}
}
}
JerrySeat:
/**
* jerry 的作为
*/
public class JerrySeat extends AbsSeat{
@Override
public void sitDown(Person person) {
if(person.getName() != null && person.getName().equals("jerry")){
System.out.println("jerry find his seat");
}else{
seat.sitDown(person);
}
}
}
Client:
public class Client {
public static void main(String[] args) {
Person tom = new Person("tom");
// 创建对象并形成责任链
TomSeat tomSeat = new TomSeat();
LucySeat lucySeat = new LucySeat();
JerrySeat jerrySeat = new JerrySeat();
tomSeat.setSeat(lucySeat);
lucySeat.setSeat(jerrySeat);
jerrySeat.setSeat(tomSeat);
// tom开始寻找自己的位置
lucySeat.sitDown(tom);
// tom find his seat
// StackOverflowError
// 由于该责任链时一个循环,没有该用户的位置将会导致栈溢出,可设置判定条件结束循环
lucySeat.sitDown(new Person("amy"));
}
}