访问者模式

允许一个或者多个操作应用到一组对象上,解耦操作和对象本身,保持类职责单一、满足开闭原则以及应对代码的复杂性。

多态是一种动态绑定,可以在运行时获取对象的实际类型,来运行实际类型对应的方法。而函数重载是一种静态绑定,在编译时并不能获取对象的实际类型,而是根据声明类型执行声明类型对应的方法。

public abstract class ResourceFile {protected String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}abstract public void accept(Visitor vistor);
}public class PdfFile extends ResourceFile {public PdfFile(String filePath) {super(filePath);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
//...
}public interface Visitor {void visit(PdfFile pdfFile);void visit(PPTFile pdfFile);void visit(WordFile pdfFile);
}public class Extractor implements Visitor {@Overridepublic void visit(PPTFile pptFile) {//...System.out.println("Extract PPT.");}@Overridepublic void visit(PdfFile pdfFile) {//...System.out.println("Extract PDF.");}@Overridepublic void visit(WordFile wordFile) {//...System.out.println("Extract WORD.");}
}public class Compressor implements Visitor {@Overridepublic void visit(PPTFile pptFile) {//...System.out.println("Compress PPT.");}@Overridepublic void visit(PdfFile pdfFile) {//...System.out.println("Compress PDF.");}@Overridepublic void visit(WordFile wordFile) {//...System.out.println("Compress WORD.");}
}public class ToolApplication {public static void main(String[] args) {Extractor extractor = new Extractor();List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);for (ResourceFile resourceFile : resourceFiles) {resourceFile.accept(extractor);}Compressor compressor = new Compressor();for (ResourceFile resourceFile : resourceFiles) {resourceFile.accept(compressor);}}private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {List<ResourceFile> resourceFiles = new ArrayList<>();
//...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)resourceFiles.add(new PdfFile("a.pdf"));resourceFiles.add(new WordFile("b.word"));resourceFiles.add(new PPTFile("c.ppt"));return resourceFiles;}
}

public abstract class ResourceFile {protected String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}public abstract ResourceFileType getType();
}public class PdfFile extends ResourceFile {public PdfFile(String filePath) {super(filePath);}@Overridepublic ResourceFileType getType() {return ResourceFileType.PDF;}
//...
}public interface Extractor {void extract2txt(ResourceFile resourceFile);
}public class PdfExtractor implements Extractor {@Overridepublic void extract2txt(ResourceFile resourceFile) {//...}
}public class ExtractorFactory {private static final Map<ResourceFileType, Extractor> extractors = new HashMap();static {extractors.put(ResourceFileType.PDF, new PdfExtractor());extractors.put(ResourceFileType.PPT, new PPTExtractor());extractors.put(ResourceFileType.WORD, new WordExtractor());}public static Extractor getExtractor(ResourceFileType type) {return extractors.get(type);}
}public class ToolApplication {public static void main(String[] args) {List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);for (ResourceFile resourceFile : resourceFiles) {Extractor extractor = ExtractorFactory.getExtractor(resourceFile.getType);extractor.extract2txt(resourceFile);}}private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {List<ResourceFile> resourceFiles = new ArrayList<>();
//...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)resourceFiles.add(new PdfFile("a.pdf"));resourceFiles.add(new WordFile("b.word"));resourceFiles.add(new PPTFile("c.ppt"));return resourceFiles;}
}
  • 双分派
    指的是执行哪个对象的方法,根据对象的运行时类型来决定;执行对象的哪个方法,根据方法参数的运行时类型来决定。

    Java 支持多态特性,代码可以在运行时获得对象的实际类型,然后根据实际类型决定调用哪个方法。但 Java 设计的函数重载的语法规则是在编译时,根据传递进函数的参数的声明类型,来决定调用哪个重载函数。也就是说,具体执行哪个对象的哪个方法,只跟对象的运行时类型有关,跟参数的运行时类型无关。所以,Java 语言只支持 Single Dispatch。

备忘录模式

在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。这个模式的定义表达了两部分内容:一部分是,存储副本以便后期恢复;另一部分是,要在不违背封装原则的前提下,进行对象的备份和恢复。

eg: 用户输入文本时,程序将其追加存储在内存文本中;用户输入“:list”,程序在命令行中输出内存文本的内容;用户输入“:undo”,程序会撤销上一次输入的文本,也就是从内存文本中将上次输入的文本删除掉。

public class InputText {private StringBuilder text = new StringBuilder();public String getText() {return text.toString();}public void append(String input) {text.append(input);}public Snapshot createSnapshot() {return new Snapshot(text.toString());}public void restoreSnapshot(Snapshot snapshot) {this.text.replace(0, this.text.length(), snapshot.getText());}
}public class Snapshot {private String text;public Snapshot(String text) {this.text = text;}public String getText() {return this.text;}
}public class SnapshotHolder {private Stack<Snapshot> snapshots = new Stack<>();public Snapshot popSnapshot() {return snapshots.pop();}public void pushSnapshot(Snapshot snapshot) {snapshots.push(snapshot);}
}public class ApplicationMain {public static void main(String[] args) {InputText inputText = new InputText();SnapshotHolder snapshotsHolder = new SnapshotHolder();Scanner scanner = new Scanner(System.in);while (scanner.hasNext()) {String input = scanner.next();if (input.equals(":list")) {System.out.println(inputText.toString());} else if (input.equals(":undo")) {Snapshot snapshot = snapshotsHolder.popSnapshot();inputText.restoreSnapshot(snapshot);} else {snapshotsHolder.pushSnapshot(inputText.createSnapshot());inputText.append(input);}}}
}

命令模式

命令模式将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能。

命令模式用的最核心的实现手段,是将函数封装成对象。具体来说就是,设计一个包含这个函数的类,实例化一个对象传来传去,这样就可以实现把函数像对象一样使用。

命令模式的主要作用和应用场景,是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等等。

整个手游后端服务器轮询获取客户端发来的请求,获取到请求之后,借助命令模式,把请求包含的数据和处理逻辑封装为命令对象,并存储在内存队列中。然后,再从队列中取出一定数量的命令来执行。执行完成之后,再重新开始新的一轮轮询。具体的示例代码如下所示:

public interface Command {void execute();
}public class GotDiamondCommand implements Command {// 省略成员变量public GotDiamondCommand(/*数据*/) {//...}@Overridepublic void execute() {// 执行相应的逻辑}
}
//GotStartCommand/HitObstacleCommand/ArchiveCommand类省略public class GameApplication {private static final int MAX_HANDLED_REQ_COUNT_PER_LOOP = 100;private Queue<Command> queue = new LinkedList<>();public void mainloop() {while (true) {List<Request> requests = new ArrayList<>();
//省略从epoll或者select中获取数据,并封装成Request的逻辑,
//注意设置超时时间,如果很长时间没有接收到请求,就继续下面的逻辑处理。for (Request request : requests) {Event event = request.getEvent();Command command = null;if (event.equals(Event.GOT_DIAMOND)) {command = new GotDiamondCommand(/*数据*/);} else if (event.equals(Event.GOT_STAR)) {command = new GotStartCommand(/*数据*/);} else if (event.equals(Event.HIT_OBSTACLE)) {command = new HitObstacleCommand(/*数据*/);} else if (event.equals(Event.ARCHIVE)) {command = new ArchiveCommand(/*数据*/);} // ...一堆else if...queue.add(command);}int handledCount = 0;while (handledCount < MAX_HANDLED_REQ_COUNT_PER_LOOP) {if (queue.isEmpty()) {break;}Command command = queue.poll();command.execute();}}}
}

解释器模式

解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。

核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分成一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。

/**** “ 8 3 2 4 - + * ”这样一个表达式,解析成(8-3+2)*4*/
public interface Expression {long interpret();
}public class NumberExpression implements Expression {private long number;public NumberExpression(long number) {this.number = number;}public NumberExpression(String number) {this.number = Long.parseLong(number);}@Overridepublic long interpret() {return this.number;}
}public class AdditionExpression implements Expression {private Expression exp1;private Expression exp2;public AdditionExpression(Expression exp1, Expression exp2) {this.exp1 = exp1;this.exp2 = exp2;}@Overridepublic long interpret() {return exp1.interpret() + exp2.interpret();}
}public class ExpressionInterpreter {private Deque<Expression> numbers = new LinkedList<>();public long interpret(String expression) {String[] elements = expression.split(" ");int length = elements.length;for (int i = 0; i < (length + 1) / 2; ++i) {numbers.addLast(new NumberExpression(elements[i]));}for (int i = (length + 1) / 2; i < length; ++i) {String operator = elements[i];boolean isValid = "+".equals(operator) || "-".equals(operator)|| "*".equals(operator) || "/".equals(operator);if (!isValid) {throw new RuntimeException("Expression is invalid: " + expression);}Expression exp1 = numbers.pollFirst();Expression exp2 = numbers.pollFirst();Expression combinedExp = null;if (operator.equals("+")) {combinedExp = new AdditionExpression(exp1, exp2);} else if (operator.equals("-")) {combinedExp = new AdditionExpression(exp1, exp2);} else if (operator.equals("*")) {combinedExp = new AdditionExpression(exp1, exp2);} else if (operator.equals("/")) {combinedExp = new AdditionExpression(exp1, exp2);}long result = combinedExp.interpret();numbers.addFirst(new NumberExpression(result));}if (numbers.size() != 1) {throw new RuntimeException("Expression is invalid: " + expression);}return numbers.pop().interpret();}
}

中介模式

中介模式定义了一个单独的(中介)对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互。

原本业务逻辑会分散在各个控件中,现在都集中到了中介类中。好处是简化了控件之间的交互,坏处是中介类有可能会变成大而复杂的“上帝类”(God Class)。

只有当参与者之间的交互关系错综复杂,维护成本很高的时候,我们才考虑使用中介模式。

public interface Mediator {void handleEvent(Component component, String event);
}public class LandingPageDialog implements Mediator {private Button loginButton;private Button regButton;private Selection selection;private Input usernameInput;private Input passwordInput;private Input repeatedPswdInput;private Text hintText;@Overridepublic void handleEvent(Component component, String event) {if (component.equals(loginButton)) {String username = usernameInput.text();String password = passwordInput.text();
//校验数据...
//做业务处理...} else if (component.equals(regButton)) {//获取usernameInput、passwordInput、repeatedPswdInput数据...
//校验数据...
//做业务处理...} else if (component.equals(selection)) {String selectedItem = selection.select();if (selectedItem.equals("login")) {usernameInput.show();passwordInput.show();repeatedPswdInput.hide();hintText.hide();
//...省略其他代码} else if (selectedItem.equals("register")) {//....}}}
}public class UIControl {private static final String LOGIN_BTN_ID = "login_btn";private static final String REG_BTN_ID = "reg_btn";private static final String USERNAME_INPUT_ID = "username_input";private static final String PASSWORD_INPUT_ID = "pswd_input";private static final String REPEATED_PASSWORD_INPUT_ID = "repeated_pswd_input_id";private static final String HINT_TEXT_ID = "hint_text";private static final String SELECTION_ID = "selection";public static void main(String[] args) {Button loginButton = (Button) findViewById(LOGIN_BTN_ID);Button regButton = (Button) findViewById(REG_BTN_ID);Input usernameInput = (Input) findViewById(USERNAME_INPUT_ID);Input passwordInput = (Input) findViewById(PASSWORD_INPUT_ID);Input repeatedPswdInput = (Input) findViewById(REPEATED_PASSWORD_INPUT_ID);Text hintText = (Text) findViewById(HINT_TEXT_ID);Selection selection = (Selection) findViewById(SELECTION_ID);Mediator dialog = new LandingPageDialog();dialog.setLoginButton(loginButton);dialog.setRegButton(regButton);dialog.setUsernameInput(usernameInput);dialog.setPasswordInput(passwordInput);dialog.setRepeatedPswdInput(repeatedPswdInput);dialog.setHintText(hintText);dialog.setSelection(selection);loginButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {dialog.handleEvent(loginButton, "click");}});regButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {dialog.handleEvent(regButton, "click");}});//....}
}dialog.setSelection(selection);loginButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {dialog.handleEvent(loginButton, "click");}});regButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {dialog.handleEvent(regButton, "click");}});//....}
}

