美文网首页
Java日记之设计模式总结(行为型)

Java日记之设计模式总结(行为型)

作者: 居居居居居居x | 来源:发表于2019-12-07 21:03 被阅读0次

前言

推荐看这篇文章之前先了解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类图
我们从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
从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类里的方法去改变状态。

状态模式运行结果

参考

相关文章

网友评论

      本文标题:Java日记之设计模式总结(行为型)

      本文链接:https://www.haomeiwen.com/subject/hwyzwctx.html