好像每天一种模式节奏有点太慢,内容也有点少,所以还是试试每篇3 - 4种模式,然后2天一更叭。
首先,常用的设计模式分为3大类,创建型模式、行为型模式和结构型模式。前两篇所写的工厂模式和建造者模式就是属于创建型模式的内容。
责任链模式
责任链模式可以拆分为责任和链,责任是指有责任去干嘛,链可以参考链表嘛,有下一级。
「场景」:现在你是某公司的员工,拿到了一个比较紧急的文件(文件的紧急性肯定不一样嘛),需要更高一层的领导来处理下文件。
普通模式
「文件类:」
代码语言:javascript复制public class File {
private FileClass fileClass; //文件的重要等级
private String content; //文件的内容
//省略
}
//枚举类表示文件的重要等级
enum FileClass {
NORMAL,
IMPORTANT,
EMERGENCY
}
「员工接口:」
代码语言:javascript复制public interface IStaff {
//获取员工的名字
public String getName();
//获取要处理文件的等级
public FileClass getFileClass();
//获取员工的需求
public String getRequest();
}
「员工:」
代码语言:javascript复制public class Staff implements IStaff {
private File file;
private String name;
//省略构造函数
@Override
public String getName() {
return name;
}
@Override
public FileClass getFileClass() {
return file.getFileClass();
}
@Override
public String getRequest() {
return "这份文件【" file.getContent() "】需要处理下";
}
}
「领导接口:」
代码语言:javascript复制public interface IHandler {
//处理文件
public void handle(IStaff staff);
}
「组长:」
代码语言:javascript复制public class Leader implements IHandler {
@Override
public void handle(IStaff staff) {
System.out.println(staff.getName() ": " staff.getRequest());
System.out.println("组长:现在处理");
}
}
「总监:」
代码语言:javascript复制public class Director implements IHandler{
@Override
public void handle(IStaff staff) {
System.out.println(staff.getName() ": " staff.getRequest());
System.out.println("总监:现在处理");
}
}
「主管:」
代码语言:javascript复制public class Supervisor implements IHandler {
@Override
public void handle(IStaff staff) {
System.out.println(staff.getName() ": " staff.getRequest());
System.out.println("主管:现在处理");
}
}
「Client:」
代码语言:javascript复制public class Client {
public static void main(String[] args) {
File file = new File(FileClass.IMPORTANT, "策划方案");
IStaff staff = new Staff(file, "imperfect");
if(file.getFileClass().equals(FileClass.NORMAL)) {
new Leader().handle(staff);
} else if(file.getFileClass().equals(FileClass.IMPORTANT)) {
new Director().handle(staff);
} else if(file.getFileClass().equals(FileClass.EMERGENCY)) {
new Supervisor().handle(staff);
} else {
System.out.println("权限不够");
}
}
}
你瞅瞅,仔细品,这一堆if else直接在Client类给暴露出来了,而且是在Client中才进行判断不同等级给不同的领导处理。
通俗地比喻呢,就是员工拿到了文件后,把自己的组长,总监,主管全都叫到自己跟前,然后说,这份文件是比较重要的,给你们谁处理才有权限。确实是可以完成任务,但是这样的方式现实吗?
看着你
接近现实的一种方式是怎么样的呢,员工拿到文件后,首先给自己的直接领导组长,在领导层(「handler」)中的最低的一层(「链头」)。接着组长再看自己有没有「责任」去处理文件,没有的话再给下一层来处理,这就是「责任链模式」。
责任链模式
文件类和员工类不变,主要是领导层(handler)的变化。
「抽象领导类:」
代码语言:javascript复制public abstract class AbstractHandler {
private FileClass fileClass;
private AbstractHandler nextHandler;
private String name;
//在类构造的时候就明确了职责
//就像你入职就知道自己的责任是处理什么文件
public AbstractHandler(FileClass fileClass, String name) {
this.fileClass = fileClass;
this.name = name;
}
//取得领导的名字
public String getName() {
return name;
}
//没有责任,交给下一级
public void setNextHandler(AbstractHandler nextHandler) {
this.nextHandler = nextHandler;
}
//处理回应,每个人的回应方式不一样,所以抽象出来
public abstract void respond(IStaff staff);
//处理信息
public void handle(IStaff staff) {
if(fileClass.equals(staff.getFileClass())) {
respond(staff);
} else {
if(nextHandler != null) {
nextHandler.respond(staff);
} else {
System.out.println("已经到最高权限!!!");
}
}
}
}
「组长:」
代码语言:javascript复制public class Leader extends AbstractHandler {
public Leader(String name) {
super(FileClass.NORMAL, name);
}
@Override
public void respond(IStaff staff) {
System.out.println(staff.getName() ": " staff.getRequest());
System.out.println(getName() "组长:做出了回应");
}
}
「总监:」
代码语言:javascript复制public class Director extends AbstractHandler{
public Director(String name) {
super(FileClass.IMPORTANT, name);
}
@Override
public void respond(IStaff staff) {
System.out.println(staff.getName() ": " staff.getRequest());
System.out.println(getName() "总监:做出了回应");
}
}
「主管:」
代码语言:javascript复制public class Supervisor extends AbstractHandler {
public Supervisor(String name) {
super(FileClass.EMERGENCY, name);
}
@Override
public void respond(IStaff staff) {
System.out.println(staff.getName() ": " staff.getRequest());
System.out.println(getName() "主管:做出了回应");
}
}
「Client:」
代码语言:javascript复制public class Client {
public static void main(String[] args) {
File file = new File(FileClass.IMPORTANT, "营销方案");
IStaff staff = new Staff(file, "imperfect");
//创建领导层
AbstractHandler leader = new Leader("leaderWu");
AbstractHandler director = new Director("directorWu");
AbstractHandler supervisor = new Supervisor("supervisorWu");
//设置层级关系,跟链表类似
leader.setNextHandler(director);
director.setNextHandler(supervisor);
//首先交给直接领导处理
leader.handle(staff);
}
}
优缺点
- 优点:处理和请求分开,员工不知道最终文件是谁处理的
- 缺点:缺点也十分明显,如果责任链很长,而处理者刚好在最后,是不是要遍历完责任链,这样性能就比较低。
UML类图
责任链UML
命令模式
命令模式,一句话就是给你一个命令,必须要遵守并且执行,有点像是军队里面“服从命令是军人的天职”。
不知道大学有没有参加过数学建模,反正我是没有参加过,但是有了解过一般构成,一个小队里面一般有主要负责「搜索的同学,写代码的同学,写论文的同学和指导老师」
普通模式
「抽象成员类(receiver):」
代码语言:javascript复制public abstract class NTeammate {
public abstract void changeRequire();
public abstract void modify();
public abstract void work();
}
「Searcher:」
代码语言:javascript复制public class NSearcher extends NTeammate {
@Override
public void changeRequire() {
System.out.println("searcher 了解到需求改变");
}
@Override
public void modify() {
}
@Override
public void work() {
System.out.println("searcher 开始搜索相关信息");
}
}
「Writer:」
代码语言:javascript复制public class NWriter extends NTeammate {
@Override
public void changeRequire() {
System.out.println("writer 了解到需求改变");
}
@Override
public void modify() {
System.out.println("writer 修改论文");
}
@Override
public void work() {
System.out.println("writer 开始写论文");
}
}
「Coder:」
代码语言:javascript复制public class NCoder extends NTeammate {
@Override
public void changeRequire() {
System.out.println("coder 了解到需求改变");
}
@Override
public void modify() {
System.out.println("coder 修改代码");
}
@Override
public void work() {
System.out.println("coder 开始码代码");
}
}
「Teacher:」
代码语言:javascript复制public class NTeacher {
public static void main(String[] args) {
NTeammate writer = new NWriter();
//需要改文章了
writer.modify();
writer.work();
}
}
一开始,老师看到写的文章不够简洁,所以就打电话给writer,让他修改,所以就有了上面的Teacher类。这样其实还好,因为文章嘛,修改润色就好了。
过了一天,老师仔细看了下后,发现代码的算法有bug,这个漏洞导致了不仅coder要修改代码,writer也要修改相应地方的文章。
脑阔疼
老师这下不仅要联系writer,也得联系coder,那么Teacher类应该怎么修改呢?
代码语言:javascript复制public class NTeacher {
public static void main(String[] args) {
NTeammate writer = new NWriter();
NTeammate coder = new NCoder();
//需要改bug和文章了
writer.modify();
writer.work();
coder.modify();
coder.work();
}
}
可以发现,就多了一个需求,代码较之前已经有很大的改动了,这是我们不希望看到的。可能有的小伙伴会想到利用中介者模式,不过中介者模式是为了减小类和类之间的耦合,这个例子中的searcher,writer,coder并没有耦合,都在各司其职。
命令模式
如果小队里面有个队长(「Invoker」)就好了,可以和老师(「client」)沟通,不止这样,老师的指令实现起来肯定是String类型,我们可以把指令封装成一个类(「command」),队长只需要发布命令,命令指示队员(「receiver」)来做什么。这就是命令模式,队员必须执行命令要求做的。
抽象队员以及具体队员还是和上面一样,这里就不再赘述。
「抽象命令类:」
代码语言:javascript复制public abstract class AbstractCommand {
protected Coder coder = new Coder();
protected Searcher searcher = new Searcher();
protected Writer writer = new Writer();
//一定要有个执行的方法,下达一个命令
public abstract void execute();
}
「具体命令类(Command):」
有哪些命令,都可以封装起来
「改变需求:」
代码语言:javascript复制public class ChangeInfoCommand extends AbstractCommand {
@Override
public void execute() {
searcher.changeRequire();
writer.changeRequire();
coder.changeRequire();
}
}
「修改文章:」
代码语言:javascript复制public class ModifyArticleCommand extends AbstractCommand {
@Override
public void execute() {
writer.modify();
writer.work();
}
}
「修改代码:」
代码语言:javascript复制public class ModifyCodeCommand extends AbstractCommand {
@Override
public void execute() {
coder.modify();
coder.work();
writer.modify();
writer.work();
}
}
「队长类(Invoke):」
代码语言:javascript复制public class Captain {
//和命令产生联系
AbstractCommand abstractCommand;
public Captain(AbstractCommand abstractCommand) {
this.abstractCommand = abstractCommand;
}
public void invoke() {
//发布命令要求队员进行相应的动作
abstractCommand.execute();
}
}
「老师类(Client):」
代码语言:javascript复制public class Teacher {
public static void main(String[] args) {
AbstractCommand command = new ModifyCodeCommand();
Captain captain = new Captain(command);
captain.invoke();
}
}
如果老师觉得又不好了,这些怎么办呢,没有必要和成员练习,只需要提出另外一个建议,队长也不要跟队员练习,只需要发布命令,由命令指示队员去做。修改就是这么简单,一行代码的事情。
代码语言:javascript复制public class Teacher {
public static void main(String[] args) {
//AbstractCommand command = new ModifyCodeCommand();
AbstractCommand command = new ModifyArticleCommand();
Captain captain = new Captain(command);
captain.invoke();
}
}
扩展
如果说,诶,改代码的时候不仅需要修改bug和修正文章,也需要searcher来搜集信息,怎么办呢?
代码语言:javascript复制public class ModifyCodeCommand extends AbstractCommand {
@Override
public void execute() {
searcher.work(); //只需要在具体的命令里面添加即可,客户端是完全不知道的
coder.modify();
coder.work();
writer.modify();
writer.work();
}
}
还有一种情况就是,某些修改之后,老师发现还是之前的版本比较好,这就要求每个队员都有一个回调函数来撤销动作,返回到上一个状态,就是找到保存的之前版本的文件。「只需要在抽象receiver类加一个回调函数即可」:
代码语言:javascript复制public abstract class NTeammate {
public abstract void changeRequire();
public abstract void modify();
public abstract void work();
//具体队友在根据自己的方式实现回调方法
public abstract void rollback();
}
接着就是添加一个「撤回命令」
代码语言:javascript复制public class callBackCommand extends AbstractCommand {
@Override
public void execute() {
//当然,需要谁撤回是可以改变的
searcher.rollback();
writer.rollback();
coder.rollback();
}
}
UML类图
命令UML
解释器模式
这个无论是工作上还是学习中都是比较冷门的设计模式。解释器模式由以下类组成
❝
- 「Context:」 Context用于封装解释器的全局信息,所有具体的解释器均需要访问Context。
- 「AbstractExpression:」 一个抽象类或接口,声明执行的解释方法,由所有具体的解释器实现
- 「TerminalExpression:」 一种解释器类,实现与语法的终结符相关的操作。终结符表达式必须终结被实现和实例化,因为它表示表达式的结尾。
- 「NonTerminalExpreesion:」 这是实现语法的不同规则或符号的类。对于每一种语法都应该创建一个类。
❞
这个东西解释起来比较拗口,一时也没有很好的通俗的解释,那就直接看例子叭。
AbstractExpression
代码语言:javascript复制public interface Expression {
public float interpret();
}
TerminalExpression
代码语言:javascript复制public class Number implements Expression{
private final float number;
public Number(float number) {
this.number = number;
}
@Override
public float interpret() {
return number;
}
}
还记得之前说过的流吗,TerminalExpression
就类似终结操作,而NonTerminalExpression
就是类似中间操作
NonTerminalExpression
代码语言:javascript复制public class Plus implements Expression{
Expression left;
Expression right;
public Plus(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public float interpret() {
return left.interpret() right.interpret();
}
}
注意对于每一个语法都要有一个独立的类
代码语言:javascript复制public class Minus implements Expression {
Expression left;
Expression right;
public Minus(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public float interpret() {
return left.interpret() - right.interpret();
}
}
Context
代码语言:javascript复制public class Evaluator {
public static void main(String[] args) {
Evaluator evaluator = new Evaluator();
System.out.println(evaluator.evaluate("3 4 "));
System.out.println(evaluator.evaluate("4 3 -"));
System.out.println(evaluator.evaluate("4 3 - 2 "));
}
public float evaluate(String expression) {
Stack<Expression> stack = new Stack<>();
float result = 0;
for (String token : expression.split(" ")) {
Expression exp = null;
if (isOperator(token)) {
if (token.equals(" ")) {
exp = stack.push(new Plus(stack.pop(), stack.pop()));
} else if (token.equals("-")) {
exp = stack.push(new Minus(stack.pop(), stack.pop()));
}
if (null != exp) {
result = exp.interpret();
stack.push(new Number(result));
}
}
if (isNumber(token)) {
stack.push(new Number(Float.parseFloat(token)));
}
}
return result;
}
private boolean isNumber(String token) {
try {
Float.parseFloat(token);
return true;
} catch (NumberFormatException e) {
return false;
}
}
private boolean isOperator(String token) {
return token.equals(" ") || token.equals("-");
}
}
UML类图
解释器UML