专业手机网站公司吗,纸箱 东莞网站建设,想自己做网站流程,苏州有什么好玩的目录1.访问者模式详解1.1 访问者模式的定义1.1.1 访问者模式在生活中的体现1.1.2 访问者模式的适用场景1.2 访问者模式的通用实现1.3 访问者模式的使用案例之KPI考核1.3.1 类图设计1.3.2 代码实现1.4 访问者模式扩展---分派1.4.1 java中静态分派示例代码1.4.2 java中动态分派1.…
目录1.访问者模式详解1.1 访问者模式的定义1.1.1 访问者模式在生活中的体现1.1.2 访问者模式的适用场景1.2 访问者模式的通用实现1.3 访问者模式的使用案例之KPI考核1.3.1 类图设计1.3.2 代码实现1.4 访问者模式扩展---分派1.4.1 java中静态分派示例代码1.4.2 java中动态分派1.4.3 访问者模式中伪动态双分派1.5 访问者模式在源码中应用1.5.1 jdk中FileVisitor1.5.2 spring中BeanDefinitionVisitor1.6 访问者模式的使用总结1.6.1 优缺点总结2 观察者模式详解2.1 观察者模式的定义2.1.1 观察者模式在生活场景中的应用2.1.2 观察者模式适用场景2.2 观察者模式应用案例之问答提示角标2.2.1 类图设计2.2.2 代码实现2.3 google开源组件实现观察者模式2.3.1 示例代码2.4 观察者模式应用案例之鼠标事件交互2.4.1 类图设计2.4.2 代码实现2.5 观察者模式在源码中的应用2.5.1 jdk中ServletContextListener2.5.2 spring中ContextLoaderListener2.6 观察者的使用总结2.6.1 优点总结2.6.2 缺点总结1.访问者模式详解
1.1 访问者模式的定义
定义
访问者模式【visitor Pattern】,是一种将数据结构与数据操作分离设计模式。是指
封装一些作用于某种数据结构中的各元素的操作。
特征
可以在不改变数据结构的前提下定义作用于这些元素的新操作。
属于行为型模式。
说明
访问者模式被称为最复杂的设计模式。运用并不多。
1.1.1 访问者模式在生活中的体现
1.参与KPI考核的人员
KPI的考核标准一般是固定不变的但参与KPI考核的员工会经常变化。
kpi考核打分的人也会经常变化
2.餐厅就餐人员
餐厅吃饭餐厅的菜单是基本稳定的就餐人员基本每天都在变化。就餐人员就是一个访问者。
总结
访问者好像就是变化的元素与不变的结构【标准规则】的构成关系处理角色。
1.1.2 访问者模式的适用场景
访问者模式很少能用到一旦需要使用涉及到的系统往往比较复杂。
1.数据结构稳定作用于数据结构的操作经常变化的场景。
2.需要数据结构与数据操作分离的场景。
3.需要对不同数据类型(元素) 进行操作而不使用分支判断具体类型的场景。
1.2 访问者模式的通用实现
1.3 访问者模式的使用案例之KPI考核
1.3.1 类图设计 1.3.2 代码实现
1.元素顶层接口定义
package com.oldlu.visitor.demo.kpi;import java.util.Random;/*** ClassName Employee* Description 员工元素抽象* Author oldlu* Date 2020/6/24 10:38* Version 1.0*/
public abstract class Employee {private String name;private int kpi;public Employee(String name) {this.name name;this.kpi new Random().nextInt(10);}public abstract void accept(IVisitor visitor);public String getName() {return name;}public int getKpi() {return kpi;}
}2.元素具体实现
package com.oldlu.visitor.demo.kpi;import java.util.Random;/*** ClassName Engineer* Description 普通开发人员* Author oldlu* Date 2020/6/24 10:43* Version 1.0*/
public class Engineer extends Employee{public Engineer(String name) {super(name);}Overridepublic void accept(IVisitor visitor) {visitor.visit(this);}//考核代码量public int getCodingLine(){return new Random().nextInt(100000);}}
package com.oldlu.visitor.demo.kpi;import java.util.Random;/*** ClassName Manager* Description 项目经理* Author oldlu* Date 2020/6/24 10:44* Version 1.0*/
public class Manager extends Employee {public Manager(String name) {super(name);}Overridepublic void accept(IVisitor visitor) {visitor.visit(this);}//考核每年的新产品研发数量public int getProducts(){return new Random().nextInt(10);}
}3.访问者顶层接口及实现
package com.oldlu.visitor.demo.kpi;/*** ClassName IVisitor* Description 访问者接口* Author oldlu* Date 2020/6/24 10:41* Version 1.0*/
public interface IVisitor {//传参具体的元素void visit(Engineer engineer);void visit(Manager manager);
}
package com.oldlu.visitor.demo.kpi;/*** ClassName CTOVisitor* Description ceo考核者,只有看kpi打分就行* Author oldlu* Date 2020/6/24 10:49* Version 1.0*/
public class CEOVisitor implements IVisitor{Overridepublic void visit(Engineer engineer) {System.out.println(工程师engineer.getName() ,KPIengineer.getKpi());}Overridepublic void visit(Manager manager) {System.out.println(项目经理manager.getName() ,KPImanager.getKpi());}
}
package com.oldlu.visitor.demo.kpi;/*** ClassName CTOVisitor* Description cto考核者* Author oldlu* Date 2020/6/24 10:49* Version 1.0*/
public class CTOVisitor implements IVisitor{Overridepublic void visit(Engineer engineer) {System.out.println(工程师engineer.getName() ,编写代码行数engineer.getCodingLine());}Overridepublic void visit(Manager manager) {System.out.println(项目经理manager.getName() ,产品数量manager.getProducts());}
}4.数据结构定义
package com.oldlu.visitor.demo.kpi;import java.util.LinkedList;
import java.util.List;/*** ClassName BusinessReport* Description 业务报表数据结构* Author oldlu* Date 2020/6/24 10:55* Version 1.0*/
public class BusinessReport {private ListEmployee employeeList new LinkedList();public BusinessReport() {employeeList.add(new Manager(项目经理A));employeeList.add(new Manager(项目经理B));employeeList.add(new Engineer(程序员A));employeeList.add(new Engineer(程序员B));employeeList.add(new Engineer(程序员C));}public void showReport(IVisitor visitor){for (Employee employee : employeeList) {employee.accept(visitor);}}
}5.测试代码
package com.oldlu.visitor.demo.kpi;/*** ClassName Test* Description 测试类* Author oldlu* Date 2020/6/24 10:54* Version 1.0*/
public class Test {public static void main(String[] args) {BusinessReport report new BusinessReport();System.out.println(CEO看报表);report.showReport(new CEOVisitor());System.out.println(CTO看报表);report.showReport(new CTOVisitor());}
}测试结果 说明
访问者顶层接口定义时内部会定义visit重载方法针对不同的访问元素实现子类进行重载。
为什么不设计成一个方法呢
因为这里一个方法把具体元素关联起来。
当系统需要增加元素实现子类时只需要增加一个实现子类该接口中增加一个重载方法。
系统方便扩展。
1.4 访问者模式扩展—分派
java中静态分派和动态分派。还有双分派。
Java中分派是方法重载的一种特殊形式。即重载方法方法名相同参数个数相同类型不同的形式。
1.4.1 java中静态分派示例代码
package com.oldlu.visitor.dispatch;/*** ClassName Main* Description 测试静态分派* Author oldlu* Date 2020/6/24 11:20* Version 1.0*/
public class Main {public static void main(String[] args) {String str 1;Integer integer 1;Main main new Main();main.test(integer);main.test(str);}public void test(String str){System.out.println(String str);}public void test(Integer integer){System.out.println(Integer integer);}
}说明
上面测试代码中test方法存在两个重载方法参数个数相同类型不同
在编译阶段就能清楚地知道参数类型称为静态分派。
相同方法名不同类型的不同方法这种形式也称为多分派。
1.4.2 java中动态分派
在程序编译阶段不能明确是哪种类型只有在运行时才能知道是哪个类型
称为动态分派。
1.定义接口及实现
package com.oldlu.visitor.dispatch.dynamic;/*** ClassName Person* Description 接口定义* Author oldlu* Date 2020/6/24 11:33* Version 1.0*/
public interface Person {void test();
}
package com.oldlu.visitor.dispatch.dynamic;/*** ClassName Women* Description 女人* Author oldlu* Date 2020/6/24 11:35* Version 1.0*/
public class Women implements Person{Overridepublic void test() {System.out.println(女人);}
}
package com.oldlu.visitor.dispatch.dynamic;/*** ClassName Man* Description 男人* Author oldlu* Date 2020/6/24 11:34* Version 1.0*/
public class Man implements Person {Overridepublic void test() {System.out.println(男人);}
}2.测试类
package com.oldlu.visitor.dispatch.dynamic;/*** ClassName Main* Description 测试类* Author oldlu* Date 2020/6/24 11:35* Version 1.0*/
public class Main {public static void main(String[] args) {Person man new Man();Person women new Women();man.test();women.test();}
}说明
当在编译期时man或women并不知道自己是什么类型只有在运行期
通过new创建实例时才能知道具体的类型所以是动态分派。
1.4.3 访问者模式中伪动态双分派
在数据结构中一般会对集合元素进行遍历处理。如下所示 可以看到这里是一次动态分派调用accept方法具体的类型要到运行时才能确定。
而且Employee也是一个抽象接口类型不能确定。 当进入到一个子类中如Engineer,accept方法调用visit方法传参this,也是动态分派。需要到
运行时才能确定类型。【因为需要在运行时创建this实例】 1.5 访问者模式在源码中应用
1.5.1 jdk中FileVisitor
FileVisitResult visitFile(T file, BasicFileAttributes attrs)throws IOException;FileVisitor接口中定义visitFile方法。传参BasicFileAtributes也是一个接口。
1.5.2 spring中BeanDefinitionVisitor
public void visitBeanDefinition(BeanDefinition beanDefinition) {visitParentName(beanDefinition);visitBeanClassName(beanDefinition);visitFactoryBeanName(beanDefinition);visitFactoryMethodName(beanDefinition);visitScope(beanDefinition);if (beanDefinition.hasPropertyValues()) {visitPropertyValues(beanDefinition.getPropertyValues());}if (beanDefinition.hasConstructorArgumentValues()) {ConstructorArgumentValues cas beanDefinition.getConstructorArgumentValues();visitIndexedArgumentValues(cas.getIndexedArgumentValues());visitGenericArgumentValues(cas.getGenericArgumentValues());}}访问时并未改变其中的内容只是返回相应结果。把数据操作与结构进行分离。
1.6 访问者模式的使用总结
1.6.1 优缺点总结
优点
1.解耦数据结构与数据操作使用操作集合可以独立变化
2.扩展性好可以通过扩展访问者角色实现对数据集的不同操作
3.元素具体类型并非单一访问者均可操作
4.各角色职责分离符合单一职责原则。
缺点
1.无法增加元素类型若系统数据结构对象易于变化经常有新的数据对象增加进来
则访问者类必须增加对应元素的操作违背开闭原则。
2.具体元素变更困难具体元素的增加属性删除属性等操作会导致对应访问者类需要
相应的修改尤其有大量访问者类时修改范围太大。
3.违背依赖倒置原则为了达到”区别对待“访问者依赖的是具体元素类型而不是抽象。
2 观察者模式详解
2.1 观察者模式的定义
定义
观察者模式【Observer Pattern】,又叫发布-订阅【Publish/Subcribe】模式模型-视图【Model/View】模式、
源监听器【Source/Listener】模式从属者【Dependents】模式。
定义一种一对多的关系一个主题对象可被多个观察者对象同时监听使得每当主题对象状态变化时所有
依赖它的对象都会得到通知并被自动更新。
属于行为型模式。
2.1.1 观察者模式在生活场景中的应用
1.App角标通知
2.起床闹钟设置
2.1.2 观察者模式适用场景
1.当一个抽象模型包含两个方面内容其中一个方面依赖于另一个方面。
2.其他一个或多个对象的变化依赖于另一个对象的变化。
3.实现类似广播机制的功能无需知道具体收听者只需分发广播系统中感兴趣
的对象会自动接收该广播。
4.多层嵌套使用形成一种链式触发机制使得事件具备跨域跨越两种观察者类型通知。
2.2 观察者模式应用案例之问答提示角标
在学习社区我们有疑问可以发布问题求助。发布问题时可以邀请某个人【或某个老师】进行
解答。
但是老师平时会很忙不会总是去刷新页面。于是就会做一个通知功能。
一旦有一个问题向老师提出通知图标上就会数字加1.
当老师登录到页面时只要查看通知角标就能知道是否有人向他提问就方便回答。
2.2.1 类图设计 2.2.2 代码实现
说明这里是基于jdk的发布-订阅api实现。
1.被观察者定义
package com.oldlu.observer.demo.gper;import java.util.Observable;/*** ClassName GPer* Description 社区生态圈被观察者* Author oldlu* Date 2020/6/24 17:31* Version 1.0*/
public class GPer extends Observable {private String name GPer 生态圈;public String getName() {return name;}private static final GPer gper new GPer();private GPer() {}public static GPer getInstance(){return gper;}public void publishQuestion(Question question){System.out.println(question.getUserName() 在 this.name 提交了一个问题);//调用jdk apisetChanged();notifyObservers(question);}
}2.数据结构问题类
package com.oldlu.observer.demo.gper;/*** ClassName Question* Description 问题* Author oldlu* Date 2020/6/24 17:34* Version 1.0*/
public class Question {//问题发布者private String userName;//内容private String content;public void setUserName(String userName) {this.userName userName;}public void setContent(String content) {this.content content;}public String getUserName() {return userName;}public String getContent() {return content;}
}3.观察者定义
package com.oldlu.observer.demo.gper;import java.util.Observable;
import java.util.Observer;/*** ClassName Teacher* Description 观察者* Author oldlu* Date 2020/6/24 17:38* Version 1.0*/
public class Teacher implements Observer {private String name;public Teacher(String name) {this.name name;}Overridepublic void update(Observable ob, Object arg) {GPer gper (GPer) ob;Question question (Question) arg;System.out.println();System.out.println(name老师你好\n 您收到一个来自gper.getName()的提问希望你解答问题内容如下\nquestion.getContent()\n提问者question.getUserName());}
}4.测试类
package com.oldlu.observer.demo.gper;import javax.management.Query;/*** ClassName Test* Description 测试类* Author oldlu* Date 2020/6/24 17:44* Version 1.0*/
public class Test {public static void main(String[] args) {GPer gper GPer.getInstance();Teacher tom new Teacher(tom);Teacher jerry new Teacher(Jerry);gper.addObserver(tom);gper.addObserver(jerry);//用户行为Question question new Question();question.setUserName(张三);question.setContent(观察者模式适用于哪些场景);gper.publishQuestion(question);}
}2.3 google开源组件实现观察者模式
依赖包
dependencygroupIdcom.google.guava/groupIdartifactIdguava/artifactIdversion18.0/version/dependency2.3.1 示例代码
1.观察者类
package com.oldlu.observer.guava;import com.google.common.eventbus.Subscribe;/*** ClassName GuavaEvent* Description 观察者* Author oldlu* Date 2020/6/24 18:07* Version 1.0*/
public class GuavaEvent {//表示观察者回调Subscribepublic void observer(String str){System.out.println(执行observer方法传参为str);}
}2.测试类
package com.oldlu.observer.guava;import com.google.common.eventbus.EventBus;/*** ClassName Test* Description 测试类* Author oldlu* Date 2020/6/24 18:09* Version 1.0*/
public class Test {public static void main(String[] args) {EventBus eventBus new EventBus();GuavaEvent event new GuavaEvent();eventBus.register(event);eventBus.post(tom);}
}测试结果
2.4 观察者模式应用案例之鼠标事件交互
当鼠标发起某个动作【如单击移动滚动。。。】得到相应的响应。
针对发出动作需要进行监听【现实中操作系统进行监听】
鼠标当成被观察者实现。
事件监听器监听动作【触发一个事件】
业务类事件类【传递参数区分不同事件或动作】
事件回调监听后需要作出响应进行回调。充当观察者。
2.4.1 类图设计 2.4.2 代码实现
1.事件监听器
package com.oldlu.observer.mouseclick.core;import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;/*** ClassName EventListener* Description 事件监听器被观察者的抽象* Author oldlu* Date 2020/6/24 18:21* Version 1.0*/
public class EventListener {protected MapString, Event events new HashMapString,Event();public void addListener(String eventType, Object target, Method callback){events.put(eventType,new Event(target,callback));}public void addListener(String eventType, Object target){try{this.addListener(eventType,target,target.getClass().getMethod(on toUpperFirstCase(eventType),Event.class));}catch (Exception e){e.printStackTrace();}}private String toUpperFirstCase(String eventType) {char [] chars eventType.toCharArray();if(chars[0] a chars[0] z){chars[0] - 32;}return String.valueOf(chars);}private void trigger(Event event){event.setSource(this);event.setTime(System.currentTimeMillis());try{if(null ! event.getCallback()){//反射调用 回调函数event.getCallback().invoke(event.getTarget(),event);}}catch (Exception e){e.printStackTrace();}}protected void trigger(String trigger){if(!this.events.containsKey(trigger)){return;}//如果已进行注册回调trigger(this.events.get(trigger).setTrigger(trigger));}
}2.被监听对象鼠标类
package com.oldlu.observer.mouseclick.event;import com.oldlu.observer.mouseclick.core.EventListener;/*** ClassName Mouse* Description 具体的被观察者* Author oldlu* Date 2020/6/24 18:21* Version 1.0*/
public class Mouse extends EventListener {public void click() {System.out.println(调用单机方法);this.trigger(MouseEventType.ON_CLICK);}
}3.事件回调
package com.oldlu.observer.mouseclick.event;import com.oldlu.observer.mouseclick.core.Event;/*** ClassName MouseCallback* Description 事件响应回调观察者* Author oldlu* Date 2020/6/24 18:22* Version 1.0*/
public class MouseEventCallback {public void onClick(Event event){System.out.println(触发鼠标单击事件\nevent);}public void onMove(Event event){System.out.println(触发鼠标双击事件);}}4.传参bean,事件类
package com.oldlu.observer.mouseclick.core;import java.lang.reflect.Method;/*** ClassName Event* Description 事件抽象传参对象* Author oldlu* Date 2020/6/24 18:22* Version 1.0*/
public class Event {//事件源,如鼠标键盘private Object source;//事件触发要通知谁观察者private Object target;private Method callback;//事件名称private String trigger;//事件触发时间private Long time;public Event(Object target, Method callback) {this.target target;this.callback callback;}public Object getSource() {return source;}public Event setSource(Object source) {this.source source;return this;}public Object getTarget() {return target;}public Event setTarget(Object target) {this.target target;return this;}public Method getCallback() {return callback;}public Event setCallback(Method callback) {this.callback callback;return this;}public String getTrigger() {return trigger;}public Event setTrigger(String trigger) {this.trigger trigger;return this;}public Long getTime() {return time;}public Event setTime(Long time) {this.time time;return this;}Overridepublic String toString() {return Event{ source source , target target , callback callback , trigger trigger \ , time time };}
}5.事件类型常量定义
package com.oldlu.observer.mouseclick.event;/*** ClassName MouseEventType* Description 鼠标事件* Author oldlu* Date 2023/2/19 10:47* Version 1.0*/
public interface MouseEventType {String ON_CLICK click;
}6.测试类
package com.oldlu.observer.mouseclick;import com.oldlu.observer.mouseclick.event.Mouse;
import com.oldlu.observer.mouseclick.event.MouseEventCallback;
import com.oldlu.observer.mouseclick.event.MouseEventType;/*** ClassName Test* Description 测试类* Author oldlu* Date 2023/2/19 11:15* Version 1.0*/
public class Test {public static void main(String[] args) {MouseEventCallback callback new MouseEventCallback();Mouse mouse new Mouse();mouse.addListener(MouseEventType.ON_CLICK,callback);mouse.click();}
}测试结果如下 2.5 观察者模式在源码中的应用
2.5.1 jdk中ServletContextListener
实际上观察者模式的典型提示*Listener【监听器】
public interface ServletContextListener extends EventListener {public void contextInitialized ( ServletContextEvent sce );public void contextDestroyed ( ServletContextEvent sce );
}2.5.2 spring中ContextLoaderListener
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {public ContextLoaderListener() {}public ContextLoaderListener(WebApplicationContext context) {super(context);}public void contextInitialized(ServletContextEvent event) {this.initWebApplicationContext(event.getServletContext());}public void contextDestroyed(ServletContextEvent event) {this.closeWebApplicationContext(event.getServletContext());ContextCleanupListener.cleanupAttributes(event.getServletContext());}
}2.6 观察者的使用总结
2.6.1 优点总结
1.观察者与被观察者是松耦合的符合依赖倒置原则。
2.分离表示层【观察者】和数据逻辑层【被观察者】并且建立了一套触发机制使得数据的变化可以
响应到多个表示层上。
3.实现一对多的通讯机制支持事件注册机制支持兴趣分发机制当被观察者触发事件时只有感兴趣
的观察者可以接收到通知。
2.6.2 缺点总结
1.如果观察者数量过多则事件通知会耗时较长。
2.事件通知呈线性关系如果其中一个观察者处理事件卡壳会影响后续的观察者接收该事件。
3.如果观察者和被观察者之间存在循环依赖则可能造成两者之间循环调用导致系统崩溃。