前言
【开发】:老大,你教了我挺多设计模式的,已经全部教完了吗?
【BOSS】:没呢,还有好几个设计模式没说过呢,今天再传授你三个吧,分别是建造者模式,责任链模式,备忘录模式,如何?
【开发】:好啊,我最喜欢学习了!
建造者模式
意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
核心代码
定义建造接口
public interface Builder {
void buildPartOne();
void buildPartTwo();
void buildPartThr();
/***
* 一般情况肯定是一个复杂的对象
*/
String getResult();
}
定义实际建造工人
public class ConcreteBuilder implements Builder {
private StringBuffer buffer = new StringBuffer();
@Override
public void buildPartOne() {
buffer.append("i am part one
");
}
@Override
public void buildPartTwo() {
buffer.append("i am part two
");
}
@Override
public void buildPartThr() {
buffer.append("i am part Thr
");
}
@Override
public String getResult() {
return buffer.toString();
}
}
如何创建不同的表示?
定义督公
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void setBuilder(Builder builder) {
this.builder = builder;
}
public void construct() {
builder.buildPartOne();
builder.buildPartTwo();
builder.buildPartThr();
}
}
模拟调用
public class App {
/***
* 建造者模式
* 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
*
* 主要解决
* 主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定
*
* 何时使用
* 一些基本部件不会变,而其组合经常变化的时候。
*
* 如何解决
* 将变与不变分离开。
*
* 关键代码
* 建造者:创建和提供实例
* 建造者接口:依赖接口编程
* 指导者:管理建造出来的实例的依赖关系
* 产品:建造者所生产的产品
* 建造者作为参数进入指导者构造方法,通过特定普遍的构造顺序或算法执行,得到产品
*
* 应用实例:
* 1.去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"
* 2.StringBuilder
*/
public static void main(String[] args){
// 创建建造者接口并指向具体建造者 - 包含最终产品
Builder concreteBuilder = new ConcreteBuilder();
// 创建指导者, 把具体建造者即工人作为参数传入, 通过统一方法执行相应的构建命令
Director director = new Director(concreteBuilder);
director.construct();
// 从工人即具体建造者获取产品
String result = concreteBuilder.getResult();
System.out.println(result);
}
}
建造者的延展思考:链式调用
链式调用让代码更优雅~
public class MyBuilder {
// 省略不必要的代码
MyBuilder withName(String name) {
this.setName(name);
return this;
}
MyBuilder withYear(String year) {
this.setYear(year);
return this;
}
MyBuilder withSex(String sex) {
this.setSex(sex);
return this;
}
}
UML图
代码见下方~
责任链模式
意图
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连城一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
典型应用
Log4J 日志系统即是使用了责任链的思想,通过不同日志级别的传递,按级别处理日志
简单实现一个Log等级系统
抽象类
定义日志等级,设置下一个处理器,抽象出写入方法
public abstract class AbstractLogger {
// 责任级别
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
// 当前级别
int level;
//责任链中的下一个元素
AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger){
this.nextLogger = nextLogger;
}
// 记录日志
public void logMessage(int level, String message){
if(this.level <= level){
write(message);
}
if(nextLogger != null){
nextLogger.logMessage(level, message);
}
}
// 抽象方法 -> 重写具体日志输出类型
abstract protected void write(String message);
}
具体日志类
public class InfoLoger extends AbstractLogger {
public InfoLoger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("InfoLoger Console::Logger: " + message);
}
}
为了避免重复,只展示一个类
实际调用
public class App {
public static void main(String[] args){
AbstractLogger log = getChainOfLoggers();
log.logMessage(AbstractLogger.INFO, "i am info");
log.logMessage(AbstractLogger.DEBUG, "i am debug");
log.logMessage(AbstractLogger.ERROR, "i am error");
}
private static AbstractLogger getChainOfLoggers(){
AbstractLogger error = new ErrorLoger(AbstractLogger.ERROR);
AbstractLogger debug = new DebugLoger(AbstractLogger.DEBUG);
AbstractLogger info = new InfoLoger(AbstractLogger.INFO);
error.setNextLogger(debug);
debug.setNextLogger(info);
return error;
}
}
// 输出结果:
// InfoLoger Console::Logger: i am info
//
// ------------------------
//
// DebugLoger Console::Logger: i am debug
// InfoLoger Console::Logger: i am debug
//
// ------------------------
//
// ErrorLoger Console::Logger: i am error
// DebugLoger Console::Logger: i am error
// InfoLoger Console::Logger: i am error
总结
多种形式
- 当前pattern下类似日志级别形式, 只要等级比A大,那B,C都会处理
- 如A->B->C 由低到高级别执行,只要执行就返回等
- 最高级形式: 低级发起请求后, 高级任一处理后,请求反馈即可(涉及到异步相关,线程通信)
优点
- 降低耦合度。它将请求的发送者和接收者解耦
- 简化了对象。使得对象不需要知道链的结构
- 增强给对象指派职责的灵活性,通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任
- 增加新的请求处理类很方便
缺点
- 不能保证请求一定被接收
- 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用
- 可能不容易观察运行时的特征,有碍于除错
使用场景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
- 可动态指定一组对象处理请求
UML图
代码见下方~
备忘录模式
意图
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后可将对象恢复到原先保存的状态
核心代码
备忘录
/**
* ******************************
* description: 备忘录,确定数据结构即可
* ******************************
*/
public class Memento {
Map<String, String> data;
}
模拟短信场景
/**
* ******************************
* description: 模拟短信场景
* ******************************
*/
public class MessageData {
private String time;
private String message;
/**
* 存储数据
*/
public Memento saveMemento () {
Map<String, String> map = Maps.newHashMap();
map.put("TIME", time);
map.put("MESSAGE", message);
return new Memento(map);
}
/**
* 取出数据
*/
public void getFromMemento(Memento memento){
time = memento.getData().get("TIME");
message = memento.getData().get("MESSAGE");
}
// 省略部分代码
}
备忘录存储容器
public class MementoTaker {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento state){
mementoList.add(state);
}
public Memento get(int index){
return mementoList.get(index);
}
}
核心调用代码
public class App {
public static void main(String[] args) throws InterruptedException {
// 创建备忘录管理者
MementoTaker mementoTaker = new MementoTaker();
MessageData messageData = new MessageData();
messageData.setTime(System.currentTimeMillis() + "");
messageData.setMessage("This is messgae first.");
mementoTaker.add(messageData.saveMemento());
System.out.println("First: -> " + messageData);
Thread.sleep(2000);
messageData.setTime(System.currentTimeMillis() + "");
messageData.setMessage("This is messgae second.");
mementoTaker.add(messageData.saveMemento());
System.out.println("Second: -> " + messageData);
Thread.sleep(2000);
// 回复初次状态
messageData.getFromMemento(mementoTaker.get(0));
System.out.println("********************检测数据是否回到初始状态******************");
System.out.println(messageData);
}
}
模式总结:其实该模式非常简单,即确定好数据结构在容器中存储一份,以便后续恢复,或者重新使用等等
总结
主要解决
所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态
备忘录思想的实践
- 打游戏时的存档
- Windows 里的 ctri + z
- 数据库的事务管理
UML图
相关代码链接
- 兼顾了《HeadFirst》以及《GOF》两本经典书籍中的案例
- 提供了友好的阅读指导