一、背景介绍
早晨上班米老师需要何老师打开水麒麟的门
通过面向对象的方式,来进行需求的拆解和方案的设计以及代码的实现
前提:米老师和何老师所在的公司背景是一个大企业,有着良好的职责分配和制度管理
二、思路&方案
- 1.面向过程:
何老师告诉米老师钥匙在哪儿
米老师到壹佰之后去拿上钥匙开门
- 2.面向对象(抽象、封装、继承、多态):
何老师管钥匙开门,这个前提米老师知道
米老师到壹佰之后喊一声何老师打开水麒麟的门
何老师听到消息之后就过来开门了
三、过程
场景要求
1.喊话人抽象出来;开门人抽象出来,将钥匙属性、和开门的方法封装到开门人类中(抽象、封装);
2.喊话人和开门人通过事件和委托传递消息(解耦合)
3.需要谁去开门就new出来具体开门人,节省资源,动态扩充(继承+工厂+反射)
实现思路&场景
- 1.面向过程实现
- 2.面向对象实现(抽象、封装;消息监听,事件和委托)
- 3.适配多场景(直接调用、观察者模式、事件委托、依赖消息体通道)
- 。3.1.一个喊话人(喊话人有权限),一个拿钥匙开门的人:
米老师知道何老师拿着水麒麟钥匙——>米老师大喊何老师给我开一下水麒麟的门——>何老师听到消息并且拿着水麒麟钥匙过来开门
- 。3.2.一个喊话人(喊话人有权限),多个拿钥匙开门的人(每个人负责不同的门):
米老师大喊给我开一下水麒麟的门——>此时听到消息并且拿着水麒麟钥匙的人就会过来开门
米老师大喊给我开一下金鹰的门——>此时听到消息并且拿着金鹰钥匙的人就会过来开门
- 。3.3.一个喊话人(喊话人有权限),多个拿钥匙开门的人(多个人负责相同的门):
米老师大喊给我开一下水麒麟的门——>此时听到消息并且拿着水麒麟钥匙的人就会过来开门——>开完门之后,要通知其他有水麒麟门钥匙的人门已经开了,不用过来了
- 。3.4.多个喊话人(喊话人权限不相同),一个拿钥匙开门的人:
3.4.1.开门的人是否有权限喊?
3.4.2.拿钥匙的人判断喊的人是否有权限进这个门
- 。3.5.多个喊话人(喊话人权限不相同),多个拿钥匙开门的人(每个人负责不同的门):
- 。3.6.多个喊话人(喊话人权限不相同),多个拿钥匙开门的人(多个人负责相同的门):
实现代码
观察者模式实现
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v2; /** * 放钥匙的地方类 */ public class Local { Local (long longitude,long latitude){ this.longitude = longitude; this.latitude = latitude; } //经度 long longitude; //纬度 long latitude; public long getLongitude() { return longitude; } public void setLongitude(long longitude) { this.longitude = longitude; } public long getLatitude() { return latitude; } public void setLatitude(long latitude) { this.latitude = latitude; } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v2; /** * 钥匙类 */ public class Key { public Key(long id, String name, Local local){ this.id = id; this.name = name; this.local = local; } //钥匙标识 Long id; //钥匙名字 String name; Local local; public String getName() { return name; } public void setName(String name) { this.name = name; } public Local getLocal() { return local; } public void setLocal(Local local) { this.local = local; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v2; /** * 锁类 */ public class Lock { public Lock(long id, String name, Local local){ this.id = id; this.name = name; this.local = local; } //锁的标识 Long id; //锁的名字 String name; //锁的地点 Local local; public String getName() { return name; } public void setName(String name) { this.name = name; } public Local getLocal() { return local; } public void setLocal(Local local) { this.local = local; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v2; public abstract class Observer { abstract void update(); }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v2; /** * 用户类 */ public class UserObserver extends Observer { UserObserver(UserSubject userSubject,String name,Key key,Lock lock){ this.userSubject = userSubject; this.name = name; this.key = key; this.lock = lock; } //名字 String name; //观察者 UserSubject userSubject; Key key; Lock lock; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override void update() { if(key.getId() == userSubject.getLock().getId()){ System.out.println(name+"用经度"+key.getLocal().getLongitude()+"纬度"+key.getLocal().getLatitude()+ "的"+key.getName()+"钥匙," + "打开了经度"+lock.getLocal().getLongitude()+"纬度"+lock.getLocal().getLatitude()+ "的"+lock.getName()+"的门"); } } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v2; import java.util.ArrayList; import java.util.List; public class Subject { private List<Observer> list = new ArrayList<>(); public void add(Observer observer){ list.add(observer); } public void delete(Observer observer){ list.remove(observer); } public void Notify(){ for (Observer o:list) { o.update(); } } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v2; /** * 用户类 */ public class UserSubject extends Subject { UserSubject(String name){ this.name = name; } //名字 private String name; private Lock lock; public String getName() { return name; } public Lock getLock() { return lock; } public void setLock(Lock lock) { this.lock = lock; } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v2; public class Client { public static void main(String[] args) { //创建一个锁和钥匙-观察者模式实现 Key key1 = new Key(1,"米老师门钥匙",new Local(11,11)); Lock lock1 = new Lock(1,"米老师门锁",new Local(1,1)); Key key2 = new Key(2,"金鹰门钥匙",new Local(22,22)); Lock lock2 = new Lock(2,"金鹰门锁",new Local(2,2)); //第一种情况,米老师调用何老师,让何老师开门 System.out.println("第一种情况,米老师广播打开 1号门"); UserSubject milaoshi = new UserSubject("米老师"); milaoshi.add(new UserObserver(milaoshi,"何老师",key1,lock1)); milaoshi.add(new UserObserver(milaoshi,"郭老师",key2,lock2)); milaoshi.setLock(lock2); milaoshi.Notify(); } }
执行结果:
事件委托方式实现
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; import java.lang.reflect.Method; /** * 事件类 */ public class Event { //要执行方法的对象 private Object object; //要执行的方法名称 private String methodName; //要执行方法的参数 private Object[] params; //要执行方法的参数类型 private Class[] paramTypes; public Event(){ } public Event(Object object,String methodName,Object...args){ this.object=object; this.methodName=methodName; this.params=args; contractParamTypes(this.params); } //根据参数数组生成参数类型数组 private void contractParamTypes(Object[] params){ this.paramTypes=new Class[params.length]; for(int i=0;i<params.length;i++){ this.paramTypes[i]=params[i].getClass(); } } public void invoke() throws Exception{ Method method=object.getClass().getMethod(this.getMethodName(), this.getParamTypes()); if(null==method){ return; } method.invoke(this.getObject(), this.getParams()); } public Object getObject() { return object; } public void setObject(Object object) { this.object = object; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Object[] getParams() { return params; } public void setParams(Object[] params) { this.params = params; } public Class[] getParamTypes() { return paramTypes; } public void setParamTypes(Class[] paramTypes) { this.paramTypes = paramTypes; } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; import java.util.ArrayList; import java.util.List; public class EventHandler { //是用一个List private List<Event> objects; public EventHandler(){ objects=new ArrayList<Event>(); } //添加某个对象要执行的事件,及需要的参数 public void addEvent(Object object,String methodName,Object...args){ objects.add(new Event(object,methodName,args)); } //通知所有的对象执行指定的事件 public void notifyX() throws Exception{ for(Event e : objects){ e.invoke(); } } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; /** * 放钥匙的地方类 */ public class Local { Local (long longitude,long latitude){ this.longitude = longitude; this.latitude = latitude; } //经度 long longitude; //纬度 long latitude; public long getLongitude() { return longitude; } public void setLongitude(long longitude) { this.longitude = longitude; } public long getLatitude() { return latitude; } public void setLatitude(long latitude) { this.latitude = latitude; } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; /** * 钥匙类 */ public class Key { public Key(long id, String name, Local local){ this.id = id; this.name = name; this.local = local; } //钥匙标识 private Long id; //钥匙名字 private String name; private Local local; public String getName() { return name; } public void setName(String name) { this.name = name; } public Local getLocal() { return local; } public void setLocal(Local local) { this.local = local; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; /** * 锁类 */ public class Lock { public Lock(long id, String name, Local local){ this.id = id; this.name = name; this.local = local; } //锁的标识 private Long id; //锁的名字 private String name; //锁的地点 private Local local; public String getName() { return name; } public void setName(String name) { this.name = name; } public Local getLocal() { return local; } public void setLocal(Local local) { this.local = local; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; public abstract class Notifier { private EventHandler eventHandler=new EventHandler(); public EventHandler getEventHandler() { return eventHandler; } public void setEventHandler(EventHandler eventHandler) { this.eventHandler = eventHandler; } //增加需要帮忙放哨的学生 public abstract void addListener(Object object,String methodName,Object...args); //告诉所有要帮忙放哨的学生:老师来了 public abstract void notifyX(); }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; public class MilaoshiNotifier extends Notifier{ @Override public void addListener(Object object, String methodName, Object... args) { this.getEventHandler().addEvent(object,methodName,args); } @Override public void notifyX() { try { this.getEventHandler().notifyX(); } catch (Exception e) { e.printStackTrace(); } } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; /** * 用户类 */ public class User { User(String name){ this.name = name; for (Map map:MapData.getMapList()) { if(map.containsKey(name) && (boolean)map.get(name)){ keys.add((Key)map.get("key")); locks.add((Lock) map.get("lock")); } } System.out.println(name+"开始监听"); } //名字 private String name; private List<Key> keys = new ArrayList<>(); private List<Lock> locks = new ArrayList<>(); public void openDoor(Date date){ for (Lock lock :locks) { for (Key key:keys) { if(lock.getId() == key.getId()){ System.out.println(name+"用经度"+key.getLocal().getLongitude()+"纬度"+key.getLocal().getLatitude()+ "的"+key.getName()+"钥匙," + "打开了经度"+lock.getLocal().getLongitude()+"纬度"+lock.getLocal().getLatitude()+ "的"+lock.getName()+"的门"); } } } } public String getName() { return name; } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; import java.util.*; public class MapData { private static List<Map> mapList = new ArrayList<>(); static { for (int i = 0; i < 10; i++) { Map<String, Object> map = new HashMap<>(); Key key = new Key(i,i+"号钥匙",new Local(i,i)); map.put("key",key); Lock lock = new Lock(i,i+"号锁",new Local(i,i)); map.put("lock",lock); if(i>5){ map.put("guolaoshi",true); }else { map.put("helaoshi",true); } mapList.add(map); } } public static List<Map> getMapList() { return mapList; } public static void addMapList(Map map) { mapList.add(map); } public static void delMapList(Map map) { mapList.remove(map); } }
package com.b0019拿钥匙开门的小例子.b0019拿钥匙的小例子v4; import java.util.Date; public class Client { public static void main(String[] args) { //事件和委托方式实现 //实例化开门人helaoshi User helaoshi = new User("helaoshi"); //实例化开门人guolaoshi User guolaoshi = new User("guolaoshi"); //实例化一个喊话人 Notifier milaoshiNotifier = new MilaoshiNotifier(); milaoshiNotifier.addListener(helaoshi,"openDoor",new Date()); milaoshiNotifier.addListener(guolaoshi,"openDoor",new Date()); System.out.println("喊话人,大喊开门"); milaoshiNotifier.notifyX(); } }
执行结果:
四、总结
- 1.通过一个开门小例子,来进行需求分析、场景设计以及在设计中考虑符合软件工程(可复用、可扩充、可维护)的设计思路
- 2.通过开门小例子来理解面向过程和面向对象的设计理念;它不是非黑既白的过程,它是需要考虑在什么场景下我们更适合用什么方式
- 3.事件和委托的方式与观察者模式的不同在于,事件和委托对于监听者最终是通过什么方法进行的接收消息的处理更加灵活,不再像观察者模式必须统一一个方法
- 。3.1.举一个例子吧:A类要通知B类的z方法和C类的x方法;在这种场景下,使用事件和委托将显得得心应手
五、升华
- 1.面向对象和面向过程的边界?考虑问题的边界?
- 2.针对于各种场景的实现,有了这次思路的刻意训练,以及边界的明确,后续的实现将变得如此简单
- 3.现实生活vs计算机中的面向过程和面向对象理解更加透彻了
针对于两个示例的client端中对于手工实例化的方式可以使用工厂+反射来动态实现,这里就留给读者一个思考~