目录
行为型
模板方法模式
这是一个比较简单的设计模式,图中的AbstractClass就是模板类,客户端调用的是它提供的模板方法(templateMethod),模板方法中定义了每个抽象步骤(step)的执行顺序。而在具体的实现类(ConcreteClass)中,需要实现每一个步骤方法(step)。如果系统中,某些类似的功能执行的框架是一样的,只不过在具体的细节上有所出入,那么可以使用该设计模式进行实现。
step方法有两种类型:抽象类型(留给子类进行实现)、钩子方法(在模板类中进行空实现,子类可以根据自己的实际需要进行重写)
Note:模板类中的模板方法最好定义成final类型,防止被子类重写。
代码如下
//模板类
public abstract class FileExport {
/***
* 模板方法:文件导出
* 模板方法中定义了具体的步骤方法调用顺序
* 定义成fianl防止子类重写
*/
public final void export(){
//设置数据源
setDataSource();
//创建文件
createFile();
//将数据注入文件中
injectataD();
//备份
save();
}
protected abstract void setDataSource();
protected abstract void createFile();
protected abstract void injectataD();
protected void save(){
//钩子方法,这里做一个空实现。子类可以选择性重写,不重写就不执行任何操作。
}
}
//模板实现类1:实现抽象步骤方法
public class CSVFileExport extends FileExport {
@Override
protected void setDataSource() {
System.out.println("使用csv相关的数据源");
}
@Override
protected void createFile() {
System.out.println("创建csv文件");
}
@Override
protected void injectataD() {
System.out.println("将csv数据注入csv文件中");
}
//Note:这里不重写save()方法,没这个需求
}
//模板实现类2
public class ImportFileExport extends FileExport {
@Override
protected void setDataSource() {
System.out.println("设置重要文件的数据源");
}
@Override
protected void createFile() {
System.out.println("创建不可被修改的文件类型");
}
@Override
protected void injectataD() {
System.out.println("注入重要数据到文件对象中");
}
@Override
protected void save() {
//重要文件,导出后需要备份(重写钩子方法)
System.out.println("在服务器中备份一份");
}
}
//客户端调用
public class Client {
public static void main(String[] args) {
System.out.println("===============导出csv文件==============");
FileExport csv = new CSVFileExport();
csv.export();
System.out.println("===============导出重要文件==============");
FileExport importFile = new ImportFileExport();
importFile.export();
}
}
命令模式
public interface DBCommand {
/*
命令接口:
命令类中只需要列出几个需要被实现的命令,
这些命令会被具体的实现类传导到具体的执行者(action)中。
执行者根据这些命令执行对应的操作即可。
*/
//执行命令
void excute();
//回滚
void undo();
}
//删除命令
public class DeleteDBCommand implements DBCommand {
/*
DB就是执行者,这里我们也可以是一个抽象的接口类
a用于记录插入的数据,以方便后面undo操作
*/
private DB db;
private String[] a;
public DeleteDBCommand(DB db, String...a) {
this.db = db;
this.a = a;
}
/*
下面两个就是具体的命令,和执行者执行方法
*/
@Override
public void excute() {
db.delete(a);
}
@Override
public void undo() {
db.insert(a);
}
}
//插入命令
public class InsertDBCommand implements DBCommand {
private DB db;
private String[] a;
public InsertDBCommand(DB db, String...a) {
this.db = db;
this.a = a;
}
@Override
public void excute() {
db.insert(a);
}
@Override
public void undo() {
db.delete(a);
}
}
//查询命令
public class SelectDBCommand implements DBCommand {
private DB db;
private String[] a;
public SelectDBCommand(DB db, String...a) {
this.db = db;
this.a = a;
}
@Override
public void excute() {
db.select(a);
}
@Override
public void undo() {
//查询不需要undo,这里空实现
}
}
//更新命令
public class UpdateDBCommand implements DBCommand {
private DB db;
private String a;
private String b;
public UpdateDBCommand(DB db, String a, String b) {
this.db = db;
this.a = a;
this.b = b;
}
@Override
public void excute() {
db.update(this.a, this.b);
}
@Override
public void undo() {
db.update(this.b, this.a);
}
}
//执行者,负责执行命令
public class DB {
private String name;
public DB(String name) {
this.name = name;
}
private String getValue(String[] a){
StringBuilder sb = new StringBuilder();
for(int i = 0; i < a.length; i++){
sb.append(a[i] + ",");
}
return sb.toString();
}
public void insert(String... a){
System.out.println("在" + name + "插入数据:" + getValue(a));
}
public void delete(String... a){
System.out.println("在" + name + "删除数据:" + getValue(a));
}
public void select(String... a){
System.out.println("在" + name + "查询数据:" + getValue(a));
}
public void update(String a, String b){
System.out.println("在" + name + "把" + a + "改成" + b);
}
}
//这里不属于命令模式角色,个人封装
public class DBController {
/**
* 用于保存已经执行了的命令,以方便回滚
*/
private Stack<DBCommand> undoCommands;
public DBController() {
this.undoCommands = new Stack<>();
}
/*
面向Client编程,这里我封装了执行和撤回方法
在执行方法中,进行记录
在客户端那边只需要传入命令,并在命令中执行具体的执行者就好了
当然,我们也可以在Controller中写定,不过这里的实现是在DBCommand的构造方法中指定执行者
*/
public void excute(DBCommand command){
command.excute();
//如果是查询,就不执行撤回
if(!(command instanceof SelectDBCommand)) {
undoCommands.push(command);
}
}
public void rollBack(){
if(!undoCommands.empty()) {
DBCommand command = undoCommands.pop();
command.undo();
}
}
}
public class Client {
public static void main(String[] args) {
DBController dbController = new DBController();
//执行者
DB db1 = new DB("数据库1");
DB db2 = new DB("数据库2");
/*
客户端给controller发出指令,并指定具体的执行者
*/
System.out.println("====================excute======================");
//插入
dbController.excute(new InsertDBCommand(db1, "a", "b", "c"));
//删除
dbController.excute(new DeleteDBCommand(db2, "a", "b", "c"));
//查询
dbController.excute(new SelectDBCommand(db1, "a", "b", "c"));
//修改
dbController.excute(new UpdateDBCommand(db2, "a", "b"));
System.out.println("==================rollBack========================");
dbController.rollBack();
dbController.rollBack();
dbController.rollBack();
dbController.rollBack();
//这边只会执行三次,因此查询不会回滚
}
}
中介者模式
中介者模式一般使用的很多,比如MVC,其中的Controller就是中介者;又比如消息队列,它充当的也是中介者的角色。中介者(Mediator)主要是负责协调各个模块的功能,而模块(Component)之间不需要关心其他模块的存在,也不用关心与其他模块的交互。模块除了提供自己的功能,通常需要实现与中介模块的通讯功能,如:给中介发消息,接收中介的消息。中介者作为协调各个模块的角色,它首先需要保存各个模块的引用(模块注册),还想需要能接受模块发送过来的消息并做相应的处理。
代码如下
//中介者接口
public interface Mediator {
/*
这里就声明两个方法:注册/通知
*/
//注册组件
void register(Components components);
//通知中介
void relay(Components components);
}
//中介者实现类
public class ConcernMediator implements Mediator {
/*
中介实现类中通常需要保存各个组件的引用
*/
private List<Components> componentsList;
public ConcernMediator() {
this.componentsList = new ArrayList<>();
}
/*
register就是注册方法,组件需要将自己注册到具体的中介者实现类中
*/
@Override
public void register(Components components) {
if(!componentsList.contains(components)) {
componentsList.add(components);
components.setMediator(this);
}
}
/*
relay方法用于组件通知中介:具体的执行内容根据自己业务场景定义
这里就是通知除了components的其他组件
*/
@Override
public void relay(Components components) {
componentsList.forEach(c -> {
if(!c.equals(components)){
//组件接收通知
c.receive();
}
});
}
}
//模块
public abstract class Components {
Mediator mediator;
//发消息
abstract void send();
//接受消息
abstract void receive();
//设置中介者
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
}
//模块实现类
public class ConcernComponents extends Components {
@Override
void send() {
System.out.println(this.hashCode() + "发送消息");
mediator.relay(this);
}
@Override
void receive() {
System.out.println(this.hashCode() + "接收到了消息");
}
}
//客户端
public class Client {
public static void main(String[] args) {
//声明中介
Mediator mediator = new ConcernMediator();
//声明组件
Components components1 = new ConcernComponents();
Components components2 = new ConcernComponents();
Components components3 = new ConcernComponents();
Components components4 = new ConcernComponents();
//组件注册
mediator.register(components1);
mediator.register(components2);
mediator.register(components3);
mediator.register(components4);
//1组件给中介发送消息,这是其他的组件应该会接受到消息
components1.send();
/*
我这里就只实现了一个组件实现类,交互也是简单的交互
具体的应用:中介者可能需要协调各个不同的组件类,
模块的交互可能也需要客户端传入参数。
*/
}
}
责任链模式
责任链是将每个处理方法使用链串起来,一般项目中我们可能会在业务代码中写很多分支,这样会导致业务代码十分臃肿,如果使用责任链模式,则代码看起来会清爽很多。责任链中的一个具体处理者(ConcreteHandlers)需要保存下一个处理者的引用(图中setNext方法),具体什么时候将责任传递给下一个则根据自己业务需求而定,可以是当前处理者无法进行处理再进行传递,也可以是自己处理好结果后,再进行传递。
代码如下
//请求类,由处理类负责处理
public class MyRequest {
/*
一个请求:如果处理者没有办法处理,就给下一个处理者处理;如果能处理就直接处理了,不传了
*/
private int authority; //我这里写死,1/2/3
private String requestName;
public MyRequest(int authority, String requestName) {
this.authority = authority;
this.requestName = requestName;
}
public int getAuthority() {
return authority;
}
public String getRequestName() {
return requestName;
}
}
//抽象的处理类
public abstract class Handler {
Handler handler; //保存下一个处理者
String name; //处理者的名字
public Handler(String name) {
this.name = name;
}
//设置下一个处理者,核心之一:使用该方法可以构造链
void setHandler(Handler handler) {
this.handler = handler;
}
//传入需要处理的请求
abstract void handle(MyRequest request);
}
//一级处理
public class OneLevelHandler extends Handler {
public OneLevelHandler(String name) {
super(name);
}
@Override
void handle(MyRequest request) {
if(request.getAuthority() == 1){
System.out.println(this.name + "处理了" + request.getRequestName());
}else{
//这里处理不了,下一个进行处理
handler.handle(request);
}
}
}
//二级处理
public class TwoLevelHandler extends Handler {
public TwoLevelHandler(String name) {
super(name);
}
@Override
void handle(MyRequest request) {
if(request.getAuthority() == 2){
System.out.println(this.name + "处理了" + request.getRequestName());
}else{
//这里处理不了,下一个进行处理
handler.handle(request);
}
}
}
//三级处理
public class ThreeLevelHandler extends Handler {
public ThreeLevelHandler(String name) {
super(name);
}
@Override
void handle(MyRequest request) {
if(request.getAuthority() == 3){
System.out.println(this.name + "处理了" + request.getRequestName());
}else{
//这里处理不了,下一个进行处理
handler.handle(request);
}
}
}
//客户端调用
public class Client {
public static void main(String[] args) {
//声明处理者
Handler oneHandler = new OneLevelHandler("一级处理者");
Handler twoHandler = new TwoLevelHandler("二级处理者");
Handler threeHandler = new ThreeLevelHandler("三级处理者");
//设置责任链
oneHandler.setHandler(twoHandler);
twoHandler.setHandler(threeHandler);
// 这里我串成了一个环,根据业务不同自行选择
threeHandler.setHandler(oneHandler);
//上面可以是使用工厂模式,然后返回要给最低等级的处理者给客户端。我这里简单写了
MyRequest request = new MyRequest(1, "删除请求");
//传入环的好处,在于不管使用哪个等级的处理者调用处理方法都问题不大
threeHandler.handle(request);
}
}
策略模式
解释
使用策略模式时,建议找出负责用许多不同方式完成特定任务的类, 然后将其中的算法抽取到一组被称为策略的独立类中。上下文(Context)的原始类必须包含一个成员变量来存储对于每种策略的引用。 上下文并不执行任务, 而是将工作委派给已连接的策略对象。Context不负责选择符合任务需要的算法(也可以使用默认策略)——客户端会将所需策略传递给Context。 实际上, Context并不需要十分了解策略, 它会通过同样的通用接口与所有策略进行交互。因此,Context可独立于具体策略。 这样你就可在不修改上下文代码或其他策略的情况下添加新算法或修改已有算法了。(这里直接copyhttps://refactoringguru.cn/design-patterns/strategy的解释,做了一点点改动,洗稿实锤哈哈哈)
我的思考(不一定对)
策略模式初看感觉好像和模板方法模式有点相似,但是仔细思考了一下还是有很多区别的。模板方法模式的模板类抽象类的功能方法中已经规定好的特定的执行步骤,但是每个步骤方法的实现是交给子类去实现的,它使用的继承。而策略模式使用的是聚合或者组合,在这一点有本质的区别,并且策略模式不一定要将功能方法的步骤写定。
代码如下
//下面是支付策略
public interface PayStrategy {
/*
支付策略
*/
void pay(float cost);
}
public class OnlinePayStrategy implements PayStrategy {
@Override
public void pay(float cost) {
System.out.println("使用电子支付" + cost + "元");
}
}
public class CashPayStrategy implements PayStrategy {
@Override
public void pay(float cost) {
System.out.println("使用现金支付" + cost + "元");
}
}
//购买渠道策略
public interface ShopStrategy {
/*
购物策略
*/
void shop(String name);
}
public class OnlineShopStrategy implements ShopStrategy {
@Override
public void shop(String name) {
System.out.println("在网上商城购买了" + name);
}
}
public class OfflienShopStrategy implements ShopStrategy {
@Override
public void shop(String name) {
System.out.println("在线下商店购买了" + name);
}
}
//Context类
public class Shopping {
/*
这里需要协调不同得策略,构造方法可以给定默认策略(不给问题不大),
但是需要提供自定义策略的方法(构造器/set方法)
*/
private PayStrategy payStrategy;
private ShopStrategy shopStrategy;
//构造器
public Shopping(){
this.payStrategy = new CashPayStrategy();
this.shopStrategy = new OfflienShopStrategy();
}
public Shopping(PayStrategy payStrategy, ShopStrategy shopStrategy) {
this.payStrategy = payStrategy;
this.shopStrategy = shopStrategy;
}
//set方法
public void setPayStrategy(PayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
public void setShopStrategy(ShopStrategy shopStrategy) {
this.shopStrategy = shopStrategy;
}
//这里算协调吧
public void shopping(String name, float cost){
shopStrategy.shop(name);
payStrategy.pay(cost);
}
}
//客户端的调用
public class Client {
public static void main(String[] args) {
//客户端这里展示使用构造器设置策略
Shopping shopping = new Shopping(
new OnlinePayStrategy(),
new OnlineShopStrategy()
);
shopping.shopping("手机", 4999);
System.out.println("==============================");
shopping.setPayStrategy(new CashPayStrategy());
shopping.shopping("电脑", 9999);
}
}
迭代器模式
迭代器模式在大部分的集合类中都有具体的实现,迭代器的主要作用是为客户端提供访问集合的方式。这样用户就不需要关注集合的内部结构,从而减少系统的耦合。
实现方式
集合接口(Collection)依赖迭代器接口,用户可以通过集合创建对应的迭代器
具体的迭代器(Concrete Iterator)是针对特定的具体集合(Concrete Collection)进行实现的
具体的集合需要实现集合接口,以此返回具体的迭代器
//二叉树的结构
public class TreeNode {
private String name;
private TreeNode left, right;
public TreeNode(String name) {
this.name = name;
}
//getter/setter/toString
}
//具体的迭代器,这里实现了java.utils.Iterator
public class TreeIterator implements Iterator<TreeNode> {
Stack<TreeNode> treeNodes = new Stack<>();
public TreeIterator(TreeNode node) {
this.treeNodes.push(node);
}
@Override
public boolean hasNext() {
return (!treeNodes.isEmpty() && treeNodes.peek() != null);
}
/*
这里使用先序遍历
*/
@Override
public TreeNode next() {
TreeNode node = treeNodes.pop();
if(node.getRight() != null){
treeNodes.push(node.getRight());
}
if(node.getLeft() != null) {
treeNodes.push(node.getLeft());
}
return node;
}
}
//这里简单写了,具体的集合类
public class TreeNodeUtils {
private String name;
private TreeNode node;
public TreeNodeUtils(String name, TreeNode node) {
this.name = name;
this.node = node;
}
public String getName() {
return name;
}
//这里可以是实现接口的抽象方法,创建一个迭代器
public Iterator iterator(){
return new TreeIterator(node);
}
}
//客户端的调用
public class Client {
public static void main(String[] args) {
TreeNode node1 = new TreeNode("1");
TreeNode node2 = new TreeNode("2");
TreeNode node3 = new TreeNode("3");
TreeNode node4 = new TreeNode("4");
TreeNode node5 = new TreeNode("5");
node1.setLeft(node3);
node3.setLeft(node5);
node1.setRight(node4);
node4.setLeft(node2);
TreeNodeUtils utils = new TreeNodeUtils("树1", node1);
Iterator iterator = utils.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next().toString());
}
}
}
观察者模式
观察者模式主要分为两大部分:发布者,订阅者。
发布者
通常发布者的状态发生改变后需要通知订阅者,因此发布者需要保存订阅者的集合,通知的时候就遍历订阅者集合,调用订阅者的通知方法。
订阅者
订阅者这边需要提供一个被通知的方法,以遍对发布者的订阅做出反应
代码如下
//抽象的发布者
public abstract class Publisher {
/*
消息发布者(充当被观察者),他的核心方法:订阅/取消订阅/推送消息
*/
//订阅者集合
private List<Subscriber> subscriberList;
private String title;
public Publisher(String title) {
subscriberList = new ArrayList<>();
this.title = title;
}
public Publisher() {
subscriberList = new ArrayList<>();
}
public void subscribe(Subscriber subscriber) {
subscriberList.add(subscriber);
}
public void cancel(Subscriber subscriber) {
if(subscriberList.contains(subscriber)){
subscriberList.remove(subscriber);
}
}
//向订阅者发布消息
public void publish() {
if(subscriberList.size() != 0){
for(Subscriber s : subscriberList){
s.send(this);
}
}
}
//更新内容
abstract void update(String title);
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
update(title);
}
}
//具体的发布者1
public class VideoPublisher extends Publisher {
public VideoPublisher(String title) {
super(title);
}
public VideoPublisher() {
super();
}
@Override
public void update(String title) {
System.out.println("更新了视频内容:" + title);
this.publish();
}
}
//具体发布者2
public class ArticlePublisher extends Publisher{
public ArticlePublisher(String title) {
super(title);
}
public ArticlePublisher() {
super();
}
@Override
void update(String title) {
System.out.println("更新文章内容:" + title);
this.publish();
}
}
//订阅者
public class Subscriber {
/*
消息订阅者(充当观察者):它能接受发布者的推送的消息
这里可以抽象的接口,不同的具体实现类的执行方式可以不同
这里就简单写了,只有一种订阅者(执行方法一样)
*/
private String name;
public Subscriber(String name) {
this.name = name;
}
//接受发布者的消息
public void send(Publisher publisher){
System.out.println(this.name + "收到了推送,标题:" + publisher.getTitle());
}
//订阅发布者
public void subscribe(Publisher publisher){
publisher.subscribe(this);
}
}
//客户端调用
public class Client {
public static void main(String[] args) {
//内容发布者
Publisher articlePublisher = new ArticlePublisher();
Publisher videoPublisher = new VideoPublisher();
//订阅者
Subscriber user_1 = new Subscriber("用户1");
Subscriber user_2 = new Subscriber("用户2");
Subscriber user_3 = new Subscriber("用户3");
user_1.subscribe(articlePublisher);
user_2.subscribe(videoPublisher);
user_3.subscribe(articlePublisher);
user_3.subscribe(videoPublisher);
articlePublisher.setTitle("头条内容资料流出,只要掌握这一项技能,你将超过99%的开发者!");
System.out.println("=============================================================");
videoPublisher.setTitle("高并发你不得不知道的那些事!");
}
}
状态模式
基于条件语句来实现一个有限状态机,判断对象当前的状态并调用对应状态下的行为方法,这种方式在状态较多的情况下,逻辑容易变的复杂并且判断语句会层层累加不利于维护。因此使用状态模式可以很好的解决此类问题的发生。
状态模式建议为对象的所有可能状态新建一个抽象类, 然后将所有状态的对应行为抽取到这些类中。原始对象被称为上下文 (context), 它并不会自行实现所有行为, 而是会保存一个指向表示当前状态的状态对象的引用, 且将所有与状态相关的工作委派给该对象(即上下文的行为的具体执行者是具体状态类,上下文类会保存当前啊状态对象实例)。上下文除了提供各种行为外,还需要提供更换状态的方法,因此所有的状态类都需要实现同一个接口,这样上下文只需要跟状态接口类进行交互。
与策略模式的区别
这个结构可能看上去与策略模式相似, 但有一个关键性的不同——在状态模式中, 特定状态知道其他所有状态的存在, 且能触发从一个状态到另一个状态的转换; 策略则几乎完全不知道其他策略的存在。
//状态接口,这里定义动作,具体状态类根据接口的动作返回状态
public interface State {
/*
这里模拟一场考试的的不同状态下的行为
状态有:开考前/发卷时间/开考后/交卷时间/答题结束/成绩批改后
行为分别是:
查看试卷内容
答题
提交
查看成绩
*/
void look();
void write();
void submit();
void searchGrade();
}
//考前状态
public class BeforeExamState implements State{
private Exam exam;
public BeforeExamState(Exam exam) {
this.exam = exam;
}
@Override
public void look() {
System.out.println(exam.getName() + ":未开考无法查看试卷");
}
@Override
public void write() {
System.out.println(exam.getName() + ":未开考无法作答");
}
@Override
public void submit() {
System.out.println(exam.getName() + ":未开考无法提交");
}
@Override
public void searchGrade() {
System.out.println(exam.getName() + ":未开考无法查看考试成绩");
}
}
//开考状态,此时不能答题
public class DispensePaperState implements State {
private Exam exam;
public DispensePaperState(Exam exam) {
this.exam = exam;
}
@Override
public void look() {
System.out.println(exam.getName() + ":可以查看试卷");
}
@Override
public void write() {
System.out.println(exam.getName() + ":未开考无法作答");
}
@Override
public void submit() {
System.out.println(exam.getName() + ":未开考无法提交");
}
@Override
public void searchGrade() {
System.out.println(exam.getName() + ":未开考无法查看考试成绩");
}
}
//开考状态,可以提交
public class CanSubmitState implements State {
private Exam exam;
public CanSubmitState(Exam exam) {
this.exam = exam;
}
@Override
public void look() {
System.out.println(exam.getName() + ":可以查看试卷");
}
@Override
public void write() {
System.out.println(exam.getName() + ":已开考可以开始作答");
}
@Override
public void submit() {
System.out.println(exam.getName() + ":可以提交");
}
@Override
public void searchGrade() {
System.out.println(exam.getName() + ":暂无考试成绩");
}
}
//批改状态
public class ExamingState implements State {
private Exam exam;
public ExamingState(Exam exam) {
this.exam = exam;
}
@Override
public void look() {
System.out.println(exam.getName() + ":已开考可以查看试卷");
}
@Override
public void write() {
System.out.println(exam.getName() + ":已开考可以开始作答");
}
@Override
public void submit() {
System.out.println(exam.getName() + ":未到规定时间无法提交");
}
@Override
public void searchGrade() {
System.out.println(exam.getName() + ":暂无考试成绩");
}
}
//批改完成
public class GradedState implements State {
private Exam exam;
public GradedState(Exam exam) {
this.exam = exam;
}
@Override
public void look() {
System.out.println(exam.getName() + ":考试已结束,不得查看试卷");
}
@Override
public void write() {
System.out.println(exam.getName() + ":考试已结束,不得作答");
}
@Override
public void submit() {
System.out.println(exam.getName() + ":考试已结束,不得提交");
}
@Override
public void searchGrade() {
System.out.println(exam.getName() + ":成绩出来了。。。。。。");
}
}
//上下文类,负责协调
public class Exam {
//初始化当前的状态
private State state = null;
//试卷名称
private String name;
private int time;
public Exam(String name) {
this.state = new BeforeExamState(this);
this.name = name;
}
public String getName() {
return name;
}
//下面这些行为方法根据state会有不同的执行策略(这里有点像策略模式,因为我没有在行为方法中写状态转移,这是状态模式和策略模式的区别
// 策略模式各个策略互相不认识,而状态模式直接的状态通常需要关联)
//开始前
void begin() {
System.out.println(name + ":开始考试");
if(state instanceof BeforeExamState)
state = new DispensePaperState(this);
}
//查看试卷
void look() {
state.look();
if(state instanceof DispensePaperState)
state = new ExamingState(this);
}
void write() {
state.write();
if(state instanceof ExamingState)
state = new CanSubmitState(this);
}
void submit(){
state.submit();
if(state instanceof CanSubmitState)
state = new GradedState(this);
}
void searchGrade(){
state.searchGrade();
}
}
public class Client {
public static void main(String[] args) {
//正常流程
Exam exam = new Exam("好试卷");
exam.begin();
exam.look();
exam.write();
exam.submit();
// exam.write(); //已经提交再作答就不行了
exam.searchGrade();
System.out.println("================================");
//非正常
Exam exam2 = new Exam("坏试卷");
exam2.begin();
exam2.look();
exam2.submit();
}
}
访问模式
实体有两个:一个是被访问的元素(Element)、另一个是访问者(Visitor)
被访问的具体元素能够告知到具体的访问者,被访问时可以根据不同的访问者执行不同的方法,同时也能调用具体的访问者的方法。
具体的访问者则对每个具体元素都有一个访问方法,以供元素调用
//元素类
public abstract class Website {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Website(String title) {
this.title = title;
}
//抽象方法,用于接收不同的访问者
abstract void visited(Visitor visitor);
}
//具体元素1
public class ELearnWebsite extends Website {
public ELearnWebsite(String title) {
super(title);
}
@Override
public void visited(Visitor visitor) {
System.out.println("在线学习网址被访问"); //被访问,元素执行的方法
visitor.visit(this); //执行访问者的方法
}
}
//具体元素2
public class BilibiliWebsite extends Website {
public BilibiliWebsite(String title) {
super(title);
}
@Override
public void visited(Visitor visitor) {
System.out.println("bilibili被访问了");
visitor.visit(this);
}
}
//访问者
public interface Visitor {
//针对不同的元素提供不同的访问方法
void visit(BilibiliWebsite website);
void visit(ELearnWebsite website);
}
//具体访问者1
public class GoodStudent implements Visitor {
@Override
public void visit(BilibiliWebsite website) {
System.out.println("在B站学习:" + website.getTitle() + ",一学就是十个小时");
}
@Override
public void visit(ELearnWebsite website) {
System.out.println("在E-learning网站学习:" + website.getTitle());
}
}
//具体访问者2
public class NormalStudent implements Visitor {
@Override
public void visit(BilibiliWebsite website) {
System.out.println("在B站学习:" + website.getTitle() + ",十分钟后刷起来动态");
}
@Override
public void visit(ELearnWebsite website) {
System.out.println("在学习网站学习:" + website.getTitle() + ",并睡得很香");
}
}
//用户端
public class Client {
public static void main(String[] args) {
Visitor goodStudent = new GoodStudent();
Visitor normalStudent = new NormalStudent();
BilibiliWebsite bilibili = new BilibiliWebsite("Java设计模式");
goodStudent.visit(bilibili);
normalStudent.visit(bilibili);
System.out.println("=============================");
ELearnWebsite website = new ELearnWebsite("数据库原理");
goodStudent.visit(website);
normalStudent.visit(website);
}
}
备忘录模式
备忘录模式的代码实现方式有很多种,不同的语言可能实现的方式不一样。但是总体的思路是一样的,备忘录模式大致有两个角色:原发器、备忘录、负责人。
原发器
原发器是被记录(备份)的对象,它的状态会发生变化,通常状态发生变化之前会保存状态到备忘录中。
备忘录
备忘录也是快照,记录着原发器某一时刻的状态。它只能够被初始化,不能被修改(没有setter方法)。
负责人
它负责聚合一个原发器和备忘录集合。通过负责人能够保存原发器的状态,或撤回到上一次状态。
这里做一个简单实现
/**
* 原发类(它会产生一个快照,并交给备忘录记录)
* 这个类可以是实现某个接口,我这里从简
*/
public class Originator {
private String name;
private String chapter;
private String status;
private String time;
public Originator(String name, String chapter, String status, String time) {
this.name = name;
this.chapter = chapter;
this.status = status;
this.time = time;
}
//保存当前的状态属性
public Memento currState(){
return new Memento(this.name, this.chapter, this.status, this.time);
}
//撤回到之前的状态属性
public void revoke(Memento memento){
this.name = memento.getName();
this.chapter = memento.getChapter();
this.status = memento.getStatus();
this.time = memento.getTime();
}
//...geter seter toString
}
/**
* 备忘录,我们这里不一定要记录原发类的所有的属性
* 备忘录也开始是抽象的接口,让具体类去实现,我这里依旧从简
*/
public class Memento {
private String name;
private String chapter;
private String status;
private String time;
public Memento(String name, String chapter, String status, String time) {
this.name = name;
this.chapter = chapter;
this.status = status;
this.time = time;
}
/*
下面的都是get方法,参数在构造器进行初始化,之后就在不允许修改
*/
public String getName() {
return name;
}
public String getChapter() {
return chapter;
}
public String getStatus() {
return status;
}
public String getTime() {
return time;
}
public String getInfo(){
return "Memento{" +
"name='" + name + '\'' +
", chapter='" + chapter + '\'' +
", status='" + status + '\'' +
", time='" + time + '\'' +
'}';
}
}
/*
备忘录类,用于保存不同时期的快照信息
*/
public class Caretaker {
List<Memento> mementoList;
int recentIndex;
public Caretaker() {
this.mementoList = new ArrayList<>();
recentIndex = -1;
}
public void add(Memento memento){
mementoList.add(memento);
recentIndex++;
}
public Memento getRecent(){
if(recentIndex > 0)
return mementoList.get(recentIndex--);
else
return mementoList.get(0);
}
}
//用户
public class Client {
public static void main(String[] args) throws InterruptedException {
//caretaker作为一个originator的备忘录,保存originator不同时期的状态
Caretaker caretaker = new Caretaker();
//第一次保存
Originator originator = new Originator("开始", "第一章", "简单", new Date().toString());
caretaker.add(originator.currState());
Thread.sleep(1000);
//改变后,第二次保存(偷懒,直接初始化,一般setter就好)
originator = new Originator("进阶", "第二章", "普通", new Date().toString());
caretaker.add(originator.currState());
Thread.sleep(1000);
//改变后,第三次保存
originator = new Originator("高阶", "第三章", "困难", new Date().toString());
caretaker.add(originator.currState());
Thread.sleep(1000);
//改变后,不保存,开始撤回
originator = new Originator("顶级", "第四章", "变态", new Date().toString());
//第一次撤回,4->3
originator.revoke(caretaker.getRecent());
System.out.println(originator);
Thread.sleep(1000);
//第二次撤回, 3->2
originator.revoke(caretaker.getRecent());
System.out.println(originator);
Thread.sleep(1000);
//第三次撤回, 2->1
originator.revoke(caretaker.getRecent());
System.out.println(originator);
Thread.sleep(1000);
}
}