行业网站建设哪家好,网站备案密码通管局,厦门做网站优化价格,wordpress安装选择协议目录1 迭代器模式1.1 目标1.2 内容定位1.3 迭代器模式1.4 迭代器模式的应用场景1.5 手写字定义的送代器1.6 迭代器模式在源码中的体现1.7 迭代器模式的优缺点2 命令模式2.1 定义2.2 命令模式的应用场景2.3 命令模式在业务场景中的应用2.4 命令模式在源码中的体现2.5 命令模式的…
目录1 迭代器模式1.1 目标1.2 内容定位1.3 迭代器模式1.4 迭代器模式的应用场景1.5 手写字定义的送代器1.6 迭代器模式在源码中的体现1.7 迭代器模式的优缺点2 命令模式2.1 定义2.2 命令模式的应用场景2.3 命令模式在业务场景中的应用2.4 命令模式在源码中的体现2.5 命令模式的优缺点1 迭代器模式
1.1 目标
1、 了解迭代器模式和命令的应用场景。
2、 自己手写迭代器
3、 掌握迭代器模式和命令模式在源码中的应用知其所以然。
1.2 内容定位
听说过迭代器模式和命令模式但并不知其所以然的人群。
1.3 迭代器模式
迭代器模式( Iterator Pattern ) 又称为游标模式(Cursor Pattern), 它提供一种顺序访问集合/ 容器对象元素的方法而又无须暴露集合内部表示。迭代器模式可以为不同的容器提供一致的 遍历行为而不用关心容器内容元素组成结构属于行为型模式。 原文 : Provide a way to access the elements of an aggregate object sequentially without exposing its under lying representation. 解释提供一种顺序访问集合/容器对象元素的方法而又无须暴露集合内部表示。 迭代器模式的本质是抽离集合对象迭代行为到迭代器中提供一致访问接口。
1.4 迭代器模式的应用场景
迭代器模式在我们生活中应用的得也比较广泛比如物流系统中的传送带不管传送的是什 么物品都被打包成一个一个的箱子并且有一个统一的二维码。这样我们不需要关心箱子里面 是啥我们在分发时只需要一个一个检查发送的目的地即可。再比如我们平时乘坐交通工具 都是统一刷卡或者刷脸进站而不需要关心是男性还是女性、是残疾人还是正常人等个性化的信息。 我们把多个对象聚在一起形成的总体称之为集合(Aggregate), 集合对象是能够包容一组对 象的容器对象。不同的集合其内部元素的聚合结构可能不同而迭代器模式屏蔽了内部元素获 取细节为外部提供一致的元素访问行为解耦了元素迭代与集合对象间的耦合并且通过提 供不同的迭代器可以为同个集合对象提供不同顺序的元素访问行为扩展了集合对象元素迭 代功能符合开闭原则。迭代器模式适用于以下场景
1、 访问一个集合对象的内容而无需暴露它的内部表示
2、 为遍历不同的集合结构提供一个统一的访问接口。
首先来看下迭代器模式的通用UML类图 从 UML类图中,我们可以看到迭代器模式主要包含三种角色:
抽象迭代器( Iterator) : 抽象迭代器负责定义访问和遍历元素的接口 ;
具体迭代器( Concreteiterator) :提供具体的元素遍历行为
抽象容器(Aggregate ) : 负责定义提供具体迭代器的接口
具体容器(ConcreteAggregate ) :创建具体迭代器。
1.5 手写字定义的送代器
总体来说,迭代器模式还是非常简单的。我们还是以课程为例下面我们自己创建一个课程的集合集合中的每一个元素就是课程对象然后自己手写一个迭代器将每一个课程对象的信息读出来。
首先创建集合元素课程Course类 public class Course {private String name;public Course(String name) {this.name name;}public String getName() {return name;}}然后创建自定义迭代器Iterator接口 public interface IteratorE {E next();boolean hasNext();}然后创建自定义的课程的集合ICourseAggregate接口 public interface ICourseAggregate {void add(Course course);void remove(Course course);IteratorCourse iterator();}然后,分别实现迭代器接口和集合接口创建Iteratorlmpl实现类 public class IteratorImplE implements IteratorE {private ListE list;private int cursor;private E element;public IteratorImpl(ListE list) {this.list list;}public E next() {System.out.print(当前位置 cursor : );element list.get(cursor);cursor ;return element;}public boolean hasNext() {if(cursor list.size() - 1){return false;}return true;}}创建课程集合CourseAggregatelmpI 实现类 public class CourseAggregateImpl implements ICourseAggregate {private List courseList;public CourseAggregateImpl() {this.courseList new ArrayList();}public void add(Course course) {courseList.add(course);}public void remove(Course course) {courseList.remove(course);}public IteratorCourse iterator() {return new IteratorImplCourse(courseList);}}然后,编写客户端代码 public class Test {public static void main(String[] args) {Course java new Course(Java架构);Course javaBase new Course(Java基础);Course design new Course(设计模式);Course ai new Course(人工智能);ICourseAggregate aggregate new CourseAggregateImpl();aggregate.add(java);aggregate.add(javaBase);aggregate.add(design);aggregate.add(ai);System.out.println(课程列表);printCourse(aggregate);aggregate.remove(ai);System.out.println(删除操作之后的课程列表);printCourse(aggregate);}private static void printCourse(ICourseAggregate aggregate) {IteratorCourse i aggregate.iterator();while (i.hasNext()){Course course i.next();System.out.println(《 course.getName() 》);}}}运行结果如下: 看到这里小伙伴们肯定会有一种似曾相识的感觉让人不禁想起我们每天都在用的JDK 自带的结合迭代器。下面我们就来看看源码中是如何运用迭代器的。
1.6 迭代器模式在源码中的体现
先来看JDK中大家非常熟悉的Iterator源码 public interface IteratorE {boolean hasNext();E next();default void remove() {throw new UnsupportedOperationException(remove);}default void forEachRemaining(Consumer? super E action) {Objects.requireNonNull(action);while (hasNext())action.accept(next());}}从上面代码中我们看到两个主要的方法定义hasNext()和 next()方 法 和我们自己写的完 全一致。
另外从上面的代码中,我们看到removeO方法实现似曾相识。其实是在组合模式中我们 见到过。迭代器模式和组合模式两者似乎存在一定的相似性。组合模式解决的是统一树形结 构各层次访问接口迭代器模式解决的是统一各集合对象元素遍历接口。虽然他们的适配场景 不同但核心理念是相通的。
下面接看来看Iterator的实现类其实在我们常用的ArrayList中有一个内部实现类Itr ,它 就实现了 Iterator接口 : public class ArrayListE extends AbstractListEimplements ListE, RandomAccess, Cloneable, java.io.Serializable{private class Itr implements IteratorE {int cursor; // index of next element to returnint lastRet -1; // index of last element returned; -1 if no suchint expectedModCount modCount;public boolean hasNext() {return cursor ! size;}SuppressWarnings(unchecked)public E next() {checkForComodification();int i cursor;if (i size)throw new NoSuchElementException();Object[] elementData ArrayList.this.elementData;if (i elementData.length)throw new ConcurrentModificationException();cursor i 1;return (E) elementData[lastRet i];}...}}其中hasNextO方法和next()方法实现也非常简单我们继续往下看在ArrayList内部还有 几个迭代器对Itr进行了进一步扩展,首先看Listltr :
private class ListItr extends Itr implements ListIteratorE {ListItr(int index) {super();cursor index;}public boolean hasPrevious() {return cursor ! 0;}public int nextIndex() {return cursor;}public int previousIndex() {return cursor - 1;}...
}它增加了 hasPreviousQ方法是否还有上一个等这样的判断。另外还有SubList对子集合的迭代处理。
当然,迭代器模式在MyBatis中也是必不可少的来看一个Defaultcursor类
public class DefaultCursorT implements CursorT {...private final CursorIterator cursorIterator new CursorIterator();
}首先它实现了 Cursor接 口 而且定义了一个成员变量cursoriterator , 我继续查看 Cursoriterator的源代码发现, 它 是 Defaultcursor的一个内部类并且实现了 JDK中的 Iterater 接口。
1.7 迭代器模式的优缺点
优点
1、 多态迭代为不同的聚合结构提供一致的遍历接口即一个迭代接口可以访问不同的集合对 象
2、 简化集合对象接口 迭代器模式将集合对象本身应该提供的元素迭代接口抽取到了迭代器中 使集合对象无须关心具体迭代行为
3、 元素迭代功能多样化每个集合对象都可以提供一个或多个不同的迭代器使的同种元素聚合结构可以有不同的迭代行为
4、解耦迭代与集合迭代器模式 封装了具体的迭代算法迭代算法的变化不会影响到集合对象的架构。
缺点
1、对于比较简单的遍历(像数组或者有序列表) 使用迭代器方式遍历较为繁琐。 在日常开发当中我们几乎不会自己写迭代器。除非我们需要定制一个自己实现的数据结构 对应的迭代器否则开源框架提供给我们的API完全够用。
2 命令模式
2.1 定义
命令模式Command Pattern 是对命令的封装每一个命令都是一个操作请求的一方发出请求要求执行一个操作接收的一方收到请求并执行操作。命令模式解耦了请求方和接收方请求方只需请求执行命令不用关心命令是怎样被接收怎样被操作以及是否被执行…等。 命令模式属于行为型模式。 原 文 Encapsulate a request as an object, there by letting you parameterize clients with different requests, queue or log requests,and support undoable operations. 解释将一个请求封装成一个对象从而让你使用不同的请求把客户端参数化对请求排队或者记录请求日志可以提供命令的撤销和恢复功能。 在软件系统中行为请求者与行为实现者通常是一种紧耦合关系因为这样的实现简单明了。 但紧耦合关系缺乏扩展性在某些场合中当需要为行为进行记录撤销或重做等处理时,只 能修改源码。而命令模式通过为请求与实现间引入一个抽象命令接口解耦了请求与实现并 且中间件是抽象的它可以有不同的子类实现因此其具备扩展性。所以命令模式的本质是 解耦命令请求与处理。
2.2 命令模式的应用场景
当系统的某项操作具备命令语义时且命令实现不稳定变化那么可以通过命令模式解耦请求与实现利用抽象命令接口使请求方代码架构稳定封装接收方具体命令实现细节。接收方与抽象命令接口呈现弱耦合内部方法无需一致具备良好的扩展性。
命令模式适用于 以下应用场景:
1、现实语义中具备命令的操作如命令菜单shell命令…
2、请求调用者和请求的接收者需要解耦使得调用者和接收者不直接交互
3、需要抽象出等待执行的行为比如撤销Undo操作和恢复Redo等操作;
4、需要支持命令宏即命令组合操作。
首先看下命令模式的通用UML类图: 从 UML类图中,我们可以看到命令模式 主要包含四种角色
接收者角色Receiver:该类负责具体实施或执行一个请求;
命令角色Command :定义需要执行的所有命令行为
具体命令角色Concrete Command 该类内部维护一个接收者Receiver 在其execute 方法中调用Receiver的相关方法
请求者角色Invoker:接收客户端的命令,并执行命令。
从命令模式的UML类图中,其实可以很清晰地看出Command的出现就是作为Receiver 和 Invoker的中间件解耦了彼此。而之所以引入Command中间件我觉得是以下两方面原因
解耦请求与实现即解耦了 Invoker和 Receiver , 因为在UML类图中Invoker是一个具 体的实现等待接收客户端传入命令即 Invoker与客户端耦合,Invoker处于业务逻辑区域 应当是一个稳定的结构。而 Receiver是属于业务功能模块 是经常变动的 如果没有Command z 则 Invoker紧耦合Receiver , 一个稳定的结构依赖了一个不稳定的结构,就会导致整个结构都不稳定了。这也就是Command引入的原因不仅仅是解耦请求与实现同时稳定 Invoker 依赖稳 定 Command 结构还是稳定的。
扩展性增强扩展性体现在两个方面
1、 Receiver属于底层细节,可以通过更换不同的Receiver达到不同的细节实现
2、 Command接口本身就是抽象的,本身就具备扩展性而且由于命令对象本身就具备抽 象 如果结合装饰器模式功能扩展简直如鱼得水。 注在一个系统中不同的命令对应不同的请求也就是说无法把请求抽象化因此命令模式中的Receiver是具体实 现但是如果在某一个模块中可以对Receiver进行抽象其实这就变相使用到了桥接模式Command类具备两个变 化的维度Command和 Receiver, 这样子的扩展性会更加优秀。 举个生活中的例子相信80后的小伙伴应该都经历过普及黑白电视机的那个年代。黑白电 视机要换台那简直不容易需要人跑上前去用力掰动电视机上那个切换频道的旋钮一 顿 〃啪 啪啪〃折腾下来才能完成一次换台。如今时代好了我们只需躺沙发上按一下遥控器就完成了 换台。这就是用到了命令模式,将换台命令和换台处理进行了分离。
另外,就是餐厅的点菜单一般是后厨先把所有的原材料组合配置好了客户用餐前只需要 点菜即可将需求和处理进行了解耦。
2.3 命令模式在业务场景中的应用
假如我们自己开发一个播放器播放器有播放功能、有拖动进度条功能、停止播放功能、暂 停功能我们自己去操作播放器的时候并不是直接调用播放器的方法,而是通过一个控制条去传达指令给播放器内核,那么具体传达什么指令会被封装为一个一个的按钮。那么每个按钮 的就相当于是对一条命令的封装。用控制条实现了用户发送指令与播放器内核接收指令的解耦。
下面来看代码首先创建播放器内核GPlayer类:
public class GPlayer {public void play(){System.out.println(正常播放);}public void speed(){System.out.println(拖动进度条);}public void stop(){System.out.println(停止播放);}public void pause(){System.out.println(暂停播放);}
}创建命令接口 IAction类:
public interface IAction {void execute();
}然后分别创建操作播放器可以接受的指令,播放指令PlayAction类
public class PlayAction implements IAction {private GPlayer gplayer;public PlayAction(GPlayer gplayer) {this.gplayer gplayer;}public void execute() {gplayer.play();}
}暂停指令PauseAction类
public class PauseAction implements IAction {private GPlayer gplayer;public PauseAction(GPlayer gplayer) {this.gplayer gplayer;}public void execute() {gplayer.pause();}
}拖动进度条指令SpeedAction类
public class SpeedAction implements IAction {private GPlayer gplayer;public SpeedAction(GPlayer gplayer) {this.gplayer gplayer;}public void execute() {gplayer.speed();}
}停止播放指令StopAction类
public class StopAction implements IAction {private GPlayer gplayer;public StopAction(GPlayer gplayer) {this.gplayer gplayer;}public void execute() {gplayer.stop();}
}最后 创建控制条Controller类
public class Controller {private ListIAction actions new ArrayListIAction();public void addAction(IAction action){actions.add(action);}public void execute(IAction action){action.execute();}public void executes(){for (IAction action:actions) {action.execute();}actions.clear();}
}从上面代码来看控制条可以执行单条命令也可以批量执行多条命令。下面来看客户端测试代码
public class Test {public static void main(String[] args) {GPlayer player new GPlayer();Controller controller new Controller();controller.execute(new PlayAction(player));controller.addAction(new PauseAction(player));controller.addAction(new PlayAction(player));controller.addAction(new StopAction(player));controller.addAction(new SpeedAction(player));controller.executes();}
}运行效果如下
正常播放
暂停播放
正常播放
停止播放
拖动进度条由于控制条已经与播放器内核解耦了以后如果想扩展新命令只需增加命令即可控制条 的结构无需改动。
2.4 命令模式在源码中的体现
首先来看JDK中的Runnable接口 实际上Runnable就相当于是命令的抽象只要是实现 了 Runnable接口的类都被认为是一个线程。
public interface Runnable { public abstract void run();
}实际上调用线程的start()方法之后就有资格去抢CPU资源而不需要我们自己编写获得 CPU资源的逻辑。而线程抢到CPU资源后,就会执行run()方法中的内容用 Runnable接口 把用户请求和CPU执行进行了解耦。
然 后 ,再看一个大家非常孰悉的junit.framework.Test接口
package junit.framework;
public interface Test { public abstract int countTestCases(); public abstract void run(TestResult result);
}Test接口中有两个方法第一个是countTestCases()方法用来统计当前需要执行的测试用例 总数。第二个是run()方法就是用来执行具体的测试逻辑其参数TestResult是用来返回测试结果的。 实际上我们在平时编写测试用例的时候只需要实现Test接口即便认为就是一个测试用例那 么在执行的时候就会自动识别。实际上我们平时通常做法都是继承TestCase类 我们不妨来看 —下 TestCase的源码
public abstract class TestCase extends Assert implements Test { ...public void run(TestResult result) { result.run(this);}...
}实际上TestCase类它也实现了 Test接口。我们继承TestCase类 ,相当于也实现了 Test 接口自然也就会被扫描成为一个测试用例。
2.5 命令模式的优缺点
优点
1、通过引入中间件抽象接口解耦了命令请求与实现
2、 扩展性良好可以很容易地增加新命令
3、 支持组合命令支持命令队列
4、可以在现有命令的基础上增加额外功能比如日志记录…结合装饰器模式更酸爽。
缺点
1、 具体命令类可能过多
2、 命令模式的结果其实就是接收方的执行结果但是为了以命令的形式进行架构解耦请求与 实现引入了额外类型结构引入了请求方与抽象命令接口增加了理解上的困难不过这 也是设计模式带来的一个通病,抽象必然会引入额外类型抽象肯定比紧密难理解。