前言
推荐看这篇文章之前先了解Java日记之设计模式初探。
行为型设计模式总共有11种
1.模板方法模式
2.迭代器模式
3.策略模式
4.解释器模式
5.观察者模式
6.备忘录模式
7.命令模式
8.中介者模式
9.责任链模式
10.访问者模式
11.状态模式
1.模板方法模式
-
定义:定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。比如把大象放进冰箱分几步,首先打开开门,然后放进去,最后关山门,这3个步骤就可以用模板方法设计模式。这个算法就是一个处理过程。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
-
适用场景:一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。各子类中公共的行为被提取出来并集中到一个公共父类中,从而避免代码重复。
-
优点:提高复用性,提高扩展性。符合开闭原则。
-
缺点:类数目增加,增加了系统实现的复杂度,继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要修改一遍。
-
扩展:钩子方法(它是这个模板对子类更进一层的开发和拓展)。
-
相关设计模式:工厂方法模式(工厂是模板的一种特殊实现)。策略模式(都有封装算法,策略模式的目的是使不同的算法可以相互替换,并且不影响应用层的使用。模板是针对一个定义算法的流程,而将一些不太一样的具体实现步骤交给子类实现,模板是不改变算法的流程的,策略是可以改变的,并且是可以相互替换的)。
代码举例:
我们引入一个场景,比如说制作一门课程。对于制作一个课程,需要制作PPT,制作视频,还要决定是否要写一个手记,还有是否要提供一些素材,意思就是说,不同的课程,制作的主线是一样的,在细分的一些细节上还是有所不同。比如有一些课程不需要手记,所以写手机这个是一个非必选项,完全留给子类来扩展,那就可以把这个方法做出钩子方法。
//定义模板标准
public abstract class ACourse {
protected final void makeCourse(){
this.makePPT();
this.makeVideo();
if (needWriteArticle()){
this.writeArticle();
}
this.packageCourse();
}
//制作什么课程都要PPT
final void makePPT(){
System.out.println("制作PPT");
}
final void makeVideo(){
System.out.println("制作视频");
}
final void writeArticle(){
System.out.println("编写手记");
}
//钩子方法
protected boolean needWriteArticle(){
return false;
}
abstract void packageCourse();
}
//设计模式课程
public class DesignPatternCourse extends ACourse{
@Override
void packageCourse() {
System.out.println("提供课程Java源代码");
}
//如果这个课程需要手记就重写为true
@Override
protected boolean needWriteArticle() {
return true;
}
}
//前端课程
public class FECourse extends ACourse{
//这个钩子值也可以交给应用层
private boolean needWriteArticleFlag = false;
public FECourse(boolean needWriteArticleFlag) {
this.needWriteArticleFlag = needWriteArticleFlag;
}
@Override
void packageCourse() {
System.out.println("提供课程前端代码");
System.out.println("提供课程图片素材");
}
@Override
protected boolean needWriteArticle() {
return needWriteArticleFlag;
}
}
//测试类
public class Test {
public static void main(String[] args) {
System.out.println("后端设计模式课程start---");
ACourse designCourse = new DesignPatternCourse();
designCourse.makeCourse();
System.out.println("后端设计模式课程end---");
System.out.println("前端课程start---");
ACourse fECourse = new FECourse(true);
fECourse.makeCourse();
System.out.println("前端课程end---");
}
}

