文章摘要(AI生成)
迭代器模式定义提供一种方法可以顺序访问集合中的各个元素,又不需要暴露该集合的内部表示角色lterator(迭代器):该角色负责定义按顺序逐个遍历元素的接口(API)。Concretelterator(具体的迭代器):该角色负责实现Iterator角色所定义的接口(API)。Aggregate(集合)
迭代器模式
定义
提供一种方法可以顺序访问集合中的各个元素,又不需要暴露该集合的内部表示
角色
- lterator(迭代器):该角色负责定义按顺序逐个遍历元素的接口(API)。
- Concretelterator(具体的迭代器):该角色负责实现Iterator角色所定义的接口(API)。
- Aggregate(集合):该角色负责定义创建Iterator角色的接口(API)。这个接口(API)是一个方法,会创建出“按顺序访问保存在我内部元素的人”。
- ConcreteAggregate(具体的集合):该角色负责实现Aggregate角色所定义的接口(API)。它会创建出具体的Iterator角色,即ConcreteIterator角色。
应用场景
从JDK 1.2版本开始增加java.util.Iterator这个接口,并逐步把Iterator应用到各个聚集类 (Collection)中,我们来看JDK 1.5的API帮助文件,你会看到有一个叫java.util.Iterable的接 口,看看有多少个接口继承了它: BeanContext,BeanContextServices,BlockingQueue,Collection,List,Queue,Set,SortedSet, 再看看有它多少个实现类: AbstractCollection,AbstractList,AbstractQueue,AbstractSequentialList,AbstractSet,ArrayBlockingQueue,ArrayList,AttributeList,BeanContextServicesSupport,BeanContextSupport,ConcurrentLinkedQueue,CopyOnWriteArrayList,CopyOnWriteArraySet,DelayQueue,EnumSet,HashSet,JobStateReasons,LinkedBlockingQueue,LinkedHashSet,LinkedList,PriorityBlockingQueue,PriorityQueue,RoleList,RoleUnresolvedList,Stack,SynchronousQueue,TreeSet,Vector, 基本上我们经常使用的类都在这个表中了,也正是因为Java把迭代器模式已经融入到基本 API中了,我们才能如此轻松、便捷。
最佳实践
尽量不要自己写迭代器模式,使用Java提供的Iterator一 般就能满足需求
spring实现
实现一个程序,可以将书放到书架中,并将书的名字按顺序显示出来
迭代器
public interface Iterator {
boolean hasNext();
Object next();
}
具体迭代器
@Component
public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;
private int index;
public void initBookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
if (index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
@Override
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
抽象集合
public interface Aggregate {
Iterator getIterator();
}
具体集合
@Component
public class BookShelf implements Aggregate {
private ArrayList<Book> books;
private int last = 0;
@Resource
private BookShelfIterator bookShelfIterator;
public void initBookShelf(int maxsize) {
this.books = new ArrayList<Book>(maxsize);
}
public Book getBookAt(int index){
return books.get(index);
}
public void appendBook(Book book){
this.books.add(book);
last++;
}
public int getLength(){
return last;
}
/**
* 创建迭代器
* @return 书架迭代器
*/
public Iterator getIterator(){
bookShelfIterator.initBookShelfIterator(this);
return bookShelfIterator;
}
}
集合元素
@Component
@Data
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class Book {
private String name;
}
元素生成器
@Component
public abstract class BookGenerator {
@Lookup
protected abstract Book book();
public Book genBooK(String name){
Book book = book();
book.setName(name);
return book;
}
}
测试
@RestController
public class IteratorController {
@Resource
private BookShelf bookShelf;
@Resource
private BookGenerator bookGenerator;
@GetMapping("/iterator")
public String iterator(){
bookShelf.initBookShelf(4);
bookShelf.appendBook(bookGenerator.genBooK("Around the world"));
bookShelf.appendBook(bookGenerator.genBooK("hello world"));
bookShelf.appendBook(bookGenerator.genBooK("hello kitty"));
bookShelf.appendBook(bookGenerator.genBooK("little pig peqi"));
Iterator it = bookShelf.getIterator();
String result = "";
while (it.hasNext()) {
Book book = (Book) it.next();
result += "书名:" + (book.getName()) +"\n";
}
return result;
}
}
测试结果为:
http://localhost:8080/iterator
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 93
Date: Sun, 28 Aug 2022 07:06:20 GMT
Keep-Alive: timeout=60
Connection: keep-alive
书名:Around the world
书名:hello world
书名:hello kitty
书名:little pig peqi
中介者模式
定义
用一个中介对象封装一系列的对象 交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它 们之间的交互。
优缺点
优点
中介者模式的优点就是减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖, 同事类只依赖中介者,减少了依赖,当然同时也降低了类间的耦合。
缺点
中介者模式的缺点就是中介者会膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。
使用场景
中介者模式简单,但是简单不代表容易使用,很容易被误用。在面向对象的编程中,对 象和对象之间必然会有依赖关系,如果某个类和其他类没有任何相互依赖的关系,那这个类 就是一个“孤岛”,在项目中就没有存在的必要了!就像是某个人如果永远独立生活,与任何 人都没有关系,那这个人基本上就算是野人了——排除在人类这个定义之外。 类之间的依赖关系是必然存在的,一个类依赖多个类的情况也是存在的,存在即合理, 那是否可以说只要有多个依赖关系就考虑使用中介者模式呢?答案是否定的。中介者模式未 必能帮你把原本凌乱的逻辑整理得清清楚楚,而且中介者模式也是有缺点的,这个缺点在使 用不当时会被放大,比如原本就简单的几个对象依赖关系,如果为了使用模式而加入了中介 者,必然导致中介者的逻辑复杂化,因此中介者模式的使用需要“量力而行”!中介者模式适 用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构。在 这种情况下一定要考虑使用中介者模式,这有利于把蜘蛛网梳理为星型结构,使原本复杂混 乱的关系变得清晰简单
大家都应该使用过Struts,MVC框架,其中的C(Controller)就是一个中介者,叫做前端 控制器(Front Controller),它的作用就是把M(Model,业务逻辑)和V(View,视图)隔离开, 协调M和V协同工作,把M运行的结果和V代表的视图融合成一个前端可以展示的页面,减少 M和V的依赖关系。MVC框架已经成为一个非常流行、成熟的开发框架,这也是中介者模式 的优点的一个体现。
最佳实践
中介者模式是一个非常好的封装模式,也是一个很容易被滥用的模式,一个对象依赖几 个对象是再正常不过的事情,但是纯理论家就会要求使用中介者模式来封装这种依赖关系, 这是非常危险的!使用中介模式就必然会带来中介者的膨胀问题,这在一个项目中是很不恰 当的。大家可以在如下的情况下尝试使用中介者模式:
- N个对象之间产生了相互的依赖关系(N>2)。
- 多个对象有依赖关系,但是依赖的行为尚不确定或者有发生改变的可能,在这种情况 下一般建议采用中介者模式,降低变更引起的风险扩散。
- 产品开发。一个明显的例子就是MVC框架,把中介者模式应用到产品中,可以提升产 品的性能和扩展性,但是对于项目开发就未必,因为项目是以交付投产为目标,而产品则是 以稳定、高效、扩展为宗旨
角色
- Mediator (仲裁者、中介者):Mediator角色负责定义与Colleague角色进行通信和做出决定的接口(API)。
- ConcreteMediator(具体的仲裁者、中介者):ConcreteMediator角色负责实现Mediator 角色的接口(API),负责实际做出决定。
- Colleague (同事):Colleague角色负责定义与Mediator角色进行通信的接口(API)
- ConcreteColleague(具体的同事):ConcreteColleague角色负责实现Colleague角色的接口(API )。
spring实现
我们通过以下程序来实现由中介者发送消息给同事,并由同事接受消息。
同事角色
定义和中介者角色进行通信的接口
public interface Colleague {
void setMediator(Mediator mediator);
String receiveMsg(String msg);
void send(String msg);
}
具体同事
定义学生和老师来接收由中介者发送的消息
@Component
@Slf4j
public class StudentColleague implements Colleague {
private Mediator mediator;
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
public void send(String message) {
this.mediator.sendChanged(message, this);
}
@Override
public String receiveMsg(String msg) {
log.info("学生收到消息:" + msg);
return msg;
}
}
@Component
@Slf4j
public class TeacherColleague implements Colleague {
private Mediator mediator;
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public String receiveMsg(String msg) {
log.info("老师收到消息:" + msg);
return msg;
}
public void send(String message) {
mediator.sendChanged(message, this);
}
}
中介者
定义与同事进行通信的接口
public abstract class Mediator {
/**
* 定义一个抽象的发送消息方法,得到同事对象和发送信息
* @param message
* @param colleague
*/
public abstract String sendChanged(String message, Colleague colleague);
}
具体中介者
负责判断发给哪个同事
@Component
public class ConcreteMediator extends Mediator {
@Setter
private TeacherColleague teacherColleague;
@Setter
private StudentColleague studentColleague;
@Override
public String sendChanged(String message, Colleague colleague) {
if (colleague == teacherColleague) {
return teacherColleague.receiveMsg(message);
} else {
return studentColleague.receiveMsg(message);
}
}
}
测试
@RestController
public class MediatorController {
@Resource
private ConcreteMediator mediator;
@Resource
private TeacherColleague teacherColleague;
@Resource
private StudentColleague studentColleague;
@GetMapping("/mediator")
public String mediator(String item){
teacherColleague.setMediator(mediator);
studentColleague.setMediator(mediator);
mediator.setTeacherColleague(teacherColleague);
mediator.setStudentColleague(studentColleague);
teacherColleague.send("早上好啊!");
studentColleague.send("早安!");
return mediator.toString();
}
}
测试结果如下
2022-08-28 15:15:16.039 INFO 11304 --- [nio-8080-exec-4] c.e.d.Mediator.TeacherColleague : 老师收到消息:早上好啊!
2022-08-28 15:15:16.040 INFO 11304 --- [nio-8080-exec-4] c.e.d.Mediator.StudentColleague : 学生收到消息:早安!
备忘录模式
定义
在不破坏封装性的前提下,捕获一个对象的内部状 态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
使用场景
- 需要保存和恢复数据的相关状态场景。
- 提供一个可回滚(rollback)的操作;比如Word中的CTRL+Z组合键,IE浏览器中的后 退按钮,文件管理器上的backspace键等。
- 需要监控的副本场景中。例如要监控一个对象的属性,但是监控又不应该作为系统的 主业务来调用,它只是边缘应用,即使出现监控不准、错误报警也影响不大,因此一般的做 法是备份一个主线程中的对象,然后由分析程序来分析。
- 数据库连接的事务管理就是用的备忘录模式
最佳实践
备忘录模式是我们设计上“月光宝盒”,可以让我们回到需要的年代;是程序数据的“后 悔药”,吃了它就可以返回上一个状态;是设计人员的定心丸,确保即使在最坏的情况下也 能获得最近的对象状态。如果大家看懂了的话,请各位在设计的时候就不要使用数据库的临 时表作为缓存备份数据了,虽然是一个简单的办法,但是它加大了数据库操作的频繁度,把 压力下放到数据库了,最好的解决办法就是使用备忘录模式。
角色
-
Originator(生成者):Originator角色会在保存自己的最新状态时生成Memento角色。当把以前保存的Memento角色传递给Originator角色时,它会将自己恢复至生成该Memento角色时的状态。在示例程序中,由Gamer类扮演此角色。
-
Memento (备忘录):Memento角色会将Originator 角色的内部信息整合在一起。在 Memento角色中虽然保存了Originator 角色的信息,但它不会向外部公开这些信息。
Memento角色有以下两种接口(API )。
- wide interface—宽接口(API):Memento角色提供的“宽接口(API)”是指所有用于获取恢复对象状态信息的方法的集合。由于宽接口(API)会暴露所有Memento角色的内部信息,因此能够使用宽接口(API)的只有Originator角色。
- narrow interface—窄接口(API):Memento角色为外部的Caretaker 角色提供了“窄接口(API)”。可以通过窄接口(API )获取的Memento角色的内部信息非常有限,因此可以有效地防止信息泄露。
通过对外提供以上两种接口(API),可以有效地防止对象的封装性被破坏。
Originator角色和 Memento角色之间有着非常紧密的联系。
-
Caretaker(负责人) :当Caretaker 角色想要保存当前的Originator角色的状态时,会通知Originator角色。Originator角色在接收到通知后会生成Memento角色的实例并将其返回给Caretaker角色。由于以后可能会用Memento实例来将Originator恢复至原来的状态,因此Caretaker角色会一直保存Memento实例。
不过,Caretaker角色只能使用Memento角色两种接口(API)中的窄接口(API),也就是说它无法访问Memento角色内部的所有信息。它只是将Originator角色生成的Memento角色当作一个黑盒子保存起来。
虽然Originator角色和 Memento角色之间是强关联关系,但Caretaker角色和Memento角色之间是弱关联关系。Memento角色对Caretaker角色隐藏了自身的内部信息。
spring实现
下面我们来看一段使用了Memento模式的示例程序。这是一个收集水果和获取金钱数的掷骰子游戏,游戏规则很简单,具体如下:
- 游戏是自动进行的
- 游戏的主人公通过掷骰子来决定下一个状态
- 当骰子点数为1的时候,主人公的金钱会增加·
- 当骰子点数为2的时候,主人公的金钱会减少·
- 当骰子点数为6的时候,主人公会得到水果·
- 主人公没有钱时游戏就会结束
在程序中,如果金钱增加,为了方便将来恢复状态,我们会生成Memento类的实例,将现在的状态保存起来。所保存的数据为当前持有的金钱和水果。如果不断掷出了会导致金钱减少的点数,为了防止金钱变为О而结束游戏,我们会使用Memento的实例将游戏恢复至之前的状态。
备忘录
将生成的内部信息整合到一起,用来表示游戏状态
@Component
public class Memento {
int money;
ArrayList<String> fruits;
public int getMoney() {
return money;
}
public void initMemento(int money) {
this.money = money;
this.fruits = new ArrayList<String>();
}
void addFruit(String fruit){
fruits.add(fruit);
}
public List<String> getFruits() {
return (List<String>)fruits.clone();
}
}
备忘录生成器
@Component
public abstract class MementoGenerator {
@Lookup
public abstract Memento memento();
public Memento genMemento(int money){
Memento memento = memento();
memento.initMemento(money);
return memento;
}
}
生成者
用来表示游戏主人公,会生成对应备忘录的实例
@Component
@Slf4j
public class Gamer {
@Getter
@Setter
private int money;
private List<String> fruits = new ArrayList<String>();
private Random random = new Random();
@Resource
private MementoGenerator mementoGenerator;
private static String[] fruitsname = { "苹果", "葡萄", "香蕉", "橘子", };
public void bet(){
int dice = random.nextInt(6)+1;
if(dice==1){
money+=100;
log.info("所持金钱增加了");
}else if(dice==2){
money/=2;
log.info("所持金钱减半了");
}else if(dice==6){
String f = getFruit();
log.info("获得了水果("+f+")。");
fruits.add(f);
}else{
log.info("什么都没发生");
}
}
private String getFruit() {
// TODO Auto-generated method stub
String prefix = "";
if(random.nextBoolean()){
prefix="好吃的";
}
return prefix+fruitsname[random.nextInt(fruitsname.length)];
}
public Memento createMemento(){
Memento m = mementoGenerator.genMemento(money);
Iterator<String> it = fruits.iterator();
while(it.hasNext()){
String f = it.next();
if(f.startsWith("好吃的")){
m.addFruit(f);
}
}
return m;
}
public void restoreMemento(Memento memento){
this.money = memento.money;
this.fruits = memento.getFruits();
}
@Override
public String toString() {
return "Gamer [money=" + money + ", fruits=" + fruits + ", random="
+ random + "]";
}
}
测试
@RestController
@Slf4j
public class MementoController {
@Resource
private Gamer gamer;
@GetMapping("/memento")
public String memento(){
gamer.setMoney(1000);
Memento memento = gamer.createMemento();
for(int i=0;i<100;i++){
log.info("==== "+i);
log.info("当前状态:"+gamer);
gamer.bet();
log.info("所持金钱为"+gamer.getMoney()+"元");
if(gamer.getMoney()>memento.getMoney()){
log.info("(所持金钱增加了许多,因此保持游戏当前的状态)");
memento= gamer.createMemento();
}else if(gamer.getMoney()<memento.getMoney()/2){
log.info("(所持金钱减少了许多,因此将游戏恢复至以前的状态)");
gamer.restoreMemento(memento);
}
}
return gamer.toString();
}
}
输出结果为:
2022-08-28 15:29:51.566 INFO 11304 --- [nio-8080-exec-6] c.e.d.Memento.MementoController : ==== 0
2022-08-28 15:29:51.566 INFO 11304 --- [nio-8080-exec-6] c.e.d.Memento.MementoController : 当前状态:Gamer [money=1000, fruits=[], random=java.util.Random@4d391309]
2022-08-28 15:29:51.566 INFO 11304 --- [nio-8080-exec-6] c.e.designpattern.Memento.game.Gamer : 什么都没发生
...
...
...
2022-08-28 15:29:51.573 INFO 11304 --- [nio-8080-exec-6] c.e.designpattern.Memento.game.Gamer : 所持金钱减半了
2022-08-28 15:29:51.573 INFO 11304 --- [nio-8080-exec-6] c.e.d.Memento.MementoController : 所持金钱为900元
2022-08-28 15:29:51.573 INFO 11304 --- [nio-8080-exec-6] c.e.d.Memento.MementoController : ==== 99
2022-08-28 15:29:51.573 INFO 11304 --- [nio-8080-exec-6] c.e.d.Memento.MementoController : 当前状态:Gamer [money=900, fruits=[好吃的苹果], random=java.util.Random@4d391309]
2022-08-28 15:29:51.573 INFO 11304 --- [nio-8080-exec-6] c.e.designpattern.Memento.game.Gamer : 什么都没发生
2022-08-28 15:29:51.573 INFO 11304 --- [nio-8080-exec-6] c.e.d.Memento.MementoController : 所持金钱为900元
评论区