百日筑基第二十八天-23种设计模式-举动型总汇
前言
设计模式可以说是对于七大设计原则的实现。
总体来说设计模式分为三大类:
- 创建型模式,共五种:单例模式、简单工厂模式、抽象工厂模式、建造者模式、原型模式。
- 布局型模式,共七种:代理模式、外观模式、享元模式、组合模式、桥接模式、装饰器模式、适配器模式。
- 举动型模式,共十一种:模板方法模式、迭代器模式、访问者模式、观察者模式、命令模式、状态模式、计谋模式、备忘录模式、中介者模式、表明器模式、责任链模式。
模板方法模式
简介
在面向对象步伐设计过程中,步伐员常常会遇到如下情况:设计一个系统时知道算法所需的关键步调,且确定了这些步调的执行顺序,但某些步调的具体实现还未知,大概说某些步调的实现与具体的情况相关。例如:去医院看病一般都要经历以下流程:挂号、列队、就诊、取药等,其中挂号和列队对每个客户都是一样的,可以在父类中实现,但是就诊和取药是因人而异的。可以延迟到子类中实现。我们把这些规定了流程或格式的实例定义成模板,答应使用者根据本身的需求去更新它。
1)模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行此方法的模板。它的子类可以按照需要重写方法实现,但调用将以抽象类中定义的方式进行。
2)简单说,模板方法模式定义了一个操纵中的算法骨架,而将步调延迟到子类中,使得子类可以不改变一个算法的布局,就可以重定义该算法的某些特定步调。
3)这种类型的设计模式属于举动型模式。
模板方式的特点
模板方式的优点:
【1】封装了稳定的部分,扩展可变部分。将稳定部分的算法封装到父类中实现,而把可变部分的算法由各子类实现。便于子类继续扩展。
【2】在父类中提取了公共的部分代码,便于代码复用。
【3】部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
模板方式的缺点:
【1】对每个差别的实现都需要定义一个子类,这个导致类的个数增加,系统更加巨大,设计也更加抽象。
【2】父类中的抽象方法由子类实现,子类执行的效果会影响父类的效果,这导致一种反向的控制布局,提高了代码的阅读难度。
使用场景:
【1】当多个子类具有公用的方法,却执行流程逻辑相同时。
【2】重要的、复杂的方法,可以考虑作为模板方法。
注意事项: 为了防止恶意操纵,一般模板方法上都加有 final 关键字
模板方法模式布局类图
模板方法模式包含以下主要角色:
【1】抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个根本方法构成,如下:
① 抽象方法:在抽象类中申明,由具体子类实现。
② 具体方法:在抽象类中已经实现,再具体子类中可以继续或重写它。
③ 钩子方法:在抽象类中已经实现,例如:用于判定的逻辑方法大概定义一个空方法。子类根据情况要不要重写此方法,此方法为钩子方法。例如下面实例中的 isRecipe() 方法。
【2】具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步调。
模板方式模式案例分析
【1】抽象类(医院看病时,整个流程的抽象类)整个流程属于不可变的,因此我使用了 final 修饰。
- public abstract class AbstractHospital {
- //流程
- public final void procedure() {
- //1、挂号
- this.regiest();
- //2、排队
- this.queue();
- //3、看病
- this.treat();
- //4、取药,有点人不用开药,只是小事
- if(isRecipe()) {
- this.recipe();
- }
- }
- //挂号
- public String regiest() {
- String regiest = "你的订单号为"+((int) (Math.random()*10));
- System.out.println(regiest);
- return regiest;
- }
- //排队
- public void queue() {
- System.out.println("排队中。。。。");
- }
- //看病
- public abstract String treat();
- //取药
- public abstract String recipe();
- //钩子方法
- boolean isRecipe() {
- return true;
- }
- }
复制代码 【2】抽象类的具体实现类(有多个,我们只写一个)
- public class Patient_A extends AbstractHospital{
- @Override
- public String treat() {
- String treat = "胃病";
- System.out.println("你的病为"+treat);
- return treat;
- }
- @Override
- public String recipe() {
- String recipe = "健胃消食片";
- System.out.println("药单:"+recipe);
- return recipe;
- }
- //重写钩子方法
- @Override
- boolean isRecipe() {
- return false;
- }
- }
复制代码 【3】客户端类:创建子类对象,流程调用的是父类公共的流程方法。
- public class Client {
- public static void main(String[] args) {
- //A 病人看病
- Patient_A patient_A = new Patient_A();
- //看病的流程
- patient_A.procedure();
- /**
- * 输出如下:
- * 你的订单号为2
- * 排队中。。。。
- * 你的病为胃病
- * 药单:健胃消食片 ==== 加了钩子程序则不显示
- */
- //B 病人看病,的流程也是一样,只需要实例化B,并调用公共的模板流程即可,提高代码的利用率
- }
- }
复制代码 模板方法模式应用源码分析
模板方法模式在 Spring 框架应用的源码分析:Spring IOC 容器初始化时运用到了模板方法模式;
【1】抽象类: AbstractApplicationContext继续接口(ConfigurableApplicationContext)其中的 refresh 方法就是模板方法,定义了执行的流程,方法中包含钩子方法、子类需要实现的抽象方法(refreshBeanFactory)、父类已实现的方法等等。
- public abstract class AbstractApplicationContext extends DefaultResourceLoader
- implements ConfigurableApplicationContext, DisposableBean {
- ......
- @Override
- public void refresh() throws BeansException, IllegalStateException {
- synchronized (this.startupShutdownMonitor) {
- prepareRefresh();
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- prepareBeanFactory(beanFactory);
- try {
- // 钩子方法,子类自行选择是否重写
- postProcessBeanFactory(beanFactory);
- invokeBeanFactoryPostProcessors(beanFactory);
- registerBeanPostProcessors(beanFactory);
- // 父类已实现
- initMessageSource();
- // 由父类进行实现
- initApplicationEventMulticaster();
- // 钩子方法
- onRefresh();
- registerListeners();
- finishBeanFactoryInitialization(beanFactory);
- // 父类实现
- finishRefresh();
- }
- catch (BeansException ex) {
- destroyBeans();
- cancelRefresh(ex);
- throw ex;
- }
- }
- }
- ......
- }
复制代码 【2】构建 AbstractApplicationContext 类图:
模板方法模式的注意事项和细节
【1】根本思想是:算法只存在于一个地方,也就是父类中,容易修改。需要修改算法时,只要修改父类的模板方法大概已经实现的某些步调,子类就会继续这些修改。
【2】实现了最大化代码复用。父类的模板方法和已实现的某些步调会被子类继续且直接使用。
【3】既同一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的布局稳定,同时由子类提供部分步调的实现。
【4】该模式的不足之处:每一个差别的实现都需要一个子类实现,导致类的个数增多,使得系统更加巨大。
【5】模板方式的使用场景:存在执行一系列步调,且一类产物的此步调根本相同,但其中个别步调的实现细节差别时,通常可以考虑使用模板方法模式。
迭代器模式
在步伐设计中,经常需要访问一个聚合对象中的各个元素,例如:我们使用 list 存储元素,通常的做法是将 list 的创建和遍历放在同一个类中。但这种方法不利于扩展,假如将存储方式更改为数组时,就需要更换迭代方式。违背了 “开闭原则”。“迭代器模式” 能较好的克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与遍历举动,对客户也隐蔽了其内部细节,满足 “单一职责原则” 和 “开闭原则”,如 Java 中的 Collection、List、Set、Map 等都包含迭代器。
迭代器模式布局类图
迭代器模式是通过将聚合对象的遍历举动分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部布局的情况下,让外部代码透明地访问聚合的内部数据。
迭代器模式主要包含以下角色:
【1】抽象聚合(Aggregate)角色:定义了存储、添加、删除聚合对象以及创建迭代对象的接口。
【2】具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
【3】抽象迭代器(Iterator)角色:定义访问和遍历和聚合元素接口,通常包含 hasNext()、next()等方法。
【4】具体迭代器(ConcreteIterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
迭代器模式案例分析
【1】抽象迭代器: 使用 JDK 自带的 Iterator 接口,我们将源码粘贴过来,无需自行实现。子类需要实现 hasNext 和 next 方法
- public interface Iterator<E> {
-
- boolean hasNext();
- //使用泛型 E
- E next();
-
- default void remove() {
- throw new UnsupportedOperationException("remove");
- }
-
- default void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- while (hasNext())
- action.accept(next());
- }
- }
复制代码 【2】具体迭代器: 定义将 List 聚集包装为 Iterator 遍历的对象 ListIterator
- public class ListIterator implements Iterator<Object>{
- //定义一个 List 集合
- List<Phone> list;
-
- //构造器
- public ListIterator(List<Phone> list) {
- this.list = list;
- }
-
- //获取位数
- int index = 0;
- @Override
- public boolean hasNext() {
- if(list != null && list.size()>index) {
- return true;
- }else {
- return false;
- }
- }
-
- @Override
- public Object next() {
- Phone object = list.get(index);
- index+=1;
- return object;
- }
-
- }
复制代码 【3】具体迭代器: 定义将 数组聚集包装为 Iterator 遍历的对象 ArrayIterator
- public class ArrayIterator implements Iterator<Object>{
- //定义 电话数组
- Phone[] phones;
- //下标
- private int index = 0;
- //构造器
- public ArrayIterator(Phone[] phones) {
- this.phones = phones;
- }
-
- @Override
- public boolean hasNext() {
- if(phones[index] != null && phones.length > index) {
- return true;
- }else {
- return false;
- }
- }
-
- @Override
- public Object next() {
- Phone phone = phones[index];
- index+=1;
- return phone;
- }
- }
复制代码 【4】 定义 List 与 数组中存储的对象 Phone。
- public class Phone {
- public String name;
- public String money;
- /**
- * @param name
- * @param money
- */
- public Phone(String name, String money) {
- super();
- this.name = name;
- this.money = money;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getMoney() {
- return money;
- }
- public void setMoney(String money) {
- this.money = money;
- }
- }
复制代码 【5】抽象聚合角色: 手机的抽象接口 IPhone
- public interface IPhone {
- //获取手机名称
- public String getName();
- //创建一个获取 Iterator 实例的方法
- public Iterator<Object> createIterator();
- //增加手机的方法
- public void add(String name,String type);
- }
复制代码 【6】具体聚合角色: 小米手机聚集类,通过 List 进行封装,并创建一个返回 Iterator 的方法,使用迭代的方式遍历。
- public class XiaoMiPhoneImpl implements IPhone{
- //小米使用 list 存储产品
- private List<Phone> xiaoMis = new ArrayList<Phone>();
- //构造器
- public XiaoMiPhoneImpl() {
- add("红米", "1200");
- add("小米6", "2300");
- add("小米7", "3200");
- }
-
- @Override
- public String getName() {
- return "====小米手机====";
- }
- //创建遍历器
- @Override
- public Iterator<Object> createIterator() {
- return new ListIterator(xiaoMis);
- }
- //集合中添加小米产品
- @Override
- public void add(String name, String money) {
- xiaoMis.add(new Phone(name, money));
- }
- }
复制代码 【7】具体聚合角色: 华为手机聚集类,通过 数组 进行封装,并创建一个返回 Iterator 的方法,使用迭代的方式遍历。
- public class HuaWeiPhoneImpl implements IPhone{
- //存储华为手机的数据
- Phone phone[];
- private int index = 0;
-
- //构造器
- public HuaWeiPhoneImpl() {
- phone = new Phone[5];
- add("荣耀", "1300");
- add("华为P8", "2000");
- add("华为P20", "8000");
- }
-
- @Override
- public String getName() {
- return "====华为手机====";
- }
-
- @Override
- public Iterator<Object> createIterator() {
- return new ArrayIterator(phone);
- }
-
- @Override
- public void add(String name, String money) {
- phone[index] = new Phone(name,money);
- index +=1;
- }
- }
复制代码 【8】构造一个数据的工厂类: 将所有的品牌聚集在一块,通过 Iterator 接口的方式进行遍历输出。
- public class OutputImpl {
- //定义一个集合
- private List<IPhone> phones;
-
- //构造器
- public OutputImpl(List<IPhone> phones) {
- this.phones = phones;
- }
-
- //输入方法
- public void outPrit() {
- //先调用 list 自身带的迭代器 Iterator
- Iterator<IPhone> iterator = phones.iterator();
- // 调用hasNext 方法
- while(iterator.hasNext()) {
- IPhone iPhones = iterator.next();
- //手机品牌名称
- System.out.println(iPhones.getName());
- //遍历品牌的所有手机,使用我们自己实现的Iterator
- pritlnPhone(iPhones.createIterator());
- }
- }
-
-
- private void pritlnPhone(Iterator<Object> createIterator) {
- while(createIterator.hasNext()) {
- Phone phone = (Phone) createIterator.next();
- System.out.println("品牌="+phone.getName()+"-----金额="+phone.getMoney());
- }
- }
- }
复制代码 【9】客户端调用: 创建手机品牌实体类,并将其组合在 List 中,调用输出工厂聚集类即可。
- public class Client {
-
- public static void main(String[] args) {
- //手机店集合
- List<IPhone> phones = new ArrayList<IPhone>();
- //创建华为手机 == 数组
- HuaWeiPhoneImpl huaWeis = new HuaWeiPhoneImpl();
- //创建小米手机 == list
- XiaoMiPhoneImpl xiaoMis = new XiaoMiPhoneImpl();
-
- //将其都加入到手机店集合
- phones.add(huaWeis);
- phones.add(xiaoMis);
-
- //调用公共的输入类
- OutputImpl outputImpl = new OutputImpl(phones);
- outputImpl.outPrit();
- /**
- * 结构如下:
- * ====华为手机====
- 品牌=荣耀-----金额=1300
- 品牌=华为P8-----金额=2000
- 品牌=华为P20-----金额=8000
- ====小米手机====
- 品牌=红米-----金额=1200
- 品牌=小米6-----金额=2300
- 品牌=小米7-----金额=3200
- */
- }
- }
复制代码 迭代器模式应用源码分析
分析一下 arrayList 的 iterator 的使用
【1】先了解下 ArrayList 的 Iterator 的使用:
- public class IteratorDemo {
-
- public static void main(String[] args) {
- List<String> a = new ArrayList<>();
- a.add("t");// ..
- // 获取到迭代器
- Iterator<String> Itr = a.iterator();
- while (Itr.hasNext()) {
- System.out.println(Itr.next());
- }
- }
- }
复制代码 【2】进入 ArrayList 的源码: 实现了 List 接口,实现了 Iterator 方法,返回遍历对象:Iterator。相当于具体聚合对象。
- public class ArrayList<E> extends AbstractList<E>
- implements List<E>
- {
- ...
- public Iterator<E> iterator() {
- return new Itr();
- }
- ...
- }
复制代码 【3】进入 List 接口查看: 发现包含一个 Iterator 的抽象方法 。相当于抽象聚合对象
- public interface List<E> extends Collection<E> {
- ...
- Iterator<E> iterator();
- ...
- }
复制代码 【4】我们进入返回的 Iterator 对象的类 Itr ,是 ArraList 类的内部类。查看 hasNext()方法,会发现遍历的对象是 Object[] 数组,具体的迭代器类(实现 hasNext 和 Next 方法)
- private class Itr implements Iterator<E> {
- int cursor; // index of next element to return
- int lastRet = -1; // index of last element returned; -1 if no such
- int 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];
- }
- public void remove() {
- if (lastRet < 0)
- throw new IllegalStateException();
- checkForComodification();
- try {
- ArrayList.this.remove(lastRet);
- cursor = lastRet;
- lastRet = -1;
- expectedModCount = modCount;
- } catch (IndexOutOfBoundsException ex) {
- throw new ConcurrentModificationException();
- }
- }
- ......
- }
复制代码 【5】上述具体迭代器角色实现了 Iterator 接口,也就是抽象迭代器角色。与我们的案例中实现同一个接口,就不展示了。
【6】源码类图展示:同时多添加了两个具体的实现类:KeyIterator 与 LinkedList
访问者模式
简介
在开发中,有些聚集存在多种差别的对象实例(例如:男人、女人),且每个对象也存在多种差别的访问者或处理方式(性格:暴躁、温和)。这样的例子尚有很多,例如:好声音节目中差别评委,以及评委对他们的评价的选项,等等。这些被处理的数据元素相对稳定,而访问方式多种多样的数据布局,假如使用 “访问者模式” 来处理比力方便。访问者模式能把处理方法从数据布局中分离出来,并可以根据需要增加新的处理方式,且不消修改原来的步伐代码与数据布局,这提高了代码的扩展性和灵活性。
【1】访问者模式(Visitor Pattern): 封装一些作用于某种数据布局的各元素的操纵,它可以在不改变数据布局的条件下定义作用于这些元素的新操纵。为数据布局中的每个元素提供多种访问方式。它将对数据的操纵与数据布局进行分离,是举动类模式中最复杂的一种模式。
【2】主要将数据布局与数据操纵分离: 办理数据布局和操纵耦合性问题。
【3】访问者模式的根本工作原理: 在被访问者(上面提到的评委)的类内里加一个对外提供接待访问者的接口。
【4】访问者模式主要应用场景: 需要对一个对象布局中的对象进行很多差别的操纵(这些操纵相互没有关联),同时制止让这些操纵 “污染” 这些类对象,可以选用访问者模式。
访问者模式的优缺点
【访问者(Visitor)模式,其主要优点如下】:
● 扩展性好:可以或许在不修改对象布局中元素的情况下,为对象布局中的元素添加新功能;
● 复用性好:可以通过访问者来定义整个对象布局通用的功能,从而提高系统的复勤奋能;
● 灵活性好:访问者模式将数据布局与作用于布局上的操纵解耦,使得操纵聚集可相对自由;
【访问者(Visitor)模式,其主要缺点如下】:
● 增加新的元素类很困难:在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加响应的具体操纵,违背了 “开闭原则”。
● 破坏封装:访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
● 违反了依赖倒置原则:访问模式依赖了具体类,而没有依赖抽象类。
● 具体元素对访问者公布细节:也就是说访问者关注了其他类的内部细节,这是迪米特法则所不发起的,这样造成了具体元素变得更比力困难。
访问者模式布局类图
访问者模式的关键在于:怎样将元素的操纵分离出来封装成独立的类,其根本布局如下:
【1】抽象访问者角色(Visitor): 定义一个访问具体元素的接口,为每个具体元素类对应一个访问操纵,该操纵中的参数类型标识了被访问的具体元素。
【2】具体访问者角色(ConcreteVisitor): 实现抽象访问者角色中声明的各个访问操纵,确定访问者访问一个元素时该做什么。
【3】抽象元素角色(Element): 声明一个包含接受操纵 accept() 的接口,被接受的访问者对象作为 accept 方法的参数。
【4】具体元素角色(ConcreteElement): 实现抽象元素角色提供的 accept() 操纵,其方法体通常都是 visitor.visit(this),另外具体元素中可能包含本身业务逻辑的相关操纵。
【5】对象布局角色(Object Structure): 是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由List、Set、Map 等聚合类实现。
访问者模式案例分析
评委评价歌手是否晋级的案例
【1】抽象访问者角色
- //访问者接口
- public interface Action {
- //得到评委A的评价 JudgesImpl_A 具体实现类
- public void getJudage_A(JudgesImpl_A judageA);
- //得到评委B的评价
- public void getJudage_B(JudgesImpl_B judageB);
- }
复制代码 【2】抽象访问者的具体实现类——晋级
- //晋级
- public class Promotion implements Action {
-
- @Override
- public void getJudage_A(JudgesImpl_A judageA) {
- System.out.println("评委A名称:"+judageA.getName());
- System.out.println("给的评价是=========晋级");
- }
-
- @Override
- public void getJudage_B(JudgesImpl_B judageB) {
- System.out.println("评委B名称:"+judageB.getName());
- System.out.println("给的评价是=========晋级");
- }
-
- }
复制代码 【3】抽象访问者的具体实现类——淘汰
- public class Eleminate implements Action{
-
- @Override
- public void getJudage_A(JudgesImpl_A judageA) {
- System.out.println("评委A名称:"+judageA.getName());
- System.out.println("给的评价是=========淘汰");
- }
-
- @Override
- public void getJudage_B(JudgesImpl_B judageB) {
- System.out.println("评委B名称:"+judageB.getName());
- System.out.println("给的评价是=========淘汰");
- }
-
- }
复制代码 【4】 抽象元素接口,包含访问者方法 accept ——评委接口
- //评委接口 被访问者接口
- public interface IJudges {
- //提供一个访问方法,出入访问者实例
- public void accept(Action action);
- }
复制代码 【5】抽象元素接口的具体实现类——评委A
- //评委A 被访问者
- public class JudgesImpl_A implements IJudges{
- //传入一个评委名称
- private String name;
- //构造器
- public JudgesImpl_A(String name) {
- super();
- this.name = name;
- }
-
- @Override
- public void accept(Action action) {
- System.out.println("具体被访问者(评委A)对象,给出的评价是==");
- action.getJudage_A(this);
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
- }
复制代码 【6】抽象元素接口的具体实现类——评委B
- //评委B 被访问者
- public class JudgesImpl_B implements IJudges{
- private String name;
- //构造器
- public JudgesImpl_B(String name) {
- super();
- this.name = name;
- }
-
- @Override
- public void accept(Action action) {
- System.out.println("具体被访问者(评委B)对象,给出的评价是==");
- action.getJudage_B(this);
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
- }
复制代码 【7】对象布局实现类,将被访问类进行封装。
- //将评委评论进行统计
- public class ObjectStrFactory {
- //维护一个被访问者集合
- private List<IJudges> judage = new LinkedList();
-
- //添加评委
- public void add(IJudges jud) {
- judage.add(jud);
- }
- //移除评委
- public void remove(IJudges jud) {
- judage.remove(jud);
- }
- //展示评论
- public void disply(Action action) {
- for (IJudges iJudges : judage) {
- iJudges.accept(action);
- }
- }
- }
复制代码 【8】客户端调用
- public class Client {
- public static void main(String[] args) {
- //创建结构体工厂
- ObjectStrFactory objectStrFactory = new ObjectStrFactory();
- //添加评委
- objectStrFactory.add(new JudgesImpl_A("周杰伦"));
- objectStrFactory.add(new JudgesImpl_B("蔡徐坤"));
- //展示评价:晋级的评价
- /**
- * 输出:
- * 具体被访问者(评委A)对象,给出的评价是==
- * 评委A名称:周杰伦
- * 给的评价是=========晋级
- * 具体被访问者(评委B)对象,给出的评价是==
- * 评委B名称:蔡徐坤
- * 给的评价是=========晋级
- */
- objectStrFactory.disply(new Promotion());
- //如果评价是 淘汰
- /**
- * 输入:
- * 具体被访问者(评委A)对象,给出的评价是==
- * 评委A名称:周杰伦
- * 给的评价是=========淘汰
- * 具体被访问者(评委B)对象,给出的评价是==
- * 评委B名称:蔡徐坤
- * 给的评价是=========淘汰
- */
- objectStrFactory.disply(new Eleminate());
- }
- }
复制代码 访问者模式的优点实操
当需要扩展一个 “待定” 的元素选项时,非常方便,简单两步操纵即可完成:
【1】添加 “待定” 元素的实体类,如下:并实现所有评委给的 “待定评价” 即可。
- //待定
- public class Wait implements Action{
-
- @Override
- public void getJudage_A(JudgesImpl_A judageA) {
- System.out.println("评委A名称:"+judageA.getName());
- System.out.println("给的评价是=========待定");
- }
-
- @Override
- public void getJudage_B(JudgesImpl_B judageB) {
- System.out.println("评委B名称:"+judageB.getName());
- System.out.println("给的评价是=========待定");
- }
-
- }
复制代码 【2】客户端调用时,只需传入待定的实体类,便可获取待定评价。通过扩展我们才看出来访问者模式的强大之处。
- public class Client {
- public static void main(String[] args) {
- //输出
- /**
- * 具体被访问者(评委B)对象,给出的评价是==
- * 评委B名称:蔡徐坤
- * 给的评价是=========淘汰
- * 具体被访问者(评委A)对象,给出的评价是==
- * 评委A名称:周杰伦
- * 给的评价是=========待定
- * 具体被访问者(评委B)对象,给出的评价是==
- * 评委B名称:蔡徐坤
- * 给的评价是=========待定
- */
- objectStrFactory.disply(new Wait());
- }
- }
复制代码 观察者模式
简介
生存中,许多事物不是单独存在的,其中一个事物发生变革可能会导致一个或多个其他事物的举动也发生变革。例如:公众号的博主与用户之间(每当推送一篇文章,我们就能被动的接收到一篇文章,条件是你关注了它)。在软件设计中也是一样,例如:MVC 模式中的模子与视图的关系。此类场景使用观察者模式来实现的话,就非常方便。
观察者模块的定义与优缺点
观察者模式(Observer Pattern): 定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到关照并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模子-视图(Model/View)模式、源-监听器(Source/Listener)模式或附属者(Dependents)模式。观察者模式是一种对象举动型模式。
观察者模式的优点如下: ①、低落了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。②、目标与观察者之间创建了一套触发机制。
观察者模式的缺点如下: ①、目标与观察者之间的依赖关系并没有完全解除。②、当观察者对象很多时,关照的发布会花费很长时间,影响步伐的服从。③、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变革的,而仅仅只是知道观察目标发生了变革。④、假如在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统瓦解。
观察者模式的布局与类图
实现观察者模式时,需要注意具体目标对象和具体观察者对象之间不能直接调用,否则会使两者之间精密耦合起来,这违反了面向对象的设计原则。
观察者模式主要包含以下角色:
【1】抽象主题角色(Subject): 也叫抽象目标类,它提供了一个用于生存观察者对象的聚集类和增加、删除观察者对象的方法,以及关照所有观察者的抽象方法。
【2】具体主题角色(Concrete Subject): 也叫具体目标类,它实现了抽象目标中的关照方法,当具体主题的内部状态发生改变时,关照所有注册过的观察者对象。
【3】抽象观察者角色(Observer): 它是一个抽象类或接口,它包含了一个更新本身的抽象方法,当接到具体主题的更改关照时被调用。
【4】具体观察者角色(Concrete Observer): 实现抽象观察者中定义的抽象方法,以便在得到目标的更改关照时更新自身的状态。
观察者模式案例分析
【1】抽象主题角色(Subject): 包含对观察者对象的增加、删除和关照等方法的抽象接口。
- //抽象主题类
- public interface ISubject {
- //添加观察者对象
- public void addObserver(IObserver o);
- //删除观察者对象
- public void removeObserver(IObserver o);
- //通知方法
- public void notifyObserver();
- }
复制代码 【2】具体主题角色(Concrete Subject): 需要创建一个聚集,存放观察者对象。并实现 ISubject 接口,并实现增删和关照方法。同时添加修改数据的方法 setDate。
- public class SubjectImpl implements ISubject{
- private String name;//博客名
- private String content;//博客内容
- private String type;//博客类型
-
- public void setDate(String name,String content,String type) {
- this.name=name;
- this.content=content;
- this.type = type;
- }
-
- //定义一个集合存放 观察者对象
- List<IObserver> observers;
-
- //构造器
- public SubjectImpl() {
- observers = new ArrayList<IObserver>();
- }
- //添加
- @Override
- public void addObserver(IObserver o) {
- observers.add(o);
- }
- //删除
- @Override
- public void removeObserver(IObserver o) {
- observers.remove(o);
- }
-
- @Override
- public void notifyObserver() {
- for (IObserver iObserver : observers) {
- if(iObserver != null) {
- iObserver.update(name,content,type);
- }
- }
- }
- }
复制代码 【3】抽象观察者角色(Observer):抽取观察者们的共同方法 update 获取目标对象传递过来的信息。
- //抽象观察者
- public interface IObserver {
- //接受主题类的信息
- public void update(String name,String content,String type);
- }
复制代码 【4】具体观察者角色(Concrete Observer):实现抽象的观察者角色,并获取目标对象传递的值,进行逻辑处理。
- public class ObserverImpl_A implements IObserver{
-
- @Override
- public void update(String name, String content, String type) {
- //对接受到的信息进行逻辑处理
- System.out.println("===观察者A====");
- System.out.println("***博客名称 : " + name + "***");
- System.out.println("***博客内容: " + content + "***");
- System.out.println("***博客类型: " + type + "***");
- }
- }
复制代码 【5】客户端:创建目标对象和观察者对象,并将观察者对象添加到目标对象的观察者聚集中,并进行关照。
- public class Client {
-
- public static void main(String[] args) {
- //目标对象 博客博主
- SubjectImpl subjectImpl = new SubjectImpl();
- //创建观察者对象 用户A
- ObserverImpl_A observerImpl_A = new ObserverImpl_A();
- //将观察对象 关注 表表对象
- subjectImpl.addObserver(observerImpl_A);
- //博客开始更新博客
- subjectImpl.setDate("Java设计模式", "访问者模式", "技术类");
- //通知观察者 关注的用户门
- subjectImpl.notifyObserver();
- }
- }
复制代码 观察者模式扩展优点
【1】只需要新增观察者对象类。
- package obsever;
-
- public class ObserverImpl_B implements IObserver{
-
- @Override
- public void update(String name, String content, String type) {
- //对接受到的信息进行逻辑处理
- System.out.println("===观察者B====");
- System.out.println("***博客名称 : " + name + "***");
- System.out.println("***博客内容: " + content + "***");
- System.out.println("***博客类型: " + type + "***");
- }
- }
复制代码 【2】客户端调用时,将其注册到目标对象中即可。符合 OCP 原则。
- ObserverImpl_B observerImpl_B = new ObserverImpl_B();
- //将观察对象 关注 表表对象
- subjectImpl.addObserver(observerImpl_B);
复制代码 观察者模式应用源码分析
【1】查看一下 Observable 的源码,发现 Observable 是一个具体的主题类,此处与我们的例子差别之处是没有实现接口,我们细想也会发现,着实主题类也无需实现接口。
- public class Observable {
- private Vector<Observer> obs;
-
- /** 创建集合 存放观察者对象 Observer */
-
- public Observable() {
- obs = new Vector<>();
- }
- //添加
- public synchronized void addObserver(Observer o) {
- if (o == null)
- throw new NullPointerException();
- if (!obs.contains(o)) {
- obs.addElement(o);
- }
- }
- //删除
- public synchronized void deleteObserver(Observer o) {
- obs.removeElement(o);
- }
- //通知
- public void notifyObservers() {
- notifyObservers(null);
- }
- public void notifyObservers(Object arg) {
-
- Object[] arrLocal;
-
- synchronized (this) {
-
- if (!changed)
- return;
- arrLocal = obs.toArray();
- clearChanged();
- }
- //循环调用 观察者对象的 update 通知方法
- for (int i = arrLocal.length-1; i>=0; i--)
- ((Observer)arrLocal[i]).update(this, arg);
- }
复制代码 【2】抽象观察者接口 Observer。
- public interface Observer {
- void update(Observable o, Object arg);
- }
复制代码 命令模式
简介
软件开发中,通常会存在 “方法的哀求者” 与 “方法的实现者” 之间存在精密的耦合关系。这不利于软件功能的扩展与维护。特别是针对举动进行(撤销、重做、记录)一系列操纵时很不方便,因此 “怎样将方法的哀求者与方法的实现者解耦”,是命令模式的主要任务和功能。在现实生存中,这样的例子也很多,例如,电视机遥控器(命令发送者)通过按钮(具体命令)来遥控电视机(命令接收者)
1)、命令模式(Command Pattern): 是一种数据驱动的设计模式,它属于举动型模式。哀求以命令的形式包裹在对象中,并传递给对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
2)、命令模式使得哀求发送者与哀求接受者消除相互之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
3)、在命令模式中,会将一个哀求封装为一个对象,以便使用差别的参数(执行者)来表示差别的哀求。同时命令模式也支持撤销的操纵。
4)、增加或删除命令非常方便。采用命令模式增加和删除命令不会影响其他类,它满足 “开闭原则” ,即扩展灵活。
5)、可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
6)、方便实现 Undo 和 Redo 操纵(适合命令模式)。命令模式可以与后面先容的备忘录模式结合,实现命令的撤销与恢复。
7)、其缺点是:可能产生大量具体命令类。因为对每一个具体操纵都需要设计一个具体命令类,这将增加系统的复杂性。
命令模式布局类图
命令模式包含以下主要角色:
【1】接口命令(Command)角色: 声明执行命令的接口,拥有执行命令的抽象方法。
【2】具体命令(Concrete Command)角色: 是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操纵。
【3】接收者(Receiver)角色: 执行命令功能的相关操纵,是具体命令对象业务的真正实现者。
【4】调用者(Invoker)角色: 是哀求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关哀求,不直接访问接收者。
命令模式案例分析
我们通过写一个空调遥控器按钮的案例来体会命令模式的特点:
【1】接口命令角色:Command,其包含两个主要方法(execute() 与 undo())
- public interface Command {
- //命令的执行方法
- public void execute();
- //撤销操作
- public void undo();
- }
复制代码 【2】 具体命令实现类:写一个制热的命令类,实现命令接口,并组合接受者角色,调用目标方法。雷同的类尚有制冷等等。
- //制热命令
- public class HeadCommand implements Command{
- //组合空调具体执行类
- private AirCondition airCondition;
- //构造器
- public HeadCommand(AirCondition airCondition) {
- super();
- this.airCondition = airCondition;
- }
-
- @Override
- public void execute() {
- //调用空调的制热方法
- airCondition.Head();
- }
-
- @Override
- public void undo() {
- //返回上一次操作
- airCondition.refrigeration();
- }
- }
复制代码 【3】接收者角色:空调类(AirCondition )
- //空调类
- public class AirCondition {
- //制热
- public void Head() {
- System.out.println("空调制热.......");
- }
- //制冷
- public void refrigeration() {
- System.out.println("空调开始制冷......");
- }
- }
复制代码 【4】调用者角色:遥控器类 (RemoteController )
- //调用者( 遥控器 ),也是命令模式的精华
- public class RemoteController {
- //添加命令按钮
- Command[] commands;
- //撤销按钮
- Command undo;
- //构造器
- public RemoteController() {
- //初始化按钮
- commands = new Command[5];
- for(int i=0;i<5;i++) {
- commands[i] = new NoCommand();
- }
- }
-
- //给遥控器添加按钮
- public void setCommand(int n , Command command) {
- commands[n]=command;
- }
-
- //调用制热按钮
- public void headCommonButton(int n) {
- commands[n].execute();
- }
-
- //撤回
- public void undoButton() {
- undo.undo();
- }
- }
复制代码 【5】客户端调用
- public class Client {
- public static void main(String[] args) {
- //创建空调实例
- AirCondition airCondition = new AirCondition();
- //调用命令类
- RemoteController remoteController = new RemoteController();
- //将命令添加至遥控按钮中
- HeadCommand headCommand = new HeadCommand(airCondition);
- remoteController.setCommand(0,headCommand);
- //调用制热功能
- remoteController.headCommonButton(0);
- }
- }
复制代码 【注意】 命令模式的好处:当增加新产物时,只需要创建新产物类即可。无需修改命令类,符合开闭原则。例如我们增加一个冰箱的制热功能。只需要添加冰箱实体类和制热命令类,同时在客户端将其添加至命令类中即可,无需修改命令类。
状态模式
在现实生存中,常常会出现这样的事例:一个人的情绪高兴时,会做出一些助人为乐的事变。情绪低落的时候,会做出一些伤天害理的事变。这里的情绪就是状态,对应做的事变就是举动。在软件开发中也是雷同的,有些对象可能会根据差别的情况做出差别的举动,我们把这种对象称为有状态的对象,而把影响对象举动的一个或多个动态变革的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其举动也随之发生改变。
根本先容
【1】状态(State)模式的定义: 对有状态的对象,把复杂的 “判定逻辑” 提取到差别的状态对象中,答应状态对象在其内部状态发生改变时改变其举动。
【2】状态模式(State Pattern): 它主要用来办理对象在多种状态转换时,需要对外输出差别的举动问题。状态和举动是一一对应的,状态之间可以相互转换。
【3】当一个对象的内在状态改变时,答应改变其举动,这个对象看起来像是改变了其类。
【4】这种类型的设计模式属于举动型模式。
状态模式的布局
状态模式把受情况改变的对象举动包装在差别的状态对象里,其意图是让一个对象在其内部状态改变的时候,其举动也随之改变。现在我们来分析其根本布局和实现方法。状态模式包含以下主要角色:
【1】情况(Context)角色: 也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操纵委托给当前状态对象来处理。
【2】抽象状态(State)角色: 定义一个接口,用以封装情况对象中的特定状态所对应的举动。
【3】具体状态(Concrete State)角色: 实现抽象状态所对应的举动。
状态模式的应用案例
使用状态模式办理 APP 抽奖问题:根据如下流程中的状态,完成具体的业务操纵。
【1】应用的布局类图: 精华在RaffleActive(上下文类)和状态的子类中。两者之间相互组合,减少复杂的逻辑判定。
【2】State 接口的实现如下:
- /*
- * 状态对应的抽象行为
- */
- public interface State {
- //扣除积分
- public void deduceMoney();
- //是否中奖
- public boolean raffle();
- //发放奖品
- public void dispensePrize();
- }
复制代码 【3】State 子类一:扣除积分类 NoRaffleState 的实现如下,扣除成功后,将 state 设置为抽奖状态。
- public class NoRaffleState implements State{
-
- //初始化时传入活动引用,扣除积分后改变其状态
- RaffleActivity active;
-
- //构造器
- public NoRaffleState(RaffleActivity active) {
- this.active = active;
- }
-
- // 当前状态可以扣分,扣分后修改状态
- @Override
- public void deduceMoney() {
- System.out.println("扣除5个积分");
- active.setState(active.getCanRaffeState());
-
- }
- @Override
- public boolean raffle() {
- System.out.println("抽了积分才能抽奖");
- return false;
- }
-
- @Override
- public void dispensePrize() {
- System.out.println("不能方法奖品");
- }
- }
复制代码 【4】 State 子类一:抽奖状态类 CanRaffeState,假如抽中设置状态为抽中状态,未抽中时设置状态为扣积分状态。
- public class CanRaffeState implements State{
- RaffleActivity active;
-
- //构造器
- public CanRaffeState(RaffleActivity active) {
- this.active = active;
- }
-
- //扣积分
- @Override
- public void deduceMoney() {
- System.out.println("已扣除积分");
- }
-
- @Override
- public boolean raffle() {
- System.out.println("正在抽奖,请稍等");
- int num = new Random().nextInt(5);
- //20% 的中奖机会,中了则返回true
- if(num == 0) {
- active.setState(active.getDispenseState());
- return true;
-
- }else {
- System.out.println("很遗憾没有抽中奖品");
- active.setState(active.getNoRaffleState());
- return false;
- }
- }
-
- @Override
- public void dispensePrize() {
- System.out.println("抽奖中,不能发送奖品");
- }
- }
复制代码 【5】 State 子类一:抽中状态类 DispenseState,假如礼物未送完,则发送礼物,并设置状态为扣分状态。否则设置为礼物以发放完,且活动结束状态。
- public class DispenseState implements State{
- RaffleActivity active;
-
- //构造器
- public DispenseState(RaffleActivity active) {
- this.active = active;
- }
-
- @Override
- public void deduceMoney() {
- System.out.println("不能扣积分");
- }
-
- @Override
- public boolean raffle() {
- System.out.println("不能抽奖");
- return false;
- }
-
- @Override
- public void dispensePrize() {
- if(active.getCount()>0) {
- System.out.println("恭喜中奖了,奖品已发货");
- active.setState(active.getNoRaffleState());
- }else {
- System.out.println("很遗憾,奖品已发完");
- active.setState(active.getDispenseOutState());
- }
- }
- }
复制代码 【6】 State 子类一:礼物发放完,却活动结束状态类 DispenseOutState
- public class DispenseOutState implements State{
-
- @Override
- public void deduceMoney() {
- System.out.println("活动结束");
- }
-
- @Override
- public boolean raffle() {
- System.out.println("活动结束");
- return false;
- }
-
- @Override
- public void dispensePrize() {
- System.out.println("活动结束");
- System.exit(0);
- }
- }
复制代码 【7】上下文类 RaffleActivity,主要存储用户的状态和礼物的总记录数等重要信息。并组合所有的状态子类,传入自身对象。
- public class RaffleActivity {
- //状态
- private State state;
- //奖品记录数
- private int count;
-
- //构造器,初始化上述两个属性
- public RaffleActivity(int count) {
- //客户端创建的时候,说明开始是开始抽奖状态。
- state = noRaffleState;
- this.count = count;
- }
-
- NoRaffleState noRaffleState = new NoRaffleState(this);
- CanRaffeState canRaffeState = new CanRaffeState(this);
- DispenseState dispenseState = new DispenseState(this);
- DispenseOutState dispenseOutState = new DispenseOutState();
-
- // 当前状态可以扣分,扣分后修改状态
- public void deduceMoney() {
- state.deduceMoney();
- }
- public void raffle() {
- if(state.raffle()) {
- state.dispensePrize();
- }
- }
-
- //需要注意,我们的数量应该是递减的
- public int getCount() {
- int countNum = count;
- count--;
- return countNum;
- }
-
- public void setCount(int count) {
- this.count = count;
- }
-
- public State getState() {
- return state;
- }
-
- public void setState(State state) {
- this.state = state;
- }
-
- public NoRaffleState getNoRaffleState() {
- return noRaffleState;
- }
-
- public CanRaffeState getCanRaffeState() {
- return canRaffeState;
- }
-
- public DispenseState getDispenseState() {
- return dispenseState;
- }
-
- public DispenseOutState getDispenseOutState() {
- return dispenseOutState;
- }
- }
复制代码 【8】客户端端调用类 Client,只需要调用上下文类,便可实现客户端的需求。
- public class Client {
-
- public static void main(String[] args) {
- //为了演示方便,就定义只有一个奖品
- RaffleActivity activity = new RaffleActivity(1);
- for(int i=0;i<30;i++) {
- System.out.println("======第"+i+"次,抽取奖品========");
- //扣积分
- activity.deduceMoney();
- //抽奖
- activity.raffle();
- }
- }
- }
复制代码 状态模式的特点
状态模式的主要优点如下:
【1】状态模式将与特定状态相关的举动局部化到一个状态中,而且将差别状态的举动分割开来,满足“单一职责原则”。
【2】减少对象间的相互依赖。将差别的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
【3】有利于步伐的扩展。通过定义新的子类很容易地增加新的状态和转换。
状态模式的主要缺点如下:
【1】状态模式的使用必然会增加系统的类与对象的个数。
【2】状态模式的布局与实现都较为复杂,假如使用不当会导致步伐布局和代码的杂乱。
计谋模式
在现实生存中常常遇到实现某种目标存在多种计谋可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或本身开私人车等。在软件开发中也常常遇到雷同的情况,当实现某一个功能存在多种算法大概计谋,我们可以根据情况大概条件的差别选择差别的算法大概计谋来完成该功能,如数据排序计谋有冒泡排序、选择排序、插入排序、二叉树排序等。
计谋模式根本先容
【1】计谋模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变革独立于使用算法的用户。计谋模式属于对象举动模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给差别对象进行管理。
【2】这算法表现了几个设计原则,第一:把变革的代码从稳定的代码中分离出来;第二:针对接口编程而不是具体的类(定义了计谋接口);第三:多个组合/聚合,少用继续(客户通过组合方式使用计谋)
【3】多重条件语句不易维护,而使用计谋模式可以制止使用多重条件查询。算法可以自由切换。
【4】计谋模式提供了对 “开闭原则” 的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
【5】计谋模式把算法的使用放到情况类中,而算法的实现移到具体计谋类中,实现了二者的分离。
【6】缺点:客户端必须明确所有计谋算法的区别,以便适时选择适当的算法类。同时计谋模式造成很多的计谋类。
计谋模式布局类图
TIP:计谋模式是预备一组算法,并将这组算法封装到一系列的计谋类内里,作为一个抽象计谋类的子类。计谋模式的重心不是怎样实现算法,而是怎样组织这些算法,从而让步伐布局更加灵活,具有更好的维护性和扩展性。
计谋模式的主要角色如下: 抽象计谋(Strategy)类:定义一个公共接口,各种差别的算法以差别的方式实现这个接口,一般使用接口大概抽象类。
具体计谋(Concrete Strategy)类: 实现了抽象计谋定义的接口,提供具体的算法实现。
上下文(Context)类: 持有一个具体的计谋类引用,终极给客户端调用。
计谋模式案例分析
我们通过写一个旅游出行方式的案例来体会计谋模式的特点:
【1】交通方式的抽象计谋角色
- //交通方式的 抽象策略
- public interface TripMode {
- public void goOut();
- }
复制代码 【2】具体计谋角色(火车、汽车、飞机)
- //具体实现类一 : 飞机
- public class ByCar implements TripMode{
-
- @Override
- public void goOut() {
- System.out.println("飞机出行,最快的选择");
- }
- }
- //具体实现类二 : 火车
- public class ByTrain implements TripMode{
-
- @Override
- public void goOut() {
- System.out.println("火车出行,最安全的选择");
- }
- }
- //具体实现类三 : 自驾车
- public class SelfDrive implements TripMode{
-
- @Override
- public void goOut() {
- System.out.println("旅游就的是自驾车");
- }
- }
复制代码 【3】我们就举一个最安全的出行实例:组合抽象计谋类,实现了具体使用类与计谋类的分离。
- package stratege;
- //注重安全的人,都会选择火车
- public class Safe {
- //组合接口
- private TripMode train;
-
- public Safe() {
- //组合一个火车实例
- train = new ByTrain();
- }
-
- //客户端也可以根据情况进行重新赋值
- public void setTrain(TripMode train) {
- this.train = train;
- }
-
- public void toBeijing() {
- //去北京选择的工具
- train.goOut();
- }
- }
复制代码 【4】客户端直接调用即可,大概使用 set 对其计谋进行重置。
- public class Client {
- public static void main(String[] args) {
- Safe safe = new Safe();
- //火车出行,最安全的选择
- safe.toBeijing();
- }
- }
复制代码 计谋接口的源码应用分析
【1】抽象计谋类:Comparator 接口
- public interface Comparator<T> {
- ...
- int compare(T o1, T o2);
- ...
- }
复制代码 【2】具体计谋类:可以根据本身的需求,实现差别的升序和降序等计谋。
- public static void main(String[] args) {
- Integer[] data = {3,1,5,8,6};
- //实现升序排序,返回-1放左边,1放右边,0不变
- //Comparator 就是一个抽象策略类(接口),我们对其进行实现,就是一个具体的策略
- Comparator<Integer> comparator = new Comparator<Integer>() {
- @Override
- public int compare(Integer o1, Integer o2) {
- if(o1>o2) {
- return 1;
- }else {
- return -1;
- }
- }
- };
- Arrays.sort(data,comparator);
- //[1, 3, 5, 6, 8]
- System.out.println(Arrays.toString(data));
- }
- }
复制代码 备忘录模式
备忘录模式(Memento Pattern): 生存对象的某个状态,以便在未来需要的时候进行数据的恢复。相当容易明确,举个简单的例子:Word 软件在编辑时按 Ctrl+Z 组合键时能撤销当前操纵,使文档恢复到之前的状态;
备忘录模式的根本先容
【1】备忘录模式(Memento Pattern): 在不破坏封装性的条件下,捕获一个对象的内部状态,并在该对象之外生存这个状态。这样以后就可将该对象恢复到原先生存的状态。
【2】现实生存中备忘录是用来记录某些要去做的事变,大概是记录已经达成共同意见的事变,以防忘记。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,大概某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操纵。
【3】备忘录模式属于举动型模式。
【4】实现了信息的封装,使得用户不需要关心状态和生存细节。符合 “单一职责原则” 。
【5】为了节约内存,备忘录模式可以和原型模式共同使用。
备忘录模式的布局与类图
忘录模式的焦点是设计备忘录类以及用于管理备忘录的管理者类,备忘录模式的主要角色如下:
【1】发起人(Originator)角色: 记录当前对象的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
【2】备忘录(Memetor)角色: 负责存储发起人对象的内部状态,在需要的时候提供这些内部状态给发起人。
【3】管理者(Caretaker)角色: 对备忘录进行管理,提供生存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
备忘录模式案例分析
我们使用备忘录模式写一个学校 100 米考试的案例:5 人一组,进行分组测试。我们需要备份的对象是,一组门生的成绩。
【1】发起人(Originator)角色: 发起人需要依赖备忘录类,对本身每次记录的成绩进行备份 createMemento 。同时需要提供一个还原方法 getOriginalFromMemento 将需要的备份类作为参数传递进来,并将效果赋值给目标类。
- //100 米 测试 5人一组 ,这是个人所花费的时间,我们使用备忘录的方式实现一下。
- public class Original {
- //姓名
- private String name;
- //时间
- private int timestamp;
-
- public Memento createMemento() {
- return new Memento(name, timestamp);
- }
- //获取目标对象 通过排名
- public void getOriginalFromMemento(Memento memento) {
- name = memento.getName();
- timestamp = memento.getTimestamp();
- }
-
- public int getTimestamp() {
- return timestamp;
- }
-
- public void setTimestamp(int timestamp) {
- this.timestamp = timestamp;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
- @Override
- public String toString() {
- return "Original [name=" + name + ", timestamp=" + timestamp + "]";
- }
- }
复制代码 【2】备忘录(Memetor)角色: 提供一些目标对象需要备份的属性,通过构造器进行属性传递。
- public class Memento {
- //姓名
- private String name;
- //时间
- private int timestamp;
-
- //构造器
- public Memento(String name, int timestamp) {
- super();
- this.name = name;
- this.timestamp = timestamp;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getTimestamp() {
- return timestamp;
- }
-
- public void setTimestamp(int timestamp) {
- this.timestamp = timestamp;
- }
- }
复制代码 【3】管理者(Caretaker)角色:创建一个存储备忘录对象的聚集 `` 等,同时创建添加、获取、清空等方法。
- public class Caretaker {
- //排名
- private int index = 1;
- //用于顺序存储 参加测试的 十名同学的成绩
- Map<Integer, Memento> mementos = new HashMap<Integer, Memento>();
- //提供一个 add 方法
- public void add(Memento m) {
- mementos.put(index, m);
- index += 1;
- }
- //获取备份类
- public Memento get(int key) {
- return mementos.get(key);
- }
- //归位
- public void remove() {
- mementos.clear();
- }
- }
复制代码 【4】客户端(Client)角色:起首将顺序按的前五名同学的成绩及姓名,通过聚集 Map 按照名次存储。五名同学测试完成后,通过目标类的 getOriginalFromMemento 方法,根据名次获取同学的成绩,并记录在成绩花名册中。末了,清空记录下一组。
- public class Client {
- public static void main(String[] args) {
- //创建 目标类
- Original original = new Original();
- //管理类
- Caretaker managerMemento = new Caretaker();
- //记录成绩
- recordResults(original,managerMemento);
- //获取第一名的成绩
- System.out.println("恢复前目标类的记录值"+original.toString());
- //从集合中获取第一名的值
- Memento memento1 = managerMemento.get(1);
- //调用目标类的还原方法
- original.getOriginalFromMemento(memento1);
- System.out.println("恢复第一名的成绩信息:"+original.toString());
-
- //从集合中获取第三名的值
- Memento memento3 = managerMemento.get(3);
- //调用目标类的还原方法
- original.getOriginalFromMemento(memento3);
- System.out.println("恢复第三名的成绩信息:"+original.toString());
-
- //清空记录下一组
- managerMemento.remove();
- }
-
- private static void recordResults(Original original,Caretaker managerMemento) {
- //第一名学生成绩
- original.setName("张三");
- original.setTimestamp(2330);
- //创建一个备份类
- Memento memento1 = original.createMemento();
- //备份类存入 管理类中
- managerMemento.add(memento1);
-
- //第二名学生成绩
- original.setName("李四");
- original.setTimestamp(2550);
- //创建一个备份类
- Memento memento2 = original.createMemento();
- //备份类存入 管理类中
- managerMemento.add(memento2);
-
- //第三名学生成绩
- original.setName("王五");
- original.setTimestamp(2560);
- //创建一个备份类
- Memento memento3 = original.createMemento();
- //备份类存入 管理类中
- managerMemento.add(memento3);
- }
- }
复制代码 【5】效果展示: 备份者模式思想相对简单,主要查看细节上的实现。备份类相对简单,就一个普通类。但是目标类,提供了存储原始对象和获取原始对象的方法,是备份者模式的精华地点。
- 恢复目标类的记录值Original [name=王五, timestamp=2560]
- 恢复第一名的成绩信息:Original [name=张三, timestamp=2330]
- 恢复第三名的成绩信息:Original [name=王五, timestamp=2560]
复制代码 中介者模式
在现实生存中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是 “网状布局”,它要求每个对象都必须知道它需要交互的对象。例如,班长和团支书等干部需要记住同学们的电话,且同学中假如有人的电话修改了,需要告诉所有干部,本身的手机号修改了,这叫作 “牵一发而动满身”,非常复杂。假如把这种 “网状布局” 改为 “星形布局” 的话,将大大低落它们之间的 “耦合性”,这时只要找一个 “中介者” 就可以了。如前面所说的问题,只要在网上创建一个每个干部都可以访问的 “通讯录”(中介)就办理了。这样的例子尚有很多,例如,你刚刚工作想租房,可以找 “房屋中介”;大概,本身刚刚到一个生疏城市找工作,可以找 “人才交流中央 ”帮忙。软件的开发过程中,这样的例子也很多,例如,在 MVC 框架中,控制器(C)就是模子(M)和视图(V)的中介者,它将大大低落对象之间的耦合性,提高系统的灵活性。
中介者模式的定义与优缺点
【1】中介者模式(Mediator): 定义一个中介对象来封装一些列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变他们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型代表。
【2】中介者模式是一种对象举动模式,其主要优点如下: ①、低落了对象之间的耦合性,使得对象易于独立地被复用。②、将对象间的一对多关联变革为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
【3】中介者模式主要缺点如下: ①、当同事类太多时,中介者的职责将很大,它会变得复杂而巨大,以至于系统难以维护。②、中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响。
【4】使用场景: 1、系统中对象之间存在比力复杂的引用关系,导致它们之间的依赖关系布局杂乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的举动,而又不想生成太多的子类。
【5】注意事项: 不应当在职责杂乱的时候使用。
中介者模式的布局与类图
中介者模式的关键点是找出 “中介者”,中介者模式包含一下主要角色:
【1】抽象中介者(Mediator)角色: 它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
【2】具体中介者(ConcreteMediator)角色: 实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
【3】抽象同事类(Colleague)角色: 定义同事类的接口,生存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
【4】具体同事类(Concrete Colleague)角色: 是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
中介者模式案例分析
【1】抽象中介者(Mediator)角色: 包含将租客注入到聚集中的方法 register 和 租客发送需求后需要进行业务逻辑处理(调用其他租客的信息或房东信息进行匹配)的方法 getMessage
- public interface Mediator {
- //注册同事类
- public void register(Colleague colleague);
- //获取消息
- public void getMessage(Colleague colleague);
- }
复制代码 【2】具体中介者(ConcreteMediator)角色: 对中介者接口的实现,这里存储租客对象的聚集为 Map 将租客类名作为 Key 存储,并在 getMessage 方法中,将与哀求租客信息符合的需求进行反馈。
- package mediator;
-
- import java.util.HashMap;
- import java.util.Map;
-
- public class ConcreteMediator implements Mediator{
- //创建存放 mediator 的集合 map
- Map<String, Colleague> mediators = new HashMap<>();
-
- @Override
- public void register(Colleague colleague) {
- if(colleague instanceof Colleague_A) {
- mediators.put("colleague_A", colleague);
- }else if(colleague instanceof Colleague_B) {
- mediators.put("colleague_B", colleague);
- }
- }
- //中介获取到 租客A 与 租客 B 的信息后,进行智能分析,并给出以下结论
- @Override
- public void getMessage(Colleague colleague) {
- //当为组合A 调用中介时,中介将租客 B 的需求进行返回 ,表示可以合租
- //这里就是中介的作用,将租客A与租客B进行关联,之间的业务逻辑都在中介中实现,将A与B彻底解耦,便于扩展维护
- if(colleague instanceof Colleague_A) {
- //智能分析A需求
- handler(colleague.needs());
- //调用租客A的需求
- System.out.println("服务的当前对象为====租客A=====");
- System.out.println("租客A 你的需求与 租客B 相吻合,下面将为你展示租客B的需求");
- String need_B = mediators.get("colleague_B").needs();
- System.out.println(need_B);
- }else if(colleague instanceof Colleague_B) {
- //智能分析B需求
- handler(colleague.needs());
- //调用租客B的需求
- System.out.println("服务的当前对象为====租客B=====");
- System.out.println("租客B 你的需求与 租客A 相吻合,下面将为你展示租客A的需求");
- String need_A = mediators.get("colleague_A").needs();
- System.out.println(need_A);
- }
- }
- //通过人工智能对用户需求进行分析 , 非设计模式的重点,省略
- private void handler(String needs) {
-
- }
- }
复制代码 【3】抽象同事类(Colleague)角色: 租客对象的抽象接口,将租客的共同点抽取。sendMessage 方法是通过调用中介的 getMessage 方法进行业务逻辑处理,返回符合用户需求的房屋信息或房客信息。needs 则为用户的需求。
- public interface Colleague {
- //租客发出需求
- public void sendMessage();
- //需求方法
- public String needs();
- }
复制代码 【4】具体同事类(Concrete Colleague)角色: 租客的具体信息,我们距离用创建了两个租客A与B,实现租客接口 Colleague 并调用中介的业务逻辑处理方法 getMessage 进行房屋信息匹配。如下:租客A
- public class Colleague_A implements Colleague{
- private Mediator mediator;
- //组合中介类
- public Colleague_A(Mediator mediator) {
- this.mediator = mediator;
- //将 租房 客户 A 注册到中介中
- mediator.register(this);
- }
- //租客A 发送自己的需求
- @Override
- public void sendMessage() {
- mediator.getMessage(this);
- }
- public String needs() {
- return "需要租两室一厅,本人女,找一名合租者";
- }
- }
复制代码 【5】租客B
- public class Colleague_B implements Colleague{
- //组合中介类
- private Mediator mediator;
-
- //将租客B 注册到中介类中
- public Colleague_B(Mediator mediator) {
- this.mediator = mediator;
- mediator.register(this);
- }
- //租客B 发送自己的需求
- @Override
- public void sendMessage() {
- mediator.getMessage(this);
- }
- public String needs() {
- return "需要一套两室一厅,需要一名女士合租";
- }
- }
复制代码 表明器模式
在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。假如将它们归纳成一种简单的表达式(例如:正则表达式等),那么这些问题实例将是该表达式的一些句子,这样就可以用 “编译原理” 中的表明器模式来实现。
表明器模式根本先容
【1】表明器模式(Interpreter Pattern): 是指给定一个语言(表达式),定义它的文法的一种表示。并定义一个表明器,使用该表明器来表明语言中的句子(表达式)。
【2】在编译原理中,一个算术表达式通过词法分析器形成词法单位,而后这些词法单位再通过语法分析器构建语法分析树,终极形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是表明器。
【3】表明器模式是一种类举动型模式,其主要优点如下: ①、扩展性好,由于在表明器模式中使用类来表示语言的文法规则,因此可以通过继续等机制来改变或扩展文法。②、容易实现,在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
【4】表明器模式的主要缺点如下: ①、执行服从较低。表明器模式中通常使用大量的循环和递归调用,当要表明的句子较复杂时,其运行速度很慢,且代码的调试过程也比力贫苦。②、会引起类膨胀。表明器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。③、可应用的场景比力少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
【5】应用场景: ①、应用可以将一个需要表明执行的语言中的句子表示为一个抽象语法树 。②、一些重复出现的问题可以用一种简单的语言来表达。③、一个简单语法需要表明的场景。
【6】这样的例子尚有,比如编译器、运算表达式计算、正则表达式、呆板人等。
表明器模式的布局与类图
表明器模式包含以下主要角色:
【1】抽象表达式(Abstract Expression)角色: 定义表明器的接口,约定表明器的表明操纵,主要包含表明方法 interpret()。
【2】终结符表达式(Terminal Expression)角色: 是抽象表达式的子类,用来实现文法中与终结符相关的操纵,文法中的每一个终结符都有一个具体终结表达式与之相对应。
【3】非终结符表达式(Nonterminal Expression)角色: 也是抽象表达式的子类,用来实现文法中与非终结符相关的操纵,文法中的每条规则都对应于一个非终结符表达式。
【4】情况(Context)角色: 通常包含各个表明器需要的数据或是公共的功能,一般用来传递被所有表明器共享的数据,后面的表明器可以从这里获取这些值。
【5】客户端(Client): 主要任务是将需要分析的句子或表达式转换成使用表明器对象形貌的抽象语法树,然后调用表明器的表明方法,固然也可以通过情况角色间接访问表明器的表明方法。
表明器模式案例分析
【1】抽象表达式: 抽象类表达式,通过 HashMap 键值对, 可以获取到变量的值。
- /**
- * 抽象类表达式,通过HashMap 键值对, 可以获取到变量的值
- */
- public abstract class Expression {
- // a + b - c
- // 解释公式和数值, key 就是公式(表达式) 参数[a,b,c], value就是就是具体值
- // HashMap {a=10, b=20}
- public abstract int interpreter(HashMap<String, Integer> var);
- }
复制代码 【2】终结符表达式:
- public class Calculator {
-
- // 定义表达式
- private Expression expression;
-
- // 构造函数传参,并解析
- public Calculator(String expStr) { // expStr = a+b
- // 安排运算先后顺序
- Stack<Expression> stack = new Stack<>();
- // 表达式拆分成字符数组
- char[] charArray = expStr.toCharArray();// [a, +, b]
-
- Expression left = null;
- Expression right = null;
- //遍历我们的字符数组, 即遍历 [a, +, b]
- //针对不同的情况,做处理
- for (int i = 0; i < charArray.length; i++) {
- switch (charArray[i]) {
- case '+': //
- left = stack.pop();// 从stack取出left => "a"
- right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b"
- stack.push(new AddExpression(left, right));// 然后根据得到left 和 right 构建 AddExpresson加入stack
- break;
- case '-': //
- left = stack.pop();
- right = new VarExpression(String.valueOf(charArray[++i]));
- stack.push(new SubExpression(left, right));
- break;
- default:
- //如果是一个 Var 就创建要给 VarExpression 对象,并push到 stack
- stack.push(new VarExpression(String.valueOf(charArray[i])));
- break;
- }
- }
- //当遍历完整个 charArray 数组后,stack 就得到最后Expression
- this.expression = stack.pop();
- }
-
- public int run(HashMap<String, Integer> var) {
- //最后将表达式a+b和 var = {a=10,b=20}
- //然后传递给expression的interpreter进行解释执行
- return this.expression.interpreter(var);
- }
- }
复制代码 【3】非终结符表达式(抽象): 抽象运算符号解析器 这里,每个运算符号,都只和本身左右两个数字有关系。
- /**
- * 抽象运算符号解析器 这里,每个运算符号,都只和自己左右两个数字有关系,
- * 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression类的实现类
- *
- * @author Administrator
- *
- */
- public class SymbolExpression extends Expression {
-
- protected Expression left;
- protected Expression right;
-
- public SymbolExpression(Expression left, Expression right) {
- this.left = left;
- this.right = right;
- }
-
- //因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现
- @Override
- public int interpreter(HashMap<String, Integer> var) {
- // TODO Auto-generated method stub
- return 0;
- }
- }
复制代码 【4】非终结符表达式(实现一):
- // 加法解释器
- public class AddExpression extends SymbolExpression {
-
- public AddExpression(Expression left, Expression right) {
- super(left, right);
- }
-
- //处理相加
- //var 仍然是 {a=10,b=20}..
- //一会我们debug 源码,就ok
- public int interpreter(HashMap<String, Integer> var) {
- //super.left.interpreter(var) : 返回 left 表达式对应的值 a = 10
- //super.right.interpreter(var): 返回right 表达式对应值 b = 20
- return super.left.interpreter(var) + super.right.interpreter(var);
- }
- }
复制代码 【5】非终结符表达式(实现二):
- public class SubExpression extends SymbolExpression {
-
- public SubExpression(Expression left, Expression right) {
- super(left, right);
- }
-
- //求出left 和 right 表达式相减后的结果
- public int interpreter(HashMap<String, Integer> var) {
- return super.left.interpreter(var) - super.right.interpreter(var);
- }
- }
复制代码 【6】客户端:
- public class ClientTest {
-
- public static void main(String[] args) throws IOException {
- // TODO Auto-generated method stub
- String expStr = getExpStr(); // a+b
- HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20}
- Calculator calculator = new Calculator(expStr);
- System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
- }
-
- // 获得表达式
- public static String getExpStr() throws IOException {
- System.out.print("请输入表达式:");
- return (new BufferedReader(new InputStreamReader(System.in))).readLine();
- }
-
- // 获得值映射
- public static HashMap<String, Integer> getValue(String expStr) throws IOException {
- HashMap<String, Integer> map = new HashMap<>();
-
- for (char ch : expStr.toCharArray()) {
- if (ch != '+' && ch != '-') {
- if (!map.containsKey(String.valueOf(ch))) {
- System.out.print("请输入" + String.valueOf(ch) + "的值:");
- String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
- map.put(String.valueOf(ch), Integer.valueOf(in));
- }
- }
- }
-
- return map;
- }
- }
复制代码 责任链模式
在现实生存中,常常会出现这样的事例:一个哀求有多个对象可以处理,但每个对象的处理条件或权限差别。例如,公司员工差旅费的报销,可审批的领导有部分负责人、副总司理、总司理等,但每个领导能批准的金额差别,员工必须根据本身要批准的金额去找差别的领导署名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。
在计算机软硬件中也有相关例子,例如非常处理中,处理步伐根据非常的类型决定本身是否处理该非常;尚有 Struts2 的拦截器、JSP 和 Servlet 的 Filter 等,所有这些,假如用责任链模式都能很好办理。
TIP
职责链模式(Chain of Responsibility Pattern):又叫责任链模式,为哀求创建了一个接收者对象的链。这种模式给予哀求的类型,对哀求的发送者和接收者进行解耦。这种类型的设计模式属于举动型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。假如一个对象不能处理该哀求,那么它会把相同的哀求传给下一个接收者,依此类推。
模式的布局
职责链模式主要包含以下角色:
抽象处理者(Handler)角色: 定义一个处理哀求的抽象类或接口,包含抽象处理方法和本身(传入下一个子类时赋值)。
具体处理者(Concrete Handler)角色: 实现抽象处理者的处理方法,判定能否处理本次哀求,假如可以处理哀求则处理,否则将该哀求转给它的后继者。
哀求对象(Request): 包含很多哀求属性。
客户类(Client)角色: 创建处理链,并向链头的具体处理者对象提交哀求,它不关心处理细节和哀求的传递过程。
职责链模式的应用案例
学校 OA 系统的采购审批项目:采购员采购讲授器材费用审批规则如下:
■ 假如金额 小于等于 10000,有学校主任审批;
■ 假如金额 小于等于 30000,由院长审批;
■ 假如金额 大于 30000,由校长审批;
【1】哀求者类(普通类): 包含两个属性(编号:id 和价格:price)
- public class Request {
- //请求编号
- private int id;
- //请求价格
- private float price;
- /**
- * @param id
- * @param price
- */
- public Request(int id, float price) {
- super();
- this.id = id;
- this.price = price;
- }
- public int getId() {
- return id;
- }
- public float getPrice() {
- return price;
- }
- }
复制代码 【2】 抽象处理者(Handler)类:特点是包含了一个本身的属性,有点像装饰者模式。用于传入本身的子类,通过 set方法。尚有一个抽象的业务处理方法 process。
- public abstract class Handler {
- //组合一个自己,并创建一个 set 方法,由子类使用
- Handler handler;
- //处理者的名称
- String name;
-
- /**
- * @param name
- * 构造器,传入名称即可
- */
- public Handler(String name) {
- super();
- this.name = name;
- }
- public void setHandler(Handler handler) {
- this.handler = handler;
- }
-
- //业务处理方法,抽象的
- public abstract void process(Request request);
- }
复制代码 【3】具体处理者(Concrete Handler)类:主任审批类。存在多个相似的类,都继续自抽象处理者。实现抽象的 process 方法,假如能处理则处理,否则抛给其他子类。
- public class CollegeHandler extends Handler{
-
- private static final float PRICE_1 = 10000f;
-
- public CollegeHandler(String name) {
- super(name);
- }
-
- //如果金额 小于等于 10000,有学校主任审批
- @Override
- public void process(Request request) {
- if(request.getPrice() <= PRICE_1) {
- System.out.println("请求编号: "+request.getId()+"被"+name + "处理");
- }else {
- handler.process(request);
- }
- }
- }
复制代码 【3.1】 具体处理者(Concrete Handler)类:院长审批。与上述的住人审批根本相同,区别就是业务处理方法 process 的处理条件差别而已。处理不了就抛给父类的 Handler 对象。校长审批与之雷同省略。。。
- public class DepartmentHandler extends Handler{
-
- private static final float PRICE_1 = 10000f;
- private static final float PRICE_3 = 30000f;
-
- public DepartmentHandler(String name) {
- super(name);
- }
-
- //如果金额 小于等于 30000,由院长审批;
- @Override
- public void process(Request request) {
- if(request.getPrice() > PRICE_1 && request.getPrice() <= PRICE_3) {
- System.out.println("请求编号: "+request.getId()+"被"+name + "处理");
- }else {
- handler.process(request);
- }
- }
- }
复制代码 【4】客户类(Client)类:将哀求对象和处理类进行关联。特点是将多个处理类的关系进行了链式组装。代码如下:
- public class Client {
- public static void main(String[] args) {
- //创建请求对象
- Request request = new Request(1, 300001);
- //创建各个处理者
- DepartmentHandler departmentHandler = new DepartmentHandler("学校主任");
- CollegeHandler collegeHandler = new CollegeHandler("院长");
- SchoolMasterHandler schoolMasterHandler = new SchoolMasterHandler("校长");
- //对处理者的链进行组装(注意:这里组装成一个环形)
- //可能有人认为,会不会出现死循环,这个应该是业务代码考虑的问题。如果有不能处理的情况应该自行抛出错误
- departmentHandler.setHandler(collegeHandler);
- collegeHandler.setHandler(schoolMasterHandler);
- schoolMasterHandler.setHandler(departmentHandler);
-
- //可以随便调用一个处理者来处理对象
- collegeHandler.process(request);
- departmentHandler.process(request);
- /** 输出展示:请求编号: 1被校长处理
- * 请求编号: 1被校长处理
- */
- }
- }
复制代码 职责链模式的特点
职责链模式的优点:
1)低落了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其哀求以及链的布局,发送者和接收者也无须拥有对方的明确信息。
2)增强了系统的可扩展性。可以根据需要增加新的哀求处理类,满足开闭原则。
3)增强了给对象指派职责的灵活性。当工作流程发生变革,可以动态地改变链内的成员大概调动它们的次序,也可动态地新增大概删除责任。
4)责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这制止了使用浩繁的 if 大概 if···else 语句。
5)责任分担。每个类只需要处理本身该处理的工作,不应处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
职责链模式的缺点:
1)、不能保证每个哀求一定被处理。由于一个哀求没有明确的接收者,所以不能保证它一定会被处理,该哀求可能一直传到链的末了都得不到处理。
2)、对比力长的职责链,哀求的处理可能涉及多个处理对象,系统性能将受到一定影响。
3)、职责链创建的公道性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
模式的应用场景
前边已经讲述了关于责任链模式的布局与特点,下面先容其应用场景,责任链模式通常在以下几种情况使用。
【1】有多个对象可以处理一个哀求,哪个对象处理该哀求由运行时候自动确定。
【2】可动态指定一组对象处理哀求,或添加新的处理者。
【3】在不明确指定哀求处理者的情况下,向多个处理者中的一个提交哀求。
模式的扩展
职责链模式存在以下两种情况:
【1】纯的职责链模式: 一个哀求必须被某一个处理者对象所接收,且一个具体处理者对某个哀求的处理只能采用以下两种举动之一:本身处理(承担责任);把责任推给下家处理。也就是上面的例子。
【2】不纯的职责链模式: 答应出现某一个具体处理者对象在承担了哀求的一部分责任后又将剩余的责任传给下家的情况,且一个哀求可以终极不被任何接收端对象所接收。在上面的else中添加部分的业务逻辑处理。
源码分析
职责链模式在 SpringMVC 框架应用中有使用。接下来就对 SpringMVC 的HandleExceptionChain 类进行分析。
上述图和下述代码说明:SpringMVC 哀求的流程图中,执行了拦截器方法 intercepto.preHandler 等等。在处理 SpringMVC 哀求时,使用到了职责链模式和适配器模式。HandlerExecutionChain 主要负责的是哀求拦截器的执行和哀求处理,但是它本身不处理哀求,只是将哀求分配给链上注册处理器执行,这是职责链的实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程。HandlerExecutionChain 维护了 HandlerInterceptor 的聚集,可以向其中注册相应的拦截器。
- public class DispatcherServlet extends FrameworkServlet {
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HandlerExecutionChain mappedHandler = null;
- //获取到 HandlerExecutionChain 对象
- mappedHandler = getHandler(processedRequest);
-
- //......
- //重点:如果执行成功则,直接返回,否则执行下一个 Handler
- if (!mappedHandler.applyPreHandle(processedRequest, response)) {
- return;
- }
-
- //上述 applyPreHandle 方法的实现
- boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
- if (getInterceptors() != null) {
- for (int i = 0; i < getInterceptors().length; i++) {
- HandlerInterceptor interceptor = getInterceptors()[i];
- //当 preHandle 链不能处理时,调用 triggerAfterCompletion 链
- if (!interceptor.preHandle(request, response, this.handler)) {
- //triggerAfterCompletion 内部也是获取所有的拦截器,并调用 afterCompletion
- triggerAfterCompletion(request, response, null);
- return false;
- }
- this.interceptorIndex = i;
- }
- }
- return true;
- }
-
- //当 handler 不符合 PreHandler 链时,执行 PostHandler 链
- mappedHandler.applyPostHandle(processedRequest, response, mv);
- void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
- for (int i = getInterceptors().length - 1; i >= 0; i--) {
- HandlerInterceptor interceptor = getInterceptors()[i];
- interceptor.postHandle(request, response, this.handler, mv);
- }
- }
-
- //上述 triggerAfterCompletion 的源码
- void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
- throws Exception {
- for (int i = this.interceptorIndex; i >= 0; i--) {
- HandlerInterceptor interceptor = getInterceptors()[i];
- try {
- interceptor.afterCompletion(request, response, this.handler, ex);
- }
- catch (Throwable ex2) {
- logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
- }
- }
- }
- }
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! 更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |