2.4 迭代器模式
(1)概念
迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
迭代器模式属于行为型模式。
(2)适用场景
1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、需要为聚合对象提供多种遍历方式。
3、为遍历不同的聚合结构提供一个统一的接口。
(3)代码示例
我们将创建一个叙述导航方法的 Iterator 接口和一个返回迭代器的 Container 接口。实现了 Container 接口的实体类将负责实现 Iterator 接口。
IteratorPatternDemo,我们的演示类使用实体类 NamesRepository 来打印 NamesRepository 中存储为集合的 Names。
创建接口:
package com.alibaba.design.iteratorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-21:16 */ public interface Iterator { public boolean hasNext(); public Object next(); } • 12
package com.alibaba.design.iteratorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-21:16 */ public interface Container { public Iterator getIterator(); }
创建实现了 Container 接口的实体类。该类有实现了 Iterator 接口的内部类 NameIterator。
package com.alibaba.design.iteratorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-21:22 */ public class NameRepository implements Container { public String []names = {"Robert" , "John" ,"Julie" , "Lora"}; @Override public Iterator getIterator() { return new NameIterator(); } private class NameIterator implements Iterator { int index; @Override public boolean hasNext() { if (index < names.length){ return true; }else { return false; } } @Override public Object next() { if(this.hasNext()){ return names[index++]; } return null; } } }
客户端测试类
package com.alibaba.design.iteratorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-21:23 */ public class IteratorPatternDemo { public static void main(String[] args) { NameRepository namesRepository = new NameRepository(); for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){ String name = (String)iter.next(); System.out.println("Name : " + name); } } }
输出示例:
(4)该模式在源码中的体现
java.util.Iterator迭代器就是用到了迭代器模式
(5)迭代器模式的优缺点
- 优点:
1、它支持以不同的方式遍历一个聚合对象。
2、迭代器简化了聚合类。
3、在同一个聚合上可以有多个遍历。
4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。 - 缺点:
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
2.5 观察者模式
(1) 概念
观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式。
(2)适用场景
观察者模式主要用于在关联行为之间建立一套触发机制的场景。观察者模式在现实生活应用也非常广泛,
比如:微信朋友圈动态通知、GPser 生态圈消息通知、邮件通知、广播通知、桌面程序的事件响应等(如下图)。
如果有设置指定老师回答,对应的老师就会收到邮件通知,这就是观察者模式的一种应用场
景。我们可能会想到 MQ,异步队列等,其实 JDK 本身就提供这样的 API。
(3) 代码示例
我们用代码来还原一下这样一个应用场景,创建 GPer 类:
package com.alibaba.design.observerpattern.gperadvice; import java.util.Observable; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:42 */ public class GPer extends Observable { private String name = "GPer生态圈"; private static GPer gper = null; private GPer(){} public static GPer getInstance(){ if(null == gper){ gper = new GPer(); } return gper; } public String getName() { return name; } public void publishQuestion(Question question){ System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。"); setChanged(); notifyObservers(question); } }
创建问题 Question 类:
package com.alibaba.design.observerpattern.gperadvice; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:43 */ public class Question { private String userName; private String content; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
创建老师 Teacher 类:
package com.alibaba.design.observerpattern.gperadvice; import java.util.Observable; import java.util.Observer; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:43 */ public class Teacher implements Observer { private String name; public Teacher(String name){ this.name = name; } @Override public void update(Observable o, Object arg) { GPer gper = (GPer)o; Question question = (Question)arg; System.out.println("==============================="); System.out.println(name + "老师,你好!\n" + "您收到了一个来自“" + gper.getName() + "”的提问,希望您解答,问题内容如下:\n" + question.getContent() + "\n" + "提问者:" + question.getUserName()); } }
客户端测试代码
package com.alibaba.design.observerpattern.gperadvice; import java.util.Observer; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:44 */ public class ObserverTest { public static void main(String[] args) { GPer gper = GPer.getInstance(); Teacher tom = new Teacher("Tom"); Teacher mic = new Teacher("Jerry"); //这为没有@Tom老师 Question question = new Question(); question.setUserName("小明"); question.setContent("观察者设计模式适用于哪些场景?"); gper.addObserver(tom); gper.addObserver(mic); gper.publishQuestion(question); } }
(4) 模式在源码中的体现
来看一下 Spring 中的 ContextLoaderListener 实现了 ServletContextListener 接口,ServletContextListener 接口又继承了 EventListener,在 JDK 中 EventListener 有非常广泛的应用。我们可以看一下源代码,ContextLoaderListener:
package org.springframework.web.context; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } @override public void contextInitialized(ServletContextEvent event) { this.initWebApplicationContext(event.getServletContext()); } @override public void contextDestroyed(ServletContextEvent event) { this.closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
ServletContextListener
package javax.servlet; import java.util.EventListener; public interface ServletContextListener extends EventListener { public void contextInitialized(ServletContextEvent sce); public void contextDestroyed(ServletContextEvent sce); }
EventListener
package java.util; public interface EventListener { }
(5)基于 Guava API 轻松落地观察者模式
Guava是一种基于开源的Java库,Google Guava源于2007年的"Google Collections Library"。这个库是为了方便编码,并减少编码错误。这个库用于提供集合,缓存,支持原语句,并发性,常见注解,字符串处理,I/O和验证的实用方法。
在这里,我还推荐给大家一个实现观察者模式非常好用的框架。API 使用也非常简单,举
个例子,先引入jar包:guava-20.jar
这里建议去maven下载这个
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>20.0</version> </dependency>
创建侦听事件 GuavaEvent:
package com.alibaba.design.observerpattern.guava; import com.google.common.eventbus.Subscribe; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:59 */ public class GuavaEvent { @Subscribe public void subscribe(String str){ System.out.println("执行subscribe方法,传入的参数是:" + str); } }
客户端测试代码:
package com.alibaba.design.observerpattern.guava; import com.google.common.eventbus.EventBus; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:59 */ public class GuavaEventTest { public static void main(String[] args) { //消息总线 EventBus eventBus = new EventBus(); GuavaEvent guavaEvent = new GuavaEvent(); eventBus.register(guavaEvent); eventBus.post("Tom"); } }
(6) 观察者模式的优缺点
- 优点:
1、观察者和被观察者之间建立了一个抽象的耦合。
2、观察者模式支持广播通信。 - 缺点:
1、观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。
2、使用要得当,要避免循环调用。
2.6 中介者模式
(1)概念
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
图片来源:https://blog.csdn.net/ZixiangLi/article/details/86228684
(2)适用场景
1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。
2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
注意事项:不应当在职责混乱的时候使用。
**主要解决:**对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
**何时使用:**多个类相互耦合,形成了网状结构。
**如何解决:**将上述网状结构分离为星型结构。
**关键代码:**对象 Colleague 之间的通信封装到一个类中单独处理。
(3)代码示例
我们通过聊天室实例来演示中介者模式。实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。我们将创建两个类 ChatRoom 和 User。User 对象使用 ChatRoom 方法来分享他们的消息。
MediatorPatternDemo,我们的演示类使用 User 对象来显示他们之间的通信。
创建中介类。
package com.alibaba.design.mediatorpattern; import java.util.Date; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-21:50 */ public class ChatRoom { public static void showMessage(User user, String message){ System.out.println(new Date().toString() + " [" + user.getName() +"] : " + message); } }
创建 user 类。
package com.alibaba.design.mediatorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-21:51 */ 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){ ChatRoom.showMessage(this,message); } }
使用 User 对象来显示他们之间的通信。
package com.alibaba.design.mediatorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-21:53 */ public class MediatorPatternDemo { public static void main(String[] args) { User robert = new User("Robert"); User john = new User("John"); robert.sendMessage("Hi! John!"); john.sendMessage("Hello! Robert!"); } }
(4)该模式在源码中的体现
在java.util.Timer中
timer是一个中介者,它持有新创建到TimerTask的引用,timer负责执行TimerTask的定时任务。不同的TimerTask只知道自己的定时任务,而不清楚其他的TimerTask任务,但是都认识Timer这个中间者对象
在线程方面也用到了这个模式
java.util.concurrent.Executor#execute(Runnable command)
java.util.concurrent.ExecutorService#submit(Runnable command)
exec是一个中介者,它持有Runnable的引用,exec是一个异步任务执行者,它负责执行Runnable任务。
Executor exec = Executors.newFixedThreadPool(100); exec.execute(new Runnable() { @Override public void run() { System.out.println("nice to meet u"); } }); exec.execute(new Runnable() { @Override public void run() { System.out.println("nice to meet u too"); } });
(5)中介者模式的优缺点
- 优点:
1、降低了类的复杂度,将一对多转化成了一对一。
2、各个类之间的解耦。
3、符合迪米特原则。 - 缺点:
中介者会庞大,变得复杂难以维护。