通过定义一个模板类,然后由子类详细的编写具体的步骤,模板方法设计模式的核心就是要加final,否则子类是可以重写的,这就是失去模板方法设计模式的用意了。
2.迭代器模式
-
定义:提供一种方法,顺序访问一个集合对象中的各个元素,而又不暴露该对象的内部表示。简单理解就是遍历。
-
适用场景:访问一个集合对象的内容而无需暴露它的内部表示。为遍历不同的集合结构提供一个统一的接口。
-
优点:分离了集合对象的遍历行为。
-
缺点:类的个数成对增加。因为这个设计模式是把存储数据和遍历数据这两个职责进行分离。
-
相关设计模式:访问者模式。
代码举例:
这里注意一下,迭代器在日常使用当中是很广泛的,但是我们是用的Jdk还有开源的项目当中,这些数据结构都会使用现成的迭代器,几乎不会自己去写一个。我们这里来解读写好的迭代器代码。
举例一个业务场景,就是慕课网网站的课程,首先我们有一个课程的实体类,然后进行遍历。
//课程实体类
public class Course {
private String name;
public Course(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//数据操作接口
public interface CourseAggregate {
void addCourse(Course course);
void removeCourse(Course course);
//返回迭代器
CourseIterator getCourseIterator();
}
//实现操作接口
public class CourseAggregateImpl implements CourseAggregate {
private List courseList;
public CourseAggregateImpl() {
this.courseList = new ArrayList();
}
@Override
public void addCourse(Course course) {
courseList.add(course);
}
@Override
public void removeCourse(Course course) {
courseList.remove(course);
}
@Override
public CourseIterator getCourseIterator() {
//返回实现迭代器接口的类
return new CourseIteratorImpl(courseList);
}
}
//迭代器接口
public interface CourseIterator {
Course nextCourse();
boolean isLastCourse();
}
//实现迭代器
public class CourseIteratorImpl implements CourseIterator {
private List courseList;
private int position;
private Course course;
public CourseIteratorImpl(List courseList){
this.courseList=courseList;
}
@Override
public Course nextCourse() {
System.out.println("返回课程,位置是: "+position);
course=(Course)courseList.get(position);
position++;
return course;
}
@Override
public boolean isLastCourse(){
if(position< courseList.size()){
return false;
}
return true;
}
}
//测试类
public class Test {
public static void main(String[] args) {
Course course1 = new Course("Java电商一期");
Course course2 = new Course("Java电商二期");
Course course3 = new Course("Java设计模式精讲");
Course course4 = new Course("Python课程");
Course course5 = new Course("算法课程");
Course course6 = new Course("前端课程");
CourseAggregate courseAggregate = new CourseAggregateImpl();
courseAggregate.addCourse(course1);
courseAggregate.addCourse(course2);
courseAggregate.addCourse(course3);
courseAggregate.addCourse(course4);
courseAggregate.addCourse(course5);
courseAggregate.addCourse(course6);
System.out.println("-----课程列表-----");
printCourses(courseAggregate);
courseAggregate.removeCourse(course4);
courseAggregate.removeCourse(course5);
System.out.println("-----删除操作之后的课程列表-----");
printCourses(courseAggregate);
}
public static void printCourses(CourseAggregate courseAggregate){
CourseIterator courseIterator= courseAggregate.getCourseIterator();
while(!courseIterator.isLastCourse()){
Course course=courseIterator.nextCourse();
System.out.println(course.getName());
}
}
}
我们分别创建了CourseAggregate和CourseIterator,然后通过实现CourseAggregate的类的getCourseIterator()
来获取你自定义的迭代器,最后在测试类中通过printCourses()
来遍历课程。这就是迭代器的基本创建了。
3.策略模式
-
定义:定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户(应用层)。可以替换大量的if...else...。
-
适用场景:系统有很多类,而他们的区别仅仅在于他们的行为不同。一个系统需要动态地在几种算法中选择一种。
-
优点:提供了对开闭原则的完美支持。可以避免使用多重条件转移语句。提高算法的保密性和安全性。
-
缺点:客户端必须知道所有的策略类,并且自行来决定使用哪一个策略类。而且会产生很多策略类。
-
相关设计模式:工厂模式、状态模式。
代码举例:
电商网站在618会举行各种促销活动,促销是课程的行为,促销行为,但是有多种实现。策略模式一般不是单独使用的,一般是配合工厂、单例、享元模式来使用的,我们这里创建一个策略工厂模式来配合实现。
//策略促销接口
public interface PromotionStrategy {
//进行促销
void doPromotion();
}
//无促销实现
public class EmptyPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion() {
System.out.println("无促销");
}
}
//返现实现
public class FanXianPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion() {
System.out.println("返现促销,返回的金额存到慕课网用户的余额中");
}
}
//满减实现
public class ManJIanPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion() {
System.out.println("满减促销,满200减20");
}
}
//立减实现
public class LiJianPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion() {
System.out.println("立减促销,课程的价格直接减去配置的价格");
}
}
//策略工厂
public class PromotionStrategyFactory {
private static Map<String, PromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<>();
//类加载的时候
static {
PROMOTION_STRATEGY_MAP.put(PromotionKey.LIJIAN, new LiJianPromotionStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.FANXIAN, new FanXianPromotionStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.MANJIAN, new ManJIanPromotionStrategy());
}
private static final PromotionStrategy NON_PROMOTION = new EmptyPromotionStrategy();
private PromotionStrategyFactory() {
}
public static PromotionStrategy getPromotionStrategy(String promotionStrategKey) {
PromotionStrategy promotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionStrategKey);
return promotionStrategy == null ? NON_PROMOTION : promotionStrategy;
}
private interface PromotionKey {
final String LIJIAN = "LIJIAN";
final String FANXIAN = "FANXIAN";
final String MANJIAN = "MANJIAN";
}
}
//策略活动
public class PromotionActivity {
private PromotionStrategy promotionStrategy;
public PromotionActivity(PromotionStrategy promotionStrategy) {
this.promotionStrategy = promotionStrategy;
}
public void executePromotionStrategy(){
promotionStrategy.doPromotion();
}
}
//测试类
public class Test {
public static void main(String[] args) {
//假设key是外部传进来的
String promotionKey1 = "LIJIAN";
PromotionActivity promotionActivity1 = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey1));
promotionActivity1.executePromotionStrategy();
String promotionKey2 = "MANJIAN";
PromotionActivity promotionActivity2 = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey2));
promotionActivity2.executePromotionStrategy();
String promotionKey3 = "";
PromotionActivity promotionActivity3 = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey3));
promotionActivity3.executePromotionStrategy();
}
}

