行为型设计模式
该文章为个人学习总结,仍有未完善地方,案例以尚硅谷为例。
模板模式
- 定义:通过抽象类定义好的抽象行为,将公共的行为抽象出来,通过抽象来调用,让具体类去实现。
- 思想:就是定义抽象类,将多个类的公共方法和属性进行抽象出来,是我们最常使用的模式。不过模板方法与建造者模式不同之处在于,模板模式目的在于抽象出公共的互相联系的方法调用,其中部分方法甚至需要具体子类去实现,而建造者模式则在于创建一个对象。 模板模式可以说是对继承抽象类的使用讲解了更丰富的用法。
- 钩子方法:在抽象类中定义一个空方法,该方法由子类去决定是否重写,当重写后来改变公共方法的执行过程。
应用场景
Spring中的ConfigurableApplicationContext中使用了模板模式。
案例代码
public abstract class BaoZi {
public void makeBaoZi(){
prepare();
makeCheek();
wrapper();
steam();
}
public void prepare(){
System.out.println("和面团");
}
public void makeCheek(){
System.out.println("做面皮");
}
public void wrapper(){
if (hasStuffing()){
System.out.println("包"+stuffing()+"包子");
}else {
System.out.println("做馒头");
}
}
public void steam(){
System.out.println("蒸煮中");
}
public abstract String stuffing();
public boolean hasStuffing(){
return true;
}
}
public class BeefBaoZi extends BaoZi {
@Override
public String stuffing() {
return "牛肉";
}
}
public class VegetableBaoZi extends BaoZi {
@Override
public String stuffing() {
return "酸菜";
}
}
public class TemplateTest {
public static void main(String[] args) {
VegetableBaoZi vegetableBaoZi = new VegetableBaoZi();
vegetableBaoZi.makeBaoZi();
BeefBaoZi beefBaoZi = new BeefBaoZi();
beefBaoZi.makeBaoZi();
}
}
命令模式
- 定义:将请求者发送者和请求接收者间的耦合消除,也即将请求封装到一个对象中,接收者引用一个抽象接口来接收,实现和扩展更加灵活。涉及的对象一般为三个:请求者,请求和接收者。
- 思想:接收者属于被动对象,应组合或聚合在请求对象中,而请求对象通过抽象接口聚合或组合在发送者中。当需要扩展时,只需实现请求的抽象类,并将接收者组合或聚合在具体请求中。这提供了统一的处理模式,对请求者来说只需传入请求体和接收者,便可通过统一的接口来调用函数。
- 应用:当需要通过统一的接口函数来实现对不同对象的相似操作时,可令请求来实现对接收者的具体操作,而发送者只要开启操作便可。
- 应用场景:jdbcTemplate
UML
代码案例
public abstract class Command {
public abstract void execute();
public abstract void undo();
}
public class LightOffCommand extends Command{
private LightReceiverCommand lightReceiverCommand;
public LightOffCommand(LightReceiverCommand lightReceiverCommand) {
this.lightReceiverCommand = lightReceiverCommand;
}
@Override
public void execute(){
System.out.println("关灯信号");
lightReceiverCommand.off();
}
@Override
public void undo() {
lightReceiverCommand.on();
}
}
public class LightOnCommand extends Command{
private LightReceiverCommand lightReceiverCommand;
public LightOnCommand(LightReceiverCommand lightReceiverCommand) {
this.lightReceiverCommand = lightReceiverCommand;
}
@Override
public void execute(){
System.out.println("开灯信号");
lightReceiverCommand.on();
}
@Override
public void undo() {
lightReceiverCommand.off();
}
}
public class LightReceiverCommand {
public void on(){
System.out.println("灯开了");
}
public void off(){
System.out.println("灯关了");
}
}
public class RemoteController {
private Command[] onCommands ;
private Command[] offCommands ;
private Command undoCommand;
public RemoteController() {
this.onCommands =new Command[5];;
this.offCommands = new Command[5];
for (int i = 0; i < 5; i++) {
this.offCommands[i] = new NoCommand();
this.onCommands[i] = new NoCommand();
}
}
public void setCommand(int num,Command onCommand,Command offCommand){
this.onCommands[num] = onCommand;
this.offCommands[num] = offCommand;
}
public void onButtonWasPushed(int num){
(undoCommand=this.onCommands[num]).execute();
}
public void offButtonWasPushed(int num){
(undoCommand=this.offCommands[num]).execute();
}
public void undo(){
undoCommand.undo();
}
}
public class OrderTest {
public static void main(String[] args) {
LightReceiverCommand light = new LightReceiverCommand();
LightOnCommand onCommand = new LightOnCommand(light);
LightOffCommand offCommand = new LightOffCommand(light);
RemoteController controller = new RemoteController();
controller.setCommand(0,onCommand,offCommand);
controller.onButtonWasPushed(0);
controller.undo();
}
}
访问者模式
- 定义:在被访问的类里面添加一个接待访问者的接口,访问者进入后便通过被访问的类来做出相应的功能。
- 双分派:将具体状态作为参数传入对象,为一次分派,在将该对象作为参数传入给具体状态,为第二次分派。双分派能达到解耦的效果,可对两者进行同时初始化操作。
- 注意:这种模式适合根据被访问的对象不同,而改变访问者执行形式的情况,但由于在抽象层使用了双分派,所以抽象层的耦合性增加,改变时需要对两个抽象层都进行修改。
代码
public interface Person {
default void getAction(Action action){
action.getResult(this);
}
}
public class Man implements Person {
/**
* 双分派
* @param action
*/
@Override
public void getAction(Action action) {
action.getResult(this);
}
}
public class Woman implements Person {
@Override
public void getAction(Action action) {
action.getResult(this);
}
}
public abstract class Action {
public abstract void getResult(Man man);
public abstract void getResult(Woman woman);
public abstract void getResult(Person person);
}
public class Success extends Action {
@Override
public void getResult(Man man) {
System.out.println("男嘉宾觉得ok");
}
@Override
public void getResult(Woman woman) {
System.out.println("女嘉宾觉得ok");
}
@Override
public void getResult(Person person) {
System.out.println("其它人觉得ok");
}
}
public class Fail extends Action
{
@Override
public void getResult(Man man) {
System.out.println("男嘉宾觉得不行");
}
@Override
public void getResult(Woman woman) {
System.out.println("女嘉宾觉得不行");
}
@Override
public void getResult(Person person) {
}
}
public class Client {
public static void main(String[] args) {
Person man = new Man();
Person woman = new Woman();
Action success = new Success();
man.getAction(success);
woman.getAction(success);
Girl girl = new Girl();
girl.getAction(success);
Action fail = new Fail();
man.getAction(fail);
}
}
迭代器模式
- 定义:提供统一的接口来提供对不同的集合进行统一的遍历,不暴露内部数据结构。
- 思想:需要两个接口Iterable和Iterator。前者表示其实现类的元素数据结构已经有相应的Iterator迭代器,可获取元素的迭代器,后者则根据Iterable实现类元素的数据结构,进行具体的迭代遍历实现。
- 目的: 单一职责原则,迭代器很好的将管理对象的功能和迭代遍历的功能分开,这样两个功能间的耦合性降低。
- 应用场景:在最常见的集合类Collection下的实现类都具有迭代器。而且每个迭代器都在实现类内部。
- 思考:迭代器
类图
每个Iterable的实现类都会有对应的Iterator具体的迭代器,这里迭代器也可以组合在其内部。
案例代码
public class Department {
private String name;
private String desc;
public Department(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ComputerCollegeIterator implements Iterator {
private Department[] departments;
private int position = 0;
public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
return !(position>=departments.length||departments[position]==null);
}
@Override
public Object next() {
return departments[position++];
}
@Override
public void remove() {
}
}
public class InfoCollegeIterator implements Iterator {
private List<Department> departments;
private int index = -1;
public InfoCollegeIterator(List<Department> departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
if (index>=departments.size()-1){ return false; }
else{
index++;
return true;
}
}
@Override
public Object next() {
return departments.get(index);
}
@Override
public void remove() {
}
}
public class InfoCollege implements College {
private List<Department> departments;
public InfoCollege() {
this.departments = new ArrayList<>();
addDepartment("信息专业","信息安全专业");
addDepartment("网络专业","网络安全专业");
addDepartment("服务器专业","服务器安全专业");
}
@Override
public String getName() {
return "信息工程学院";
}
@Override
public void addDepartment(String name, String desc) {
departments.add(new Department(name,desc));
}
@Override
public Iterator getIterator() {
return new InfoCollegeIterator(departments);
}
}
public class ComputerCollege implements College{
private Department[] departments;
private int numOfDepartment = 0;
public ComputerCollege() {
this.departments = new Department[3];
addDepartment("java","计算机程序语言");
addDepartment("PHP","PHP专业");
addDepartment("大数据","大数据专业");
}
@Override
public String getName() {
return "计算机学院";
}
@Override
public void addDepartment(String name,String desc) {
Department department = new Department(name,desc);
departments[numOfDepartment++] = department;
}
@Override
public Iterator getIterator() {
return new ComputerCollegeIterator(departments);
}
}
public interface College {
public String getName();
public void addDepartment(String name,String desc);
public Iterator getIterator();
}
public class PrintOutPut {
private List<College> colleges;
public PrintOutPut(List<College> colleges) {
this.colleges = colleges;
}
public void printAll(){
Iterator<College> iterator = colleges.iterator();
while (iterator.hasNext()){
College college = iterator.next();
System.out.println("========="+college.getName()+"========");
printDepartment(college.getIterator());
}
}
public void printDepartment(Iterator iterator){
while (iterator.hasNext()){
Department department = (Department) iterator.next();
System.out.println(department.getName() + department.getDesc());
}
}
}
public class IteratorTest {
public static void main(String[] args) {
Set set = new HashSet();
Iterator iterator = set.iterator();
ArrayList<College> colleges = new ArrayList<>();
colleges.add(new ComputerCollege());
colleges.add(new InfoCollege());
PrintOutPut printOutPut = new PrintOutPut(colleges);
printOutPut.printAll();
}
}
观察者模式
- 定义:对象间多对一依赖的一种处理方案,当一的一端发生改变时,自动维护更新多的一端的状态。
- 思想:在被观察者内部维护一个观察者链表,当被观察者数据发送改变时,遍历通知观察者传递改变后的参数。
- 不足:当改变的状态较多时,显得复杂,可理解性差。可以将数据封装为对象或抽象类。
此外,在观察者模式的基础上演变出监听者模式。
类图
代码
public interface IObservable {
// 添加观察者
void addObserve(IObserve observable);
// 删除观察者
void removeObserve(IObserve observable);
// 向观察者发送信息
void notifyObservers(String message);
}
public interface IObserve {
// 观察者处理被观察者发送过来额信息
void handleNotify(String message);
}
public class FistObserve implements IObserve {
@Override
public void handleNotify(String message) {
System.out.println("1号接受的消息" + message);
}
}
public class SecondObserve implements IObserve {
@Override
public void handleNotify(String message) {
System.out.println("2号接受的消息"+message);
}
}
public class Test {
public static void main(String[] args) {
//创建多个观察者
IObserve iObserve1 = new FistObserve();
IObserve iObserve2 = new SecondObserve();
//创建被观察者
Some some =new Some();
//被观察者添加观察者
some.addObserve(iObserve1);
some.addObserve(iObserve2);
//被观察者向观察者发送信息
some.notifyObservers("GOGOGOGOGOGO");
//删除观察者后
some.removeObserve(iObserve1);
some.notifyObservers("GO ");
Observable observable;
}
}
监听者模式
监听器设计模式是观察者设计模式的一种实现。他与观察者设计模式不同点有
-
1.一个被观察者只能有一个观察者对象,而不是多个
-
2.被监听者(事件源)的状态改变,被定义为一个对象,称为事件,而不是字符串
-
3.监听器(监听者)负责监听发生在事件源上的事件.
-
事件源(被监听对象,产生事件的对象):提供订阅与取消监听者的方法,并负责维护监听者列表,发送事件给监听者
-
事件处理器
监听器的成员方法,当时事件发生后会触发对应的处理器(成员方法)。
一般情况下,监听器对象被事件触发后,都是需要从事件中获取到事件源对象,然后再从事件源中获取一些数据。也就是说,在事件对象中一般需要提供获取事件源对象的方法,当然,除了获取事件源方法外,根据业务需求,事件对象一般还需要提供一些其他的数据,以便让监听器获取。
监听器在进行工作时,可以分为以下步骤
1.将监听器绑定到事件源(注册监听器)
2.事件发生后触发监听器的成员方法,即事件处理器,传递事件对象
3.事件处理器通过事件对象获得事件源,并对事件源进行处理。
在监听器模式下,监听器负责对产生的事件对象给予的信息作出相应的处理;事件对象是根据事件源情况而生成的,
并给自己定义出某一确定的事件类型供监听器获取后进行抉择;事件源则通过注册监听器,并将生产出的事件触发监听器。
监听器模式相较于观察者模式,多了一个中间的事件对象,且由该对象触发监听器而不是一个基本参数。
也就是说,事件对象可以封装更加丰富的需要供处理的数据给监听器。
类图
代码案例
事件类,当传递一个事件时,顺便把产生该事件的事件源携带过去。
//通常对于事件对象,我们一般需要从事件对象中获取到事件源对象
public interface ICurdEvent{
//声明事件类型
String Create_EVENT = "create event";
String Update_EVENT = "update event";
String Retrieve_EVENT = "retrieve event";
String Delete_EVENT = "delete event";
//获取事件源对象
IListenerable getEventSource();
//获取事件类型
String getEventType();
}
监听者,类似于观察者
public interface IListener {
void handle(ICurdEvent event);
}
被监听者,里面注册监听者,并提供触发器,当调用触发器的方法时,会产生相应的带有不同标志的
public interface IListenerable {
//为事件源注册监听器
void setListener(IListener listener);
//触发监听器
void triggerListener(ICurdEvent event);
}
事件类,通过被监听者触发了什么条件,传入对应的触发标志构造对应的事件对象,本身与监听者共同规定了约束。
//事件对象。事件对象保存了一些需要的数据之外,还带有事件源信息eventSource
public class CurdEvent implements ICurdEvent {
private IListenerable eventSource; //事件源
private String methodname; //事件源所执行的方法名称
public CurdEvent(IListenerable eventSource,String methodname){
this.eventSource = eventSource;
this.methodname = methodname;
}
@Override
public IListenerable getEventSource() {
return eventSource;
}
//根据事件源所执行的不同的方法返回不同的事件类型
@Override
public String getEventType() {
String eventType = null;
if (methodname.startsWith("save")){
eventType = Create_EVENT;
}else if (methodname.startsWith("remove")){
eventType = Delete_EVENT;
}else if (methodname.startsWith("modify")){//修改
eventType = Update_EVENT;
}else if (methodname.startsWith("find")){
eventType = Retrieve_EVENT;
}else {
eventType = "have not this event type";
}
return eventType;
}
}
测试
public class Test {
public static void main(String[] args) {
//定义监听器
IListener listener = new Listener();
//定义事件源
Some some = new Some();
//事件源注册监听器
some.setListener(listener);
some.saveStudent();
some.removeStudent();
}
}
中介者模式
- 定义:用一个中介对象封装对象之间的交互。中介者使服务的对象之间不需要互相显示的调用。将调用封装到中介者中,从而降低子系统耦合性。
- 思想:让中介者通过数据结构保存所有参与的对象的引用信息,并将对象间的交互行为在中介者内部定义,而对象则保有中介者引用。当触发时,通过参数和自身信息,让中介者找到指定方法,然后调用关系函数进行处理。
UML类图
备忘录模式Memento
定义:在不破化封装性的前提下,捕获该对象的状态,并在外部保存,之后用于恢复到原先的状态。
思想:用一个对象memento来保存另一个对象的状态。当要恢复时,传入memento并获取里面保存的状态。
注意:频繁的备忘容易消耗太多资源。
适用:存档,回退,事务回滚等。
代码
public class CareTaker {
private List<Memento> mementos = new ArrayList<>();
public void add(Memento memento){
mementos.add(memento);
}
public Memento getMemento(int index){
return mementos.get(index);
}
}
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
public class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento saveMemento(){
return new Memento(this.state);
}
public void getFromMemento(Memento memento){
state = memento.getState();
}
}
public class Test {
public static void main(String[] args) {
Originator originator = new Originator();
originator.setState("攻击力:100");
System.out.println(originator.getState());
CareTaker careTaker = new CareTaker();
careTaker.add(originator.saveMemento());
originator.setState("攻击力:999");
System.out.println(originator.getState());
originator.getFromMemento(careTaker.getMemento(0));
System.out.println("恢复到"+originator.getState());
}
}
解释器模式
定义:给定一个语言或表达式,定义它的文法的一种表示,并定义一个解释器来解释该语言。
代码
public abstract class Expression {
public abstract int interpreter(HashMap<String,Integer> var);
}
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var)-super.right.interpreter(var);
}
}
public abstract class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
}
策略模式
定义:将超类中可能因子类的不同情况发生变化的部分封装成接口,将算法的实现从行为分离出来。
场景:在子类多层继承并重写父类方法时,会使得子类都需要重写父类的方法,使得父类失去了其存在价值,所以需要将父类可能需要重写的方法封装成一个接口聚合在父类中,由子类去具体决定行为,未提供具体的行为的子类仍可使用父类的方法。将变化从稳定中抽离出来,可以让变化部分彼此隔离,即便是多层继承,也互不影响。
JDK使用场景
Comparator接口是一个行为的策略接口。
类图
代码
public abstract class Duck {
protected FlyBehavior flyBehavior;
protected QuackBehavior quackBehavior;
public void fly(){
if (flyBehavior!=null){
flyBehavior.fly();
}else {
System.out.println("会飞翔");
}
}
public void swimming(){
System.out.println("会游泳");
}
public void quack(){
if (quackBehavior!=null){
quackBehavior.quack();
}else {
System.out.println("会嘎嘎叫");
}
}
}
public interface FlyBehavior {
void fly();
}
public class NoFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("不会飞");
}
}
public interface QuackBehavior {
void quack();
}
public class NoQuackBehavior implements QuackBehavior {
@Override
public void quack() {
System.out.println("无法叫");
}
}
public class ToyDuck extends Duck {
public ToyDuck(){
flyBehavior = new NoFlyBehavior();
quackBehavior = new NoQuackBehavior();
}
}
public class WildDuck extends Duck {
}
public class Test {
public static void main(String[] args) {
WildDuck wildDuck = new WildDuck();
wildDuck.fly();
wildDuck.quack();
wildDuck.swimming();
ToyDuck toyDuck = new ToyDuck();
toyDuck.quack();
toyDuck.swimming();
toyDuck.fly();
}
}
职责链模式
定义:将请求和处理逻辑分离,实现解耦,提高系统的灵活性。
缺点:当处理链增长时会导致性能降低,可以在Handler中设置最大节点数,判断是否超过最大数,超过则限制放入。
应用场景:多级请求,多层的判断或多级处理。
类图
代码
public abstract class Approver {
Approver approver;
String name;
public Approver(String name) {
this.name = name;
}
public void setApprover(Approver approver) {
this.approver = approver;
}
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
public class CollegeApprover extends Approver {
public CollegeApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()>=5000&&purchaseRequest.getPrice()<10000){
System.out.println("被"+name+"处理了");
}else {
approver.processRequest(purchaseRequest);
}
}
}
public class DepartmentApprover extends Approver {
public DepartmentApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()<5000){
System.out.println("被"+name+"处理");
}else{
approver.processRequest(purchaseRequest);
}
}
}
public class PurchaseRequest {
private int type;
private float price = 0.0f;
private int id = 0;
public PurchaseRequest(int type, float price, int id) {
this.type = type;
this.price = price;
this.id = id;
}
public int getType() {
return type;
}
public float getPrice() {
return price;
}
}
public class SchoolApprover extends Approver {
public SchoolApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()>=15000){
System.out.println("被"+name+"处理了");
}else {
System.out.println("无法处理");
}
}
}
public class ViceSchoolApprover extends Approver {
public ViceSchoolApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()>=10000&&purchaseRequest.getPrice()<15000){
System.out.println("被"+name+"处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
public class Client {
public static void main(String[] args) {
PurchaseRequest purchaseRequest = new PurchaseRequest(1,8000,1);
SchoolApprover schoolApprover = new SchoolApprover("校长");
ViceSchoolApprover viceSchoolApprover = new ViceSchoolApprover("副校长");
viceSchoolApprover.setApprover(schoolApprover);
CollegeApprover collegeApprover = new CollegeApprover("院长");
collegeApprover.setApprover(viceSchoolApprover);
DepartmentApprover departmentApprover = new DepartmentApprover("系主任");
departmentApprover.setApprover(collegeApprover);
departmentApprover.processRequest(purchaseRequest);
}
}