经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
设计模式之观察者模式
来源:cnblogs  作者:Ericyshi  时间:2018/9/25 20:11:32  对本文有异议

工作中我们经常会出现这样一种场景:在工作不忙的时候,老板出去办事了,然后很多员工就在工位上开始忙自己的事,比如看股票,看视频,看小说,吃零食。前台员工充当哨兵,只要老板一来,就马上通知员工们。

第一版,双向耦合代码实现:

  1. //秘书充当前哨
  2. public class Secretary {
  3. private List<StockObserver> observers = new ArrayList<StockObserver>();
  4. private String action;
  5. public void attach(StockObserver observer){
  6. observers.add(observer);
  7. }
  8. public void notifyObservers(){
  9. for(StockObserver so : observers){
  10. so.update();
  11. }
  12. }
  13. public String getAction() {
  14. return action;
  15. }
  16. public void setAction(String action) {
  17. this.action = action;
  18. }
  19. }
  1. public class StockObserver {
  2. private String name;
  3. private Secretary sub;
  4. public StockObserver(String name, Secretary sub){
  5. this.name = name;
  6. this.sub = sub;
  7. }
  8. public void update(){
  9. System.out.println(sub.getAction() + " " + name + " 关闭股票行情,继续工作!");
  10. }
  11. }
  1. public class Test {
  2. public static void main(String[] args) {
  3. //前台人员
  4. Secretary xxx = new Secretary();
  5. //看股票的同事
  6. StockObserver yyy = new StockObserver("xiaoming", xxx);
  7. StockObserver zzz = new StockObserver("xiaohua", xxx);
  8. //前台记下两位同事
  9. xxx.attach(zzz);
  10. xxx.attach(yyy);
  11. xxx.setAction("老板来了");
  12. xxx.notifyObservers();
  13. }
  14. }

我们发现“前台”类与“看股票者”是双向耦合的,如果观察者中还有想看NBA直播、看电影的,那“前台”类就得改动。这违反了开放-封闭原则,也违背了依赖倒转原则,我们应该让程序都依赖抽象,而不是互相依赖。我们发现,老板也好,前台也好,秘书也好,都是具体的通知者。这里观察者也不应该依赖具体的实现,而是一个抽象的通知者。

第二版,解耦实践:

  1. //通知者接口
  2. public interface Subject {
  3. void attach(Observer observer);
  4. void detach(Observer observer);
  5. void notifyObservers();
  6. String getSubjectState();
  7. void setSubjectState(String action);
  8. }
  1. public class Boss implements Subject{
  2. private List<Observer> observers = new ArrayList<Observer>();
  3. private String action;
  4. @Override
  5. public void attach(Observer observer) {
  6. observers.add(observer);
  7. }
  8. @Override
  9. public void detach(Observer observer) {
  10. observers.remove(observer);
  11. }
  12. @Override
  13. public void notifyObservers() {
  14. for(Observer o : observers){
  15. o.update();
  16. }
  17. }
  18. @Override
  19. public String getSubjectState() {
  20. return this.getAction();
  21. }
  22. public String getAction() {
  23. return action;
  24. }
  25. public void setAction(String action) {
  26. this.action = action;
  27. }
  28. @Override
  29. public void setSubjectState(String action) {
  30. this.setAction(action);
  31. }
  32. }

前台类与老板类类似,略。

  1. public abstract class Observer {
  2. protected String name;
  3. protected Subject sub;
  4. public Observer(String name, Subject sub){
  5. this.name = name;
  6. this.sub = sub;
  7. }
  8. public abstract void update();
  9. }
  1. public class StockObserver1 extends Observer{
  2. public StockObserver1(String name, Subject sub){
  3. super(name, sub);
  4. }
  5. public void update(){
  6. System.out.println(sub.getSubjectState() + " " + name + " 关闭股票行情,继续工作!");
  7. }
  8. }
  1. public class NBAObserver extends Observer{
  2. public NBAObserver(String name, Subject sub){
  3. super(name, sub);
  4. }
  5. public void update(){
  6. System.out.println(sub.getSubjectState() + " " + name + " 关闭NBA直播,继续工作!");
  7. }
  8. }
  1. public class Test2 {
  2. public static void main(String[] args) {
  3. //前台人员
  4. Subject boss = new Boss();
  5. //看股票的同事
  6. Observer yyy = new StockObserver1("xiaoming", boss);
  7. Observer zzz = new NBAObserver("xiaohua", boss);
  8. //前台记下两位同事
  9. boss.attach(zzz);
  10. boss.attach(yyy);
  11. boss.detach(yyy);
  12. boss.setSubjectState("你们的boss我回来了");
  13. boss.notifyObservers();
  14. }
  15. }

这就是改进版的观察者模式。

观察者模式:也叫做发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有的观察者,使他们能够自动地更新自己。

观察者模式特点:

将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是要维护相关对象间的一致性。这个给维护、扩展、重用都带来了不便。而观察者模式解决了这个不便。那什么时候该用此模式呢?当一个对象的改变需要同时改变其他对象的时候。总的来讲,观察者模式所做的工作就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖具体,从而使得各自的变化都不会影响到另一边的变化。该实现方法也有不足之处,尽管已经使用了依赖倒转原则,但“抽象通知者”还是依赖“抽象观察者”,也就是说万一没了抽象观察者这样的接口,这通知功能就完不成了。另外就是每个具体的观察者,它不一定是“更新”的方法要调用,而是其他不同名的方法。