整个业务逻辑的架构不变,只是替换其中的促销业务逻辑,就可以通过key值来决定你要进行什么样子的促销策略,然后将key值传入工厂策略模式里面进行调用,这样就可以减少大量的if...else...了,唯一的缺点就是应用层必须知道有哪些促销策略。
4.解释器模式
-
定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。为了解释一种语言,而为语言创建的解释器。
-
适用场景:某个特定类型问题发生频率足够高,例如脚本语言,或者日志文件,我们需要对这些文件进行解析,就要通过程序(解释器)来解决该问题。就是其一般使用开源工具包
-
优点:语言由很多类表示,容易改变及扩展此“语言”。
-
缺点:当语法规则数目太多时,增加了系统复杂度。
-
相关设计模式:适配器模式
解释器模式的使用频率其实是很低的。而且现在实现解释器模式一般也是使用开源工具包来进行实现,这里就不上代码举例了。
5.观察者模式
-
优点:定义了对象之间的一对多的依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新。比如,微信的朋友圈点赞之后,就代表着你是观察者,而朋友圈这条信息就是被观察者就是主题对象,那在这个主题对象被评论的时候,微信就会通知观察者即点赞的人,这就是观察者模式的应用、
-
适用场景:关联行为场景,建立一套触发机制。
-
优点:观察者和被观察者之间建立了一个抽象的耦合。观察者模式支持广播通信。
-
缺点:观察者之间有过多的细节依赖,提高时间消耗及程序复杂度。还有使用要得到,避免循环调用。
代码举例:
//课程类,继承Observable,是被观察者
public class Course extends Observable {
private String CourseName;
public Course(String courseName) {
this.CourseName = courseName;
}
public String getCourseName() {
return CourseName;
}
public void produceQuestion(Course course, Question question) {
System.out.println(question.getUserName() + "在" + course.CourseName+"提交了一个问题");
setChanged();
notifyObservers(question);
}
}
//问题类
public class Question {
private String userName;
private String questionContent;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getQuestionContent() {
return questionContent;
}
public void setQuestionContent(String questionContent) {
this.questionContent = questionContent;
}
}
//老师,继承Observer,是观察者
public class Teacher implements Observer {
private String teacherName;
public Teacher(String teacherName) {
this.teacherName = teacherName;
}
@Override
public void update(Observable o, Object arg) {
Course course = (Course) o;
Question question = (Question) arg;
System.out.println(teacherName + "老师的" + course.getCourseName() + "课程接收到一个" + question.getUserName() + "提交的问题:" + question.getQuestionContent());
}
}
//测试类
public class Test {
public static void main(String[] args) {
Course course = new Course("Java设计模式");
Teacher teacher = new Teacher("alpha");
course.addObserver(teacher);
//业务逻辑
Question question = new Question();
question.setUserName("juju");
question.setQuestionContent("java函数如何编写");
course.produceQuestion(course, question);
}
}