三、行为型模式【访问者模式、备忘录模式、命令模式、解释器模式、中介模式】相关推荐

  1. 设计模式——中介模式

    今天我们来学习最后一个行为型设计模式:中介模式.中介模式也属于不怎么常用的模式,应用场景比较特殊,所以,中介模式也不是学习的重点,稍微了解一下. 概述 中介模式:(Mediator Design Pa ...

  2. iOS设计模式四部曲(三):行为型模式 内附Demo

    本篇是四部曲的第三篇,第一篇请点这里iOS设计模式四部曲(一):创建型模式,第二篇请点击这里iOS设计模式四部曲(二):结构型模式.由于个人能力有限,文中难免有一些遗漏或者错误,请各位看官不吝赐教!谢 ...

  3. Java设计模式之行为型:访问者模式

    背景: 去医院看病时,医生会给你一个处方单要你去拿药,拿药我们可以分为两步走: (1)去柜台交钱,划价人员会根据处方单上的药进行划价,交钱. (2)去药房拿药,药房工作者同样根据处方单给你相对应的药. ...

  4. 设计模式之美——行为型3:迭代模式、访问者模式、命令模式、解释器模式、中介模式

    迭代器模式 迭代器模式的原理和实现 迭代器模式(Iterator Design Pattern),也叫作游标模式(Cursor Design Pattern). 迭代器模式.它用来遍历集合对象.这里说 ...

  5. Redis系列(三)-Redis哨兵模式(一篇文章让你全面的了解reids哨兵模式)

    哨兵模式概述 举一个通俗易懂的例子 有一个皇帝(master)他有2个儿子,大儿子(slave1)和小儿子(slave2).有一天皇帝离家出走了皇位空虚(master宕机),大儿子和小儿子为了争夺皇位 ...

  6. 【Java 行为型设计模式 IV】观察者模式、中介模式详解

    愿你如阳光,明媚不忧伤. 目録 8. 观察者模式 观察者模式优点和缺点 观察者模式的应用场景 观察者模式模式的结构 观察者模式的实现 观察者模式的应用实例 I 9. 中介模式 中介模式优点和缺点 中介 ...

  7. UVM设计模式 (七)命令模式、三种sequence启动方式、start_item/finish_item、中介模式、virtual sequence

    本篇介绍UVM中的sequence,这是UVM中最基础的部分.对于前面介绍的uvm_callback, uvm_visitor等,很少被使用到或者也只有搭建平台的人会使用.不能认为平台的搭建更富有&q ...

  8. 虚拟网络编辑器三种模式工作原理详细介绍(桥接-网络地址转换-主机模式)

     1.桥接模式(Bridged) 使用该模式的虚拟操作系统是局域网中的一个独立主机,具有独立访问网络中其他主机的功能.它的配置信息由VMnet0虚拟网络提供,不支持DHCP服务.用户需要按照一定的规则 ...

  9. 模板方法模式详解附有代码案例分析(包含模板方法模式重构JDBC操作业务代码示例)

    模板方法模式 一.模板方法模式的概念和角色 (一).模板方法模式的概念 (二).模板方法模式的角色 二.模板方法模式的应用场景 三. 模板方法模式的代码示例 四.模板方法模式重构JDBC操作业务 五. ...

最新文章

  1. AI公开课:19.03.07雷鸣教授《人工智能革命与趋势》课堂笔记以及个人感悟
  2. 学习:erlang的term反序列化,string转换为term
  3. sklearn使用日志(part1)--特征消除术Recursive feature elimination
  4. ON DUPLICATE KEY UPDATE
  5. springboot启动原理_SpringBoot启动原理及相关流程
  6. 维密天使糖糖传授自拍秘籍,最满意自拍来自OPPO R11s
  7. 第六章 输入输出系统-作业
  8. 【机器学习-斯坦福】学习笔记7 - 最优间隔分类器问题
  9. java 卡密_【java实现点卡生成】
  10. 2022年3月编程语言排行榜:Lua重新回到前20
  11. [11]ESP32+三轴磁罗盘HMC5883L移植与调试
  12. win7网络看不到win10计算机,网络共享中win7能找到win10,但win10找不到win7
  13. Task01:基于逻辑回归的分类预测
  14. 计步器 c语言,ADXL345单片机计步器程序
  15. SLG手游Java服务器的设计与开发——数据管理
  16. W3C 标准 较详细
  17. python 获取硬盘信息失败请谨慎操作_老毛桃pe装机工具出现获取硬盘信息失败,请谨慎操作...
  18. 傻瓜教程:手把手教你解决多个应用实例(附代码、手绘图)
  19. 【华人学者风采】李海洲 新加坡国立大学
  20. 冯诺曼伊体系 计算机五大逻辑,科学网—再谈冯·诺伊曼结构 - 姜咏江的博文

热门文章

  1. 微信小程序基础学习笔记Day02
  2. Android-通讯卫士
  3. 基于excel数据绘制饼图
  4. 苹果应用商店登陆服务器出现问题,苹果应用商店提示无法登录的问题
  5. 曰期计算器java,日期计算器
  6. php.script.shell.1,shell script到底是什么?怎么使用?
  7. 公关策划书的标准格式及撰写要素
  8. hdmi tv 信息 的edid_EDID解决方案—HDMI
  9. Alert The AC power adapter wattage and type cannot be determined. (戴尔电脑开机)
  10. 华夏名网十周年盛大感恩优惠活动