第三版,事件委托实现

  1. public class Event {
  2. //要执行方法的对象
  3. private Object object;
  4. //要执行的方法名称
  5. private String methodName;
  6. //要执行方法的参数
  7. private Object[] params;
  8. //要执行方法的参数类型
  9. private Class[] paramTypes;
  10. public Event(){
  11. }
  12. public Event(Object object, String methodName, Object...args){
  13. this.object = object;
  14. this.methodName = methodName;
  15. this.params = args;
  16. contractParamTypes(this.params);
  17. }
  18. //根据参数数组生成参数类型数组
  19. private void contractParamTypes(Object[] params){
  20. this.paramTypes = new Class[params.length];
  21. for(int i = 0; i < params.length; i++){
  22. this.paramTypes[i] = params[i].getClass();
  23. }
  24. }
  25. public Object getObject() {
  26. return object;
  27. }
  28. public void setObject(Object object) {
  29. this.object = object;
  30. }
  31. public String getMethodName() {
  32. return methodName;
  33. }
  34. public void setMethodName(String methodName) {
  35. this.methodName = methodName;
  36. }
  37. public Object[] getParams() {
  38. return params;
  39. }
  40. public void setParams(Object[] params) {
  41. this.params = params;
  42. }
  43. public Class[] getParamTypes() {
  44. return paramTypes;
  45. }
  46. public void setParamTypes(Class[] paramTypes) {
  47. this.paramTypes = paramTypes;
  48. }
  49. //执行该对象的该方法
  50. public void invoke() throws Exception{
  51. String name = this.getMethodName();
  52. Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
  53. if(null == method){
  54. return;
  55. }
  56. method.invoke(this.getObject(), this.getParams());
  57. }
  58. }
  1. public class EventHandler {
  2. private List<Event> objects;
  3. public EventHandler(){
  4. objects = new ArrayList<Event>();
  5. }
  6. //添加某个对象要执行的事件及需要的参数
  7. public void addEvent(Object object, String methodName, Object...args){
  8. objects.add(new Event(object, methodName, args));
  9. }
  10. //通知所有的对象执行指定的事件
  11. public void notifyX() throws Exception{
  12. for(Event e : objects){
  13. e.invoke();
  14. }
  15. }
  16. }
  1. public abstract class Notifier {
  2. private EventHandler eventHandler = new EventHandler();
  3. //增加需要帮忙放哨的学生
  4. public abstract void addListener(Object object, String methodName, Object...args);
  5. public abstract void notifyX();
  6. public EventHandler getEventHandler() {
  7. return eventHandler;
  8. }
  9. public void setEventHandler(EventHandler eventHandler) {
  10. this.eventHandler = eventHandler;
  11. }
  12. }
  1. public class GoodNotifier extends Notifier{
  2. @Override
  3. public void addListener(Object object, String methodName, Object... args) {
  4. System.out.println("有新的同学委托尽职尽责的放哨人");
  5. this.getEventHandler().addEvent(object, methodName, args);
  6. }
  7. @Override
  8. public void notifyX() {
  9. System.out.println("尽职尽责的放哨人告诉所有需要帮忙的同学:老师来了");
  10. try {
  11. this.getEventHandler().notifyX();
  12. } catch (Exception e) {
  13. // TODO Auto-generated catch block
  14. e.printStackTrace();
  15. }
  16. }
  17. }
  1. public class PlayingGameListener {
  2. public PlayingGameListener(){
  3. System.out.println("我正在玩游戏 开始时间:" + new Date());
  4. }
  5. public void stopPlayingGame(Date date){
  6. System.out.println("老师来了,快回到座位上,结束时间:" + date);
  7. }
  8. }
  1. public class WatchingTVListener {
  2. public WatchingTVListener(){
  3. System.out.println("我正在看电视开始时间:" + new Date());
  4. }
  5. public void stopWatchingTV(Date date){
  6. System.out.println("老师来了,快关闭电视,结束时间:" + date);
  7. }
  8. }
  1. public class Test {
  2. public static void main(String[] args){
  3. Notifier goodNotifier = new GoodNotifier();
  4. PlayingGameListener playingGameListener = new PlayingGameListener();
  5. WatchingTVListener watchingTVListener = new WatchingTVListener();
  6. goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
  7. goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
  8. try {
  9. Thread.sleep(1000);
  10. } catch (InterruptedException e) {
  11. // TODO Auto-generated catch block
  12. e.printStackTrace();
  13. }
  14. goodNotifier.notifyX();
  15. }
  16. }

现在可以解释一下委托是什么了。委托就是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法可以像其他任何方法一样,具有参数和返回值,委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。new EventHandler()其实就是创建了一个委托实例,一个委托可以搭载多个方法,所有方法被依次唤起,它可以使委托对象所搭载的方法并不需要属于同一个类。但委托也是有前提的,那就是委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号