Course类继承Observable后就成为了被观察者,Teacher类继承Observer接口就成为了被观察者,需要实现
update()
方法,这个方法就是当被观察者发生改变的时候会调用的方法。在测试类中,通过addObserver()
则就可以添加多个观察者,然后调用Course类的produceQuestion()
方法,在produceQuestion()
方法里面,会调用Observable的setChanged()
方法,表示被观察者发生改变,然后通过notifyObservers()
可以传递需要处理的对象或者业务逻辑,这个对象从update()
中的第二个参数可以获取,第一个参数为被观察者对象。6.备忘录模式
-
定义: 保存一个对象的某个状态,以便在适当的状态恢复对象,一句话,后悔药。
-
适用场景:保存及恢复数据相关的业务场景。还有后悔的时候,想恢复之间的状态。
-
优点:为用户提供一种可恢复的机制和存档信息的封装。
-
缺点:资源占用。
-
相关设计模式:状态模式。
我们这里引入一个业务场景,就是在网站上面发布一篇手记,然后其中有暂存的功能,而这个功能就可以通过备忘录设计模式来进行实现。
代码举例:
//手记类
public class Article {
private String title;
private String content;
private String imgs;
public Article(String title, String content, String imgs) {
this.title = title;
this.content = content;
this.imgs = imgs;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getImgs() {
return imgs;
}
public void setImgs(String imgs) {
this.imgs = imgs;
}
//保存快照
public ArticleMemento saveToMemento() {
ArticleMemento articleMemento = new ArticleMemento(this.title,this.content,this.imgs);
return articleMemento;
}
//恢复快照
public void undoFromMemento(ArticleMemento articleMemento) {
this.title = articleMemento.getTitle();
this.content = articleMemento.getContent();
this.imgs = articleMemento.getImgs();
}
@Override
public String toString() {
return "Article{" +
"title='" + title + '\'' +
", content='" + content + '\'' +
", imgs='" + imgs + '\'' +
'}';
}
}
//快照类
public class ArticleMemento {
private String title;
private String content;
private String imgs;
public ArticleMemento(String title, String content, String imgs) {
this.title = title;
this.content = content;
this.imgs = imgs;
}
public String getTitle() {
return title;
}
public String getContent() {
return content;
}
public String getImgs() {
return imgs;
}
@Override
public String toString() {
return "ArticleMemento{" +
"title='" + title + '\'' +
", content='" + content + '\'' +
", imgs='" + imgs + '\'' +
'}';
}
}
首先创建一个Article类,也就是手记类,里面有两个重要的方法,saveToMemento()
和undoFromMemento()
方法。saveToMemento()
主要就是会创建一个ArticleMemento实例,也就是快照实例,然后把需要保存的数据放入到ArticleMemento实例中保存下来,undoFromMemento()
主要就是恢复那些已经保存的快照实例里面的值,这里注意一下,ArticleMemento也就是快照类是不需要set()
方法的,它只能通过构造函数来传递数值。然后,我们在创建一个管理这些快照的管理类。
public class ArticleMementoManager {
private final Stack<ArticleMemento> ARTICLE_MEMENTO_STACK = new Stack<>();
public ArticleMemento getMemento()
{
ArticleMemento articleMemento= ARTICLE_MEMENTO_STACK.pop();
return articleMemento;
}
public void addMemento(ArticleMemento articleMemento)
{
ARTICLE_MEMENTO_STACK.push(articleMemento);
}
}
管理方式通过Stack来进行管理,getMemento()
就是出栈,获得最新保存的快照实例,addMemento()
就是入栈操作了。

我们从UML中可以看出,ArticleMemento类,也就是快照类是只能被Article类来进行创造的,ArticleMemento类和ArticleMementoManager 类是一种聚合关系,因为ArticleMementoManager类可以有多个ArticleMemento。
//测试类
public class Test {
public static void main(String[] args) {
ArticleMementoManager articleMementoManager = new ArticleMementoManager();
Article article= new Article("如影随行的设计模式A","手记内容A","手记图片A");
ArticleMemento articleMemento = article.saveToMemento();
articleMementoManager.addMemento(articleMemento);
System.out.println("标题:"+article.getTitle()+" 内容:"+article.getContent()+" 图片:"+article.getImgs()+" 暂存成功");
System.out.println("手记完整信息:"+article);
System.out.println("修改手记start");
article.setTitle("如影随行的设计模式B");
article.setContent("手记内容B");
article.setImgs("手记图片B");
System.out.println("修改手记end");
System.out.println("手记完整信息:"+article);
articleMemento = article.saveToMemento();
articleMementoManager.addMemento(articleMemento);
article.setTitle("如影随行的设计模式C");
article.setContent("手记内容C");
article.setImgs("手记图片C");
System.out.println("暂存回退start");
System.out.println("回退出栈1次");
articleMemento = articleMementoManager.getMemento();
article.undoFromMemento(articleMemento);
System.out.println("回退出栈2次");
articleMemento = articleMementoManager.getMemento();
article.undoFromMemento(articleMemento);
System.out.println("暂存回退end");
System.out.println("手记完整信息:"+article);
}
}

7.命令模式
-
定义:将“请求”封装为对象,以便使用不同的请求。命令模式解决了应用程序中对象的职责以及它们之间的通信方式。命令模式可以使发送者和接收者完全解耦,发送命令的对象只知道如何发送请求,不需要只要如何完成请求。
-
适用场景:请求调用者和请求接收者需要解耦,使得调用者和接收者不直接交互。需要抽象出等待执行的行为。
-
优点:降低耦合。容易扩展新命令或者一组命令。
-
缺点:命令的无限扩展会增加类的数量,提高系统实现复杂度。
-
相关设计模式:备忘录模式,我们可以通过备忘录保存命令的历史记录。
假设我们开发一个功能,关闭一个课程的视频,或者局部关闭某一小节或者打开。对于这些课程来说,第一章节是免费开放的,后续章节需要购买之后才能学习观看,假如说下了一个命令,免费开放了第二章,又过了一阵,免费开放了第三章。然后又过了一阵把第二章节又关闭了。
代码举例:
//命令接口
public interface Command {
void execute();
}
//课程视频类
public class CourseVideo {
private String name;
public CourseVideo(String name) {
this.name = name;
}
public void open() {
System.out.println(this.name + "课程视频开发");
}
public void close() {
System.out.println(this.name + "课程视频关闭");
}
}
//关闭视频命令
public class CloseCourseVideoCommand implements Command {
private CourseVideo courseVideo;
public CloseCourseVideoCommand(CourseVideo courseVideo) {
this.courseVideo = courseVideo;
}
@Override
public void execute() {
courseVideo.close();
}
}
//打开视频命令
public class OpenCourseVideoCommand implements Command {
private CourseVideo courseVideo;
public OpenCourseVideoCommand(CourseVideo courseVideo) {
this.courseVideo = courseVideo;
}
@Override
public void execute() {
courseVideo.open();
}
}
首先创建一个命令接口和一个CourseVideo类,CourseVideo类里面有要执行的方法,我们的目标就是包装这些方法(命令),接着创建OpenCourseVideoCommand类和CloseCourseVideoCommand类,这两个类继承Command接口,构造函数传入CourseVideo实例,接着在execute()
里实现你要封装的命令,接下来要执行这些命令或者一组命令肯定要有一个执行者,接着创建一个Staff类:
public class Staff {
private List<Command> commandList = new ArrayList<>();
public void addCommand(Command command) {
commandList.add(command);
}
public void executeCommands() {
for (Command command : commandList) {
command.execute();
}
commandList.clear();
}
}
Staff类里面有一个List,里面放入的就是一组命令,通过addCommand()
方法可以放入你想要组合的命令,最后通过executeCommands()
来执行,执行完毕后会通过clear()
来清除这个集合命令。

从UML图可以看出,首先CloseCourseVideoCommand和OpenCourseVideoCommand是命令它们实现Command接口,Staff里面可以有多个Command或者一个。也就是说如果我们扩展Command,Command和Staff已经是一对多的关系,所以Staff里面的代码是不用动的。
public class Test {
public static void main(String[] args) {
CourseVideo courseVideo = new CourseVideo("Java设计模式-命令模式");
OpenCourseVideoCommand openCourseVideoCommand = new OpenCourseVideoCommand(courseVideo);
CloseCourseVideoCommand closeCourseVideoCommand = new CloseCourseVideoCommand(courseVideo);
Staff staff = new Staff();
staff.addCommand(openCourseVideoCommand);
staff.addCommand(closeCourseVideoCommand);
staff.executeCommands();
}
}

8.中介者模式
-
定义:定义一个封装一组对象如何交互的对象。通过使对象明确的相互引用来促进松散耦合,并允许独立地改变它们的交互。
-
适用场景:在系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱切难以理解。交互的公共行为,如果需要改变行为则可以增加新的中介者类。
-
优点:将一对多转化成了一对一、降低程序复杂度,以及类之间的解耦。
-
缺点:中介者过多,导致系统复杂。
-
相关设计模式:观察者模式(有时候会结合使用)。
我们引入一个业务场景,我们所有的课程都有一个学习的群,现在使用的是QQ群,对于学习群来说,小伙伴在里面交流的介质。
代码举例:
//中介者类
public class StudyGroup {
public static void showMessage(User user, String message){
System.out.println(new Date().toString() + " [" + user.getName() + "] : " + message);
}
}
//用户类
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String name) {
this.name = name;
}
public void sendMessage(String message) {
StudyGroup.showMessage(this, message);
}
}
//测试类
public class Test {
public static void main(String[] args) {
User geely = new User("Geely");
User tom= new User("Tom");
geely.sendMessage(" Hey! Tom! Let's learn Design Pattern");
tom.sendMessage("OK! Geely");
}
}
StudyGroup类就是一个中介者类,里面有一个showMessage()
方法,里面传入user和message,是一个静态方法,接着看User类,里面有一个sendMessage()
方法,对于A用户来说,如果在调用sendMessage()
的时候,只和这个中介者模式打交道就可以了,A用户发送了一个消息,然后交由StudyGroup类也就是中介者来进行展示消息。这就是中介者模式的核心。

