浅析java设计模式(一)----异构容器,可以存储任何对象类型为其他类提供该对象
最近在着手重构一个java UI桌面项目,发现这个项目在一开始的时候由于需求不明确,以及开发人员对swing框架不熟悉等问题造成了页面代码混乱的情况:为了能够在各个类里都可以拿到其他类的引用去进行相应的页面响应操作,在每一个类的构造方法中都传入了主类的引用,在主类中提供了所有类的get()方法,这样的做法显得十分的臃肿,就像这样:
打开主页面后会显示窗体B,窗体B的按钮支持我们打开窗体A,窗体A按钮支持修改B中属性.我们只能通过在主页面的类中使用get(),set()方法来持有A和B的引用,在A和B的构造方法中提供主页面的引用,从而才能做到在B中调用A,A中调用B.但是这样的做法随着项目的开展主页面的类中get()和set()方法的数量将多到你无法想象,那么是否可以提供一个容器,在创建页面时就将该页面对象存入该容器中,其他页面只需通过这个容器来获取其他页面窗体进行操作?
废话不多说我们开始干活,由于不同的页面类型即其类可能不同所以我们提供的容器需要是Object的:
1 public class ClassContainerOne { 2 private static Map<String, Object> container = new HashMap<>(); 3 4 public static void addClass(String name,Object value){ 5 container.put(name,value); 6 } 7 public static Object getClass(String name){ 8 return container.get(name); 9 } 10 }
简单的封装一下我们就可以正常使用,这样的操作,我们只能依靠String来区分对象并且自己来完成强制类型转换:
1 public class Test { 2 public static void main(String[] args) { 3 Teacher teacher = new Teacher("A老师"); 4 Student student = new Student("B学生"); 5 ClassContainerOne.addClass("teacher",teacher); 6 ClassContainerOne.addClass("student",student); 7 Teacher teacher1 = (Teacher) ClassContainerOne.getClass("teacher"); 8 Student student1 = (Student)ClassContainerOne.getClass("student"); 9 System.out.println(teacher1 + " " + student1); 10 } 11 }
我想大家都会想到一个问题,那就是这样的操作安全吗?显然是否定的,一旦我们强制转换错误,那系统就会崩溃,因此我们用泛型来修改完善我们的容器类:
1 public class ClassContainerTwo { 2 private static Map<Class<?>, Object> container = new HashMap<>(); 3 4 public static <T> void addClass(Class<T> valueType,T value) { 5 container.put(valueType, value); 6 } 7 8 public static <T> T getClass(Class<T> valueType) { 9 return valueType.cast(container.get(valueType)); 10 } 11 }
我们转为使用其Class类型作为key值来对应我们的对象,确实可以做到对象获取时的万无一失:
1 public class Test { 2 public static void main(String[] args) { 3 Teacher teacher = new Teacher("A老师"); 4 Student student = new Student("B学生"); 5 ClassContainerTwo.addClass(Teacher.class,teacher); 6 ClassContainerTwo.addClass(Student.class,student); 7 Teacher teacher1 = ClassContainerTwo.getClass(Teacher.class); 8 Student student1 = ClassContainerTwo.getClass(Student.class); 9 System.out.println(teacher1 + " " + student1); 10 } 11 }
但是这样做的代价就是我们无法存放多个相同的对象,我们可以创建一个钩子类来衔接这个类容器和各个对象:
1 public class Key<T> { 2 private String name; 3 private Class<T> valueType; 4 5 public Key(String name, Class<T> valueType) { 6 this.name = name; 7 this.valueType = valueType; 8 } 9 10 /** 11 * 同时重写equals()和hashCode(),避免加入类容器是和 12 * 从类容器中取出对象时实例化的key不是同一个对象,及类属性相同,但是地址不同 13 */ 14 @Override 15 public boolean equals(Object o) { 16 if (this == o) return true; 17 if (o == null || getClass() != o.getClass()) return false; 18 Key<?> key = (Key<?>) o; 19 return Objects.equals(name, key.name) && 20 Objects.equals(valueType, key.valueType); 21 } 22 23 @Override 24 public int hashCode() { 25 return Objects.hash(name, valueType); 26 } 27 28 public Class<T> getValueType() { 29 return valueType; 30 } 31 }
然后继续完善我们的类容器:
1 public class ClassContainerThree { 2 private static Map<Key<?>,Object> container = new HashMap<>(); 3 4 public static <T> void addClass(Key<T> key,T value) { 5 container.put(key, value); 6 } 7 8 public static <T> T getClass(Key<T> key) { 9 return key.getValueType().cast(container.get(key)); 10 } 11 }
这样的封装,虽然对于Key的实例化代码较长,但是很好的解决了我们的类容器存储和获取问题:
1 public class Test { 2 public static void main(String[] args) { 3 Teacher teacher = new Teacher("A老师"); 4 Student student = new Student("B学生"); 5 ClassContainerThree.addClass(new Key<>("teacher",Teacher.class),teacher); 6 ClassContainerThree.addClass(new Key<>("teacher",Student.class),student); 7 Teacher teacher1 = ClassContainerThree.getClass(new Key<>("teacher",Teacher.class)); 8 Student student1 = ClassContainerThree.getClass(new Key<>("teacher",Student.class)); 9 System.out.println(teacher1 + " " + student1); 10 } 11 }