本文接着介绍行为设计模式中的行为模式,太长分为两部分来介绍。没看过之前的介绍的可以看以前的介绍的创建型模式和结构型模式。
创建型模式
结构型模式
16职责链模式
16.1行为模式的定义
行为模式关注系统中对象之间的交互,研究系统在运行时对象之间的相互通信协作,进一步明确对象的职责。
行为模式不仅关注类和对象本身,还重点关注他们之间的相互作用和职责划分。
行为模式分为类行为模式 和对象行为模式两种
16.2职责链模式的定义
避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理他为止。
比如关于学生的事务,总是先从班主任处理开始,处理不了找年纪主任,再处理不了再找院长再到校长
16.3职责链模式的结构
- Handler(抽象处理者):它定义了一个处理请求的接口,一般设计为抽象课,由于不同的处理者处理请求方式不同,因此在其中定义看抽象请求处理方法。
- ConcreteHandler(具体处理者):它是抽象处理者的子类,可以处理用户请求,在具体处理者类中定义抽象处理方法,再处理请求 方法.
16.4职责链模式的实现
我们得有一个请求的实例类
public class PurchasRequest {
private double amount;
private int number;
private String purpose;
public PurchasRequest(double amount, int number, String purpose) {
this.amount = amount;
this.number = number;
this.purpose = purpose;
}
//getter and setter
}
在写一个处理者的抽象类,让不同类型的处理者都继承这个类
public abstract class Approver {
protected Approver approver;
protected String name;
public Approver(String name) {
this.name = name;
}
public void setApprover(Approver approver) {
this.approver = approver;
}
public abstract void processRequest(PurchasRequest request);
}
public class Director extends Approver {
public Director(String name) {
super(name);
}
@Override
public void processRequest(PurchasRequest request) {
if(request.getAmount()<50000){
System.out.println("主任"+this.name+"审批采购单"+request.getNumber()+"金额"+request.getAmount()+"元,采购目的为"+request.getPurpose());
}else {
this.approver.processRequest(request);
}
}
}
public class VicePresident extends Approver {
public VicePresident(String name) {
super(name);
}
@Override
public void processRequest(PurchasRequest request) {
if(request.getAmount()<100000){
System.out.println("副董事"+this.name+"审批采购单"+request.getNumber()+"金额"+request.getAmount()+"元,采购目的为"+request.getPurpose());
}else {
this.approver.processRequest(request);
}
}
}
public class President extends Approver {
public President(String name) {
super(name);
}
@Override
public void processRequest(PurchasRequest request) {
if(request.getAmount()<500000){
System.out.println("董事长"+this.name+"审批采购单"+request.getNumber()+"金额"+request.getAmount()+"元,采购目的为"+request.getPurpose());
}else {
this.approver.processRequest(request);
}
}
}
public class Congress extends Approver {
public Congress(String name) {
super(name);
}
@Override
public void processRequest(PurchasRequest request) {
System.out.println("召开董事会审批采购单"+request.getNumber()+"金额"+request.getAmount()+"元,采购目的为"+request.getPurpose());
}
}
客户端测试,创建请求,创建处理对象,形成职责链,将请求传入最低级的处理,处理链就开始运转
public class Client {
public static void main(String[] args) {
Approver lijie = new Director("李杰");
Approver haifen = new VicePresident("海峰");
Approver zhiyuan = new President("志远");
Approver donshihui = new Congress("董事会");
lijie.setApprover(haifen);
haifen.setApprover(zhiyuan);
zhiyuan.setApprover(donshihui);
//donshihui.setApprover(lijie);
PurchasRequest request1 = new PurchasRequest(45000,1001,"圣剑");
lijie.processRequest(request1);
PurchasRequest request2 =new PurchasRequest(60000,1002,"龙虾");
lijie.processRequest(request2);
PurchasRequest request3 = new PurchasRequest(200000,1003,"买房");
lijie.processRequest(request3);
PurchasRequest request4 = new PurchasRequest(800000,1004,"买挂");
lijie.processRequest(request4);
//主任李杰审批采购单1001金额45000.0元,采购目的为圣剑
//副董事海峰审批采购单1002金额60000.0元,采购目的为龙虾
//董事长志远审批采购单1003金额200000.0元,采购目的为买房
//召开董事会审批采购单1004金额800000.0元,采购目的为买挂
}
}
16.5纯与不纯的职责链模式
纯的职责链模式
就是一个具体的处理者要么将请求完全处理,要么就不处理,交给下一级
不纯的职责链模式
允许处理部分请求,把处理不了的请求交给下一级。
16.6职责链模式的优缺点
- 优点:一个对象无须知道是其他哪一个对象处理其请求,降低了系统的耦合度,简化了对象之间相互连接,给对象的职责分配多了很多灵活性,添加处理者十分方便
- 缺点:不能保证请求一定会被处理,对于较长的请求链对系统的性能会有一定的影响。创建请求链出问题,可能会导致死循环
16.6职责链的适用环境
有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端可以动态的创建职责链处理请求,还可以改变处理的先后次序。
17命令模式
17.1命令模式的定义
将一个请求封装为一个对象,从而可用不同的请求对客户端进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。
例如开关和家用电器,对于电器和开关分离,只知道开关可以打开一个电器,但是并不知道是哪一个电器
17.2命令模式的结构
- Command(抽象命令类):一般是抽象类或者接口,在其中声明了用于执行请求的execute()方法,通过这些方法调用请求者操作
- ConcreteCommand(具体命令类):具体方法时抽象命令的子类,实现在抽象命令中声明的方法,他具体对应的接收对象,将接收者对象的动作绑定其中。
- Invoker(调用者):调用请求者即请求发送者,它通过命令来执行请求。
- Receiver(接收者):接收者执行与请求相关的操作,具体实现对请求业务处理。
17.3命令模式的实现
比如我们在使用电脑时,为了简化用户,添加了功能键,可以自定义功能。
我们先写一个按钮键,里面引入一个命令,点击按键执行命令的方法
public class FunctionButton {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void click(){
System.out.println("单机功能键:");
command.execute();
}
}
写一个命令的抽象类,可以是一个命令的统一的接口方式
public abstract class Command {
public abstract void execute();
}
分别写命令实现这个接口,里面要传入执行的对象
public class ExitCommand extends Command{
private SystemExitClass exitClass;
public ExitCommand() {
exitClass = new SystemExitClass();
}
@Override
public void execute() {
exitClass.exit();
}
}
public class HelpCommand extends Command {
private DisplayHelpClass helpClass;
public HelpCommand() {
helpClass = new DisplayHelpClass();
}
@Override
public void execute() {
helpClass.display();
}
}
写具体的执行内容
public class DisplayHelpClass {
public void display(){
System.out.println("显示帮助文档");
}
}
public class SystemExitClass {
public void exit(){
System.out.println("退出系统");
}
}
测试我们也使用xml配置里面把主要命令的实例对象的类名写入,通过xml工具类对其调用获取实例对象,往button中传入命令,执行即可
public class Client {
public static void main(String[] args) {
Command help =(Command) XMLUtil.getBean(0);
FunctionButton button =new FunctionButton();
button.setCommand(help);
button.click();
System.out.println("=================");
Command display =(Command)XMLUtil.getBean(1);
FunctionButton button1 =new FunctionButton();
button1.setCommand(display);
button1.click();
//单机功能键:
//显示帮助文档
//=================
//单机功能键:
//退出系统
}
}
17.4实现命令队列
我们可以建一个命令队列,里面写入命令的添加和移除的方法,以及命令队列的便利执行方法
public class CommandQueue {
private ArrayList<Command> list = new ArrayList<>();
public void addCommand(Command command){
list.add(command);
}
public void removeCommand(Command command){
list.remove(command);
}
public void execute(){
for(Command command: list){
command.execute();
}
}
}
我们在写一个执行者专门执行命令队列;
public class Invoker {
private CommandQueue queue ;
public Invoker(CommandQueue queue) {
this.queue = queue;
}
public void call(){
queue.execute();
}
}
测试
Command help =(Command) XMLUtil.getBean(0);
Command display =(Command)XMLUtil.getBean(1);
CommandQueue queue =new CommandQueue();
queue.addCommand(help);
queue.addCommand(display);
Invoker invoker =new Invoker(queue);
invoker.call();
//显示帮助文档
//退出系统
17.5宏命令
宏命令又称为组合命令,它是组合模式和命令模式联合的产物,宏命令是一个具体命令类,它拥有一个集合,包含了对其他命令对象的引用。
通常宏命令不直接与请求接收者交互,而是通过他的成员来调用接收者的方法。
17.6命令模式的优缺点
- 优点:降低系统的耦合度,增加新的命令很容易,比较容易的设计了一个命令队列和宏命令
- 缺点:使用命令模式可能会导致系统产出过多的具体命令类
17.7命令模式的适用环境
系统需要将调用者和接收者解耦,使得调用者和接收者不直接交互。
18解释器模式
18.1解释器的定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这解释器适用改表示来解释语言中的句子
18.2解释器模式的结构
- AbstractExpression(抽象表达式):在抽象表达式中声明抽象了抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类
- TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,实现了与文法中终结相关联的解释操作,在句中每一个终结符都是该类的一个实例。
- NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,实现了文法中非终结表达式的解释操作。
- Context(环境类):又称上下文,用于存储解释器之外的一些全局信息,通常他存储了需要解释的语句。
18.3解释器模式的实现
我们写一个与语言解释器,完成一些简单的移动动作
我们先需要一个抽象的节点类
public abstract class AbstractNode {
public abstract String interpret();
}
再写一个非终结节点的解释角色,and解释
public class AndNode extends AbstractNode {
private AbstractNode left;
private AbstractNode right;
public AndNode(AbstractNode left,AbstractNode right){
this.left= left;
this.right =right;
}
@Override
public String interpret() {
return left.interpret()+"再"+right.interpret();
}
}
再写一个语句句柄,将三个特定指令进行解释
public class Sentence extends AbstractNode {
private AbstractNode direction;
private AbstractNode action;
private AbstractNode distance;
public Sentence(AbstractNode direction,AbstractNode action, AbstractNode distance){
this.direction =direction;
this.action = action;
this.distance = distance;
}
@Override
public String interpret() {
return direction.interpret()+action.interpret()+distance.interpret();
}
}
再写一些基本的终结符表达式
public class DirectionNode extends AbstractNode {
private String name;
public DirectionNode(String name){
this.name = name;
}
@Override
public String interpret() {
if(name.equalsIgnoreCase("up")){
return "向上";
}else if(name.equalsIgnoreCase("down")){
return "向下";
}else if(name.equalsIgnoreCase("left")){
return "向左";
}else if(name.equalsIgnoreCase("right")){
return "向右";
}else {
return "无效命令";
}
}
}
public class ActionNode extends AbstractNode {
private String action;
public ActionNode (String action){
this.action =action;
}
@Override
public String interpret() {
if(action.equalsIgnoreCase("move")){
return "移动";
}else if(action.equalsIgnoreCase("run")){
return "快速移动";
}else {
return "无效命令";
}
}
}
public class DistanceNode extends AbstractNode {
private String distance;
public DistanceNode(String distance){
this.distance =distance;
}
@Override
public String interpret() {
return this.distance;
}
}
中心处理分配指令,最终解释权
public class InstructionHandler {
private AbstractNode node;
public void handle(String instruction){
AbstractNode left =null, right =null ;
AbstractNode direction =null,action =null,distance =null;
Stack<AbstractNode> stack = new Stack<>();
String[] words = instruction.split(" ");
for(int i=0;i<words.length;i++){
if(words[i].equalsIgnoreCase("and")){
left =stack.pop();
String word1 = words[++i];
direction =new DirectionNode(word1);
String word2 =words[++i];
action = new ActionNode(word2);
String word3 =words[++i];
distance = new DistanceNode(word3);
right = new Sentence(direction,action,distance);
stack.push(new AndNode(left,right));
}else {
String word1 = words[i];
direction =new DirectionNode(word1);
String word2 =words[++i];
action = new ActionNode(word2);
String word3 =words[++i];
distance = new DistanceNode(word3);
left = new Sentence(direction,action,distance);
stack.push(left);
}
}
this.node = stack.pop();
}
public String output(){
return node.interpret();
}
}
测试
public class Client {
public static void main(String[] args) {
String instruction ="down run 10 and left move 20";
InstructionHandler handler = new InstructionHandler();
handler.handle(instruction);
System.out.println(handler.output());
}
}
18.4解释器模式的优缺点
- 优点:易于改变和扩展文法,可以方便的实现一个简单的语言,实现文法较为容易,增加解释表达式较为方便
- 缺点:难以维护执行效率低
18.5适用环境
可以将一个需要解释执行的语言中的句子表示为一个抽象的语法树,对执行效率要求不高
19迭代器模式
19.1迭代器模式的定义
提供一种方法顺序访问一个聚合对象中各个元素,而又不用暴露该对象的内部表示
19.2迭代器模式的结构
- Itertaor(抽象迭代器):定义访问遍历元素的接口,声明访问元素的方法
- ConcreteIterator(具体迭代类):实现接口和方法
- Aggregate(抽象聚合类):用于储存和管理对象
- ConcreteAggregate(具体聚合类):抽象聚合类的子类
19.3迭代器模式的实现
先写具体的对象类的抽象类,里面引入抽象方法创建迭代器
public abstract class AbstractObjectList {
protected List<Object> objects = new ArrayList<>();
public AbstractObjectList (List<Object> objects){
this.objects=objects;
}
public void addObject(Object object){
this.objects.add(object);
}
public void removeObject(Object object){
this.objects.remove(object);
}
public List<Object> getObjects(){
return this.objects;
}
public abstract AbstractIterator createIterator();
}
在写具体抽象类,构造器还是父类的要传入一个对象的集合,重写创建迭代器方法
public class ProductList extends AbstractObjectList {
public ProductList(List<Object> objects) {
super(objects);
}
@Override
public AbstractIterator createIterator() {
return new ProductIterator(this);
}
}
在写迭代器的接口,其中提供迭代的方法
public interface AbstractIterator {
public void next();
public boolean isLast();
public void previous();
public boolean isFirst();
public Object getNextItem();
public Object getPreviousItem();
}
写迭代器实现这个方法
public class ProductIterator implements AbstractIterator {
private List<Object> products;
private int cursor1;
private int cursor2;
public ProductIterator(AbstractObjectList list){
this.products = list.getObjects();
cursor1 =0 ;
cursor2 =products.size()-1;
}
@Override
public void next() {
if(cursor1<products.size()){
cursor1++;
}
}
@Override
public boolean isLast() {
return cursor1==products.size();
}
@Override
public void previous() {
if(cursor2>-1){
cursor2--;
}
}
@Override
public boolean isFirst() {
return cursor2==-1;
}
@Override
public Object getNextItem() {
return products.get(cursor1);
}
@Override
public Object getPreviousItem() {
return products.get(cursor2);
}
}
测试
public class Client {
public static void main(String[] args) {
List<Object> products =new ArrayList<>();
products.add("李连杰");
products.add("成龙");
products.add("洪金宝");
products.add("吴京");
products.add("张晋");
AbstractObjectList list =new ProductList(products);
AbstractIterator iterator =new ProductIterator(list);
System.out.println("正向遍历");
while(!iterator.isLast()){
System.out.print(iterator.getNextItem()+" ");
iterator.next();
}
System.out.println();
System.out.println("反向遍历");
while (!iterator.isFirst()){
System.out.print(iterator.getPreviousItem()+" ");
iterator.previous();
}
//正向遍历
//李连杰 成龙 洪金宝 吴京 张晋
//反向遍历
//张晋 吴京 洪金宝 成龙 李连杰
}
}
19.4迭代器的优化
也可以将迭代器的重写在商品实例类中用内部类的形式代替。
....
public AbstractIterator createIterator() {
return new ProductIterator(this);
}
public class ProductIterator implements AbstractIterator {
private List<Object> products;
private int cursor1;
private int cursor2;
public ProductIterator(AbstractObjectList list){
this.products = list.getObjects();
cursor1 =0 ;
cursor2 =products.size()-1;
}
@Override
public void next() {
if(cursor1<products.size()){
cursor1++;
}
}
....
19.5JDK中的迭代
在Jdk中 的集合类对迭代器进行了接口的实现,会增加一个创建迭代器类的方法,子类的集合需要实现这个方法并创建迭代器,所以集合可以自己遍历,但是在list集合中他不仅实现了父类的迭代器,自己也创建了自己的迭代器,有了自己的迭代法
19.6迭代器的优缺点
- 优点:支持不同方式的遍历一个聚合对象,在同一个对象上,定义不同的遍历方式,新增很方便
- 缺点:增加新的聚合类需要新的迭代器,类的个数增加
19.7迭代器模式的适用环境
访问一个聚合对象的内容无须暴露他的内部,需要为一个聚合对象提供多种遍历方式
20中介者模式
20.1中介者模式的定义
定义一个对象来封装一系列对象的交互。中介者模式使各对象之间不需要显式的地相互引用,从而使其耦合松散,而且用户可以独立地改变它们之间的交互。
中介者模式又称为调停者模式,它是一种对象行为型模式。在中介者模式中,通过引入中介者来简化对象之间复杂的交互。
类似于需要qq通知每个人,现在建个群,@所有人就可以了
20.2中介者模式的结构
- Mediator(抽象中介者):定义一个借口,用于各同事之间的通信
- ConcreteMediator(具体中介者):它是抽象中介者的子类
- Colleague(抽象同事类):定义了同事类的所有公共的方法
- ConcreteColleague(具体同事类):实现抽象同事类中的方法
20.3中介者模式的实现
我们在编写客户端的用户信息时,如果删除用户或者增加用户所有的组合框的单选框都会增加一项或者减少一项。
所以我们用一个中介者模式来实现这个需求
首先写一个中介者的抽象类
public abstract class Mediator {
public abstract void componentChanged(Component c);
}
在写他的实体类继承抽象,并引入每个同事类,方便后续操作
public class ConcreteMediator extends Mediator {
public Button button;
public List list;
public ComboBox comboBox;
public TextBox textBox;
@Override
public void componentChanged(Component c) {
if(c==button){
System.out.println("单机增加按钮");
list.update();
comboBox.update();
textBox.update();
}else if(c==list){
System.out.println("从列表中选中用户");
list.select();
textBox.setText();
}else if(c==comboBox){
System.out.println("从组合框中选中客户");
comboBox.select();
textBox.setText();
}
}
}
再写同事类的抽象类和它的子类
public abstract class Component {
protected Mediator mediator;
public void setMediator(Mediator mediator){
this.mediator = mediator;
}
public void changed(){
mediator.componentChanged(this);
}
public abstract void update();
}
public class Button extends Component {
@Override
public void update() {
}
}
public class List extends Component {
@Override
public void update() {
System.out.println("列表框增加一项:张无忌");
}
public void select(){
System.out.println("列表框中选中项:小龙女");
}
}
public class ComboBox extends Component {
@Override
public void update() {
System.out.println("组合框增加一项:张无忌");
}
public void select(){
System.out.println("组合框选中项:小龙女");
}
}
public class TextBox extends Component {
@Override
public void update() {
System.out.println("客户端信息增加成功后文本框清空");
}
public void setText(){
System.out.println("文本框提示:小龙女");
}
}
最后做测试
public class Client {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
Button button =new Button();
List list =new List();
ComboBox comboBox =new ComboBox();
TextBox textBox = new TextBox();
button.setMediator(mediator);
list.setMediator(mediator);
textBox.setMediator(mediator);
comboBox.setMediator(mediator);
mediator.button=button;
mediator.list=list;
mediator.comboBox=comboBox;
mediator.textBox=textBox;
button.changed();
System.out.println("==================");
list.changed();
}
}
//单机增加按钮
//列表框增加一项:张无忌
//组合框增加一项:张无忌
//客户端信息增加成功后文本框清空
//==================
//从列表中选中用户
//列表框中选中项:小龙女
//文本框提示:小龙女
20.4中介者模式的优缺点
- 优点:简化了对象之间的交互,可将各同事对象解耦,还可以减少子类的生成。
- 缺点:具体中介类中包含了大量的同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。
20.5中介者模式的使用环境
系统中对象之间有着复杂的引用关系,系统结构混乱难以理解,通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。