9.责任链模式
-
定义:为请求创建一个接收此次请求对象的链。
-
适用场景:一个请求的处理需要多个对象当中的一个或几个协作处理。
-
优点:请求的发送者和接收者(请求的处理)解耦,而且责任链也可以动态组合。
-
缺点:如果责任链太长或者处理时间过长,影响性能,责任链有可能过多。
-
相关设计模式:状态模式。
引入一个业务场景,也就是说线上的课程在发布的时候,经过两个人批准,一个是手记的检查人,另一个是视频的检查人。
代码举例:
//课程类
public class Course {
private String name;
private String article;
private String Video;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getArticle() {
return article;
}
public void setArticle(String article) {
this.article = article;
}
public String getVideo() {
return Video;
}
public void setVideo(String video) {
Video = video;
}
@Override
public String toString() {
return "Course{" +
"name='" + name + '\'' +
", article='" + article + '\'' +
", Video='" + Video + '\'' +
'}';
}
}
//责任链接口
public abstract class Approver {
protected Approver approver;
public void setNextApprover(Approver approver){
this.approver = approver;
}
public abstract void deploy(Course course);
}
//手记检查类
public class ArticleApprover extends Approver {
@Override
public void deploy(Course course) {
if (course.getArticle() != null) {
System.out.println(course.getName() + "含有手记批准");
if (approver != null){
approver.deploy(course);
}
}else {
System.out.println(course.getName()+"不包含手记,不批准,流程结束");
return;
}
}
}
//视频检查类
public class VideoApprover extends Approver{
@Override
public void deploy(Course course) {
if (course.getVideo() != null) {
System.out.println(course.getName() + "含有视频批准");
if (approver != null){
approver.deploy(course);
}
}else {
System.out.println(course.getName()+"不包含视频,不批准,流程结束");
return;
}
}
}
首先创建一个责任链接口Approver ,里面deploy()
主要就是每个拦截器具体处理的业务,还有一个setNextApprover()
方法,这个方法也是责任链模式的核心方法,它决定这当前拦截器处理完毕后要执行的下一个拦截器的方法,类似数据结构的链表,然后创建了ArticleApprover和VideoApprover类并实现了Approver接口,在deploy()
方法里面编写具体实现的代码,在执行业务逻辑后,开始判断是否还有下一个拦截器,如果有,就执行下一个拦截器的业务逻辑,如果没有,则拦截链执行完毕。
public class Test {
public static void main(String[] args) {
Approver artApprover = new ArticleApprover();
Approver videoApprover = new VideoApprover();
Course course = new Course();
course.setName("java");
course.setArticle("shouji"); //把这行代码进行注释,artApprover拦截器就会截止,不会继续往下面执行
course.setVideo("video");
artApprover.setNextApprover(videoApprover);
artApprover.deploy(course);
}
}
在测试类中,artApprover通过setNextApprover()
来设置下一个要处理的拦截器为videoApprover,当artApprover的拦截器处理完毕后就会执行videoApprover拦截器的业务逻辑,但是当artApprover的拦截器处理结果是有问题的,则不会处理下一个拦截器,会直接终止整个拦截器链。

10.访问者模式
-
定义:封装作用于某数据结构(如List/Set/Map等)中的各元素的操作。可以在不改变各元素类的前提下,定义作用于这些元素的操作。访问者模式平时用的其实非常少。
-
适用场景:一个数据结构(如List/Set/Map等)包含很多类型对象,数据结构与数据操作分离的需求。
-
优点:增加新的操作很容易,即增加新的访问者。
-
缺点:增加新的数据结构困难,具体元素变更比较麻烦。
-
相关设计模式:迭代器模式。
引入一个业务场景,线上有免费课程FreeCourse和实战课程CodingCourse,然后对这些课程进行遍历。
代码举例:
//课程抽象类
public abstract class Course {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void accept(IVisitor visitor);
}
//实战课程类
public class CodingCourse extends Course {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
//免费课程类
public class FreeCourse extends Course {
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
//访问者接口
public interface IVisitor {
void visit(FreeCourse freeCourse);
void visit(CodingCourse codingCourse);
}
//访问者实现类
public class Visitor implements IVisitor {
//访问免费课程,打印所有免费课程名称
@Override
public void visit(FreeCourse freeCourse) {
System.out.println("免费课程:"+freeCourse.getName());
}
//访问实战课程,打印所有实战课程名称及价格
@Override
public void visit(CodingCourse codingCourse) {
System.out.println("实战课程:"+codingCourse.getName()+" 价格:"+codingCourse.getPrice()+"元");
}
}
首先定义一个Course抽象类,具体的业务逻辑实现zeyou则由CodingCourse和FreeCourse来进行是实现,具体业务逻辑方法是放在accept()
方法中,接下来定义一个访问者IVisitor接口,并创建一个Visitor类来实现和这个访问者接口,这里需要注意一下,如果需要新的访问接口,就可以直接在IVisitor接口中进行定义,具体的展示都是在Visitor里实现的方法来具体实现。
public class Test {
public static void main(String[] args) {
List<Course> courseList = new ArrayList<>();
FreeCourse freeCourse = new FreeCourse();
freeCourse.setName("SpringMVC数据绑定");
CodingCourse codingCourse = new CodingCourse();
codingCourse.setName("Java设计模式精讲 -- By Geely");
codingCourse.setPrice(299);
courseList.add(freeCourse);
courseList.add(codingCourse);
for(Course course : courseList){
course.accept(new Visitor());
}
}
}
在测试类中,最后的循环那里需要一个访问者,具体的实现都是在这个类里面了,应用层就无需在纠结具体的业务逻辑的实现。

11.状态模式
-
定义:允许一个对象在其内部状态改变时,改变它的行为。
-
适用场景:一个对象存在多个状态(不同状态下行为不同),且状态可相互转换。
-
优点:将不同的状态隔离,把各种状态的转换逻辑,分不到State的子类中,减少相互依赖。增加新的状态非常简单。
-
缺点:状态多的业务场景导致数目增加,系统变复杂。
-
相关设计模式:享元模式
引入一个业务场景,就是网站的课程视频,有暂停、播放、快进和停止,在停止的时候是无法快进的。所以如果是停止的状态,在转换成快进的时候要做一个校验。
代码举例:
//视频上下文类
public class CourseVideoContext {
private CourseVideoState courseVideoState;
public final static PlayState PLAY_STATE = new PlayState();
public final static StopState STOP_STATE = new StopState();
public final static PauseState PAUSE_STATE = new PauseState();
public final static SpeedState SPEED_STATE = new SpeedState();
public CourseVideoState getCourseVideoState() {
return courseVideoState;
}
public void setCourseVideoState(CourseVideoState courseVideoState) {
this.courseVideoState = courseVideoState;
this.courseVideoState.setCourseVideoContext(this);
}
public void play(){
this.courseVideoState.play();
}
public void speed(){
this.courseVideoState.speed();
}
public void stop(){
this.courseVideoState.stop();
}
public void pause(){
this.courseVideoState.pause();
}
}
//视频状态类
public abstract class CourseVideoState {
protected CourseVideoContext courseVideoContext;
public void setCourseVideoContext(CourseVideoContext courseVideoContext) {
this.courseVideoContext = courseVideoContext;
}
public abstract void play();
public abstract void speed();
public abstract void pause();
public abstract void stop();
}
首先创建了一个CourseVideoContext类,这是这个设计模式的核心类,里面持有了四个状态的实例和一个CourseVideoState类,getCourseVideoState()
方法为获取视频的当前状态,setCourseVideoState()
为设置视频的状态。
//暂停类
public class PauseState extends CourseVideoState {
@Override
public void play() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
}
@Override
public void speed() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.SPEED_STATE);
}
@Override
public void pause() {
System.out.println("暂停播放课程视频状态");
}
@Override
public void stop() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
}
}
//播放类
public class PlayState extends CourseVideoState {
@Override
public void play() {
System.out.println("正常播放课程视频状态");
}
@Override
public void speed() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.SPEED_STATE);
}
@Override
public void pause() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PAUSE_STATE);
}
@Override
public void stop() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
}
}
//停止类
public class StopState extends CourseVideoState {
@Override
public void play() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
}
@Override
public void speed() {
System.out.println("ERROR 停止状态不能快进!!");
}
@Override
public void pause() {
System.out.println("ERROR 停止状态不能暂停!!");
}
@Override
public void stop() {
System.out.println("停止播放课程视频状态");
}
}
//快进类
public class SpeedState extends CourseVideoState {
@Override
public void play() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
}
@Override
public void speed() {
System.out.println("快进播放课程视频状态");
}
@Override
public void pause() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PAUSE_STATE);
}
@Override
public void stop() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
}
}
//测试类
public class Test {
public static void main(String[] args) {
CourseVideoContext courseVideoContext = new CourseVideoContext();
courseVideoContext.setCourseVideoState(new PlayState());
System.out.println("当前状态:"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.pause();
System.out.println("当前状态:"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.speed();
System.out.println("当前状态:"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.stop();
System.out.println("当前状态:"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.speed();
}
}
在测试类中,创建了一个CourseVideoContext实例,然后通过setCourseVideoState()
方法设置视频状态为PlayState,然后可以通过getCourseVideoState()
来获取当前视频的状态。也可以直接调用courseVideoContext类里的方法去改变状态。

网友评论