我们讲解了工厂模式,建造者模式,装饰器模式,适配器模式的应用,其中Calendar用到了工厂模式和建造者模式,Collections类用到了装饰器模式和适配器模式,而且主要说了工厂模式和建造模式的结合,那么我们继续说一下JDK中其他设计模式的应用
模板模式在Collections类的应用
模板模式就是在框架的使用者在不改变框架的前提下,只修改部分的扩展点,从而到了基于扩展点去定制化框架,Java的Collections类的Sort()函数,就是利用了模板模式的扩展特性
Collections.sort的使用,就是让用户去传入一个排序的方法,可以自定义排序策略
public class Demo {
public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student(“Alice”, 19, 89.0f)); students.add(new Student(“Peter”, 20, 78.0f)); students.add(new Student(“Leo”, 18, 99.0f)); Collections.sort(students, new AgeAscComparator()); print(students); Collections.sort(students, new NameAscComparator()); print(students); Collections.sort(students, new ScoreDescComparator()); print(students); } public static void print(List<Student> students) { for (Student s : students) { System.out.println(s.getName() + ” ” + s.getAge() + ” ” + s.getScore()); } } public static class AgeAscComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.getAge() – o2.getAge(); } } public static class NameAscComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.getName().compareTo(o2.getName()); } } public static class ScoreDescComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { if (Math.abs(o1.getScore() – o2.getScore()) < 0.001) { return 0; } else if (o1.getScore() < o2.getScore()) { return 1; } else { return -1; } } } } |
那么这个Collections.sort()就是实现了对集合的排序,为了提高扩展性,将具体的实现的手段,让客户实现后传入,这个的实现类似于JdbcTemplate,但是内部的实现是
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c); } |
这种方式其实一种回调的机制
而且,Collections.sort()还很类似于一种策略模式,因为可以将排序的多种实现方式当做一种算法或者策略,看做策略模式的应用
但是,这并不是一种严格意义上的策略模式,因为策略模式分为了策略的定义,创建,使用三个部分,策略通过工厂模式来创建,并且根据用户的输入,动态的决定使用什么样的策略
这种Collections.sort的使用,并非通过了工厂模式,其使用也不是动态的确定
观察者模式在JDK中的应用
我们之前说了Google的EventBus,而Java JDK内部也提供了观察者模式的简单框架,让我们可以直接使用,比EventBus要简单的多,只包含了两个类,java.util.Observerable和java.util.Observer,前者是被观察者,后者是观察者
代码实现如下
public interface Observer {
void update(Observable o, Object arg); } public class Observable { private boolean changed = false; private Vector<Observer> obs; 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(); } for (int i = arrLocal.length-1; i>=0; i–) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } } |
我们着重看了changed变量和notifyObserver()函数
首先是changed变量,用于表示,被观察者的状态是否被更新,如果有更新的话,我们可以设置将changed为true,然后调用notifyObservers()函数,来触发观察者执行update()函数,不然,即使调用了notifyObservers()函数,也会因为没有update为true而无法更新
那么更新的时候,我么需要依次的调用setChanged()和notifyObservers()两个函数,单独调用notifyObservers()函数式不起作用的
然后就是notifyObservers()函数
为了保证多线程环境下,添加,移除,通知观察者三个操作不例外,我们Observable类中大部分函数可以通过synchronized加锁,不过呢,notifyObservers()本身没有加上锁,为什么呢?
如何保证不冲突的呢?
notifyObservers()函数之所以没有加锁,是为了性能,如果连这个都加上了锁,那么可能在并发过程中,因为执行update的时间过长,导致的速度过慢
在其中,进行加锁,并且拷贝了一份观察者的列表,赋值给一个新的数组,这就相当于一个快照,然后遍历快照,执行每个观察者的update,然后提高了整体并发性能
单例模式在Runtime类中的应用
JDK在Runtime类中是一个单例类,这个是用于我们添加JVM关闭时候的钩子用的,每个Java应用在运行时候回启动一个JVM进程,每个JVM进程都只有一个Runtime实例,查看JVM的状态和行为
runtime就是非常适合单例的
/**
* Every Java application has a single instance of class * <code>Runtime</code> that allows the application to interface with * the environment in which the application is running. The current * runtime can be obtained from the <code>getRuntime</code> method. * <p> * An application cannot create its own instance of this class. * * @author unascribed * @see java.lang.Runtime#getRuntime() * @since JDK1.0 */ public class Runtime { private static Runtime currentRuntime = new Runtime(); public static Runtime getRuntime() { return currentRuntime; } /** Don’t let anyone else instantiate this class */ private Runtime() {} //…. public void addShutdownHook(Thread hook) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission(“shutdownHooks”)); } ApplicationShutdownHooks.add(hook); } //… } |
其他模式在JDK中的实现汇总
回顾一下
我们在说模板模式的时候,说了Java Servlet,Junit TestCase ,Java InputStream,Java AbstractList四个,来说明了复用性和扩展性
享元模式,说了Integer中的-128~127的整形模式,我们讲到了String中的常量字符串是可以复用的.这都是经典的享元模式
职责链模式,我们讲到了Java Servlet中的Filter就是通过职责链来实现的,通过Spring的Interceptor,也是如此,拦截器,过滤器的功能都是靠职责链实现的
迭代器模式,我们说了Java中集合的迭代器,可以进行相关的迭代工作
本章重点:
我们主要说了,JDK的一些的设计模式的实现,其中有工厂模式 建造者模式 装饰器模式 适配器模式,模板模式,观察者模式,除此外,我们汇总了其他模式在JDK的应用,比如单例 享元 职责链 迭代器
但是其本质都是想说明,在其中我们需要灵活的使用不同的设计模式
课后思考:
1.每个函数都加一把synchronized锁,会不影响性能?有无优化手段?
2.changed成员变量里是否多此一举
1.肯定降低了性能,而通常优化的手段,是更小粒度的锁或者使用乐观锁,在这个方法中已经将notifyObservers方法原本的大锁,利用一个复制技术缩小到一小点了,也是一种版本控制的方式,这里先给出一个尝试优化,使用原子类Boolean来替换setChanged这个大锁,并且使用copyonwriteArrayList来替换我们的数组
2.如果没有多并发的任何情况,changed的设计就是多此一举了,但是如果出现了高并发,那么直接去尝试直接执行更新操作可能会是一个非常漫长的等待,于是利用一个简单的标识位,并加上了锁来进行了修改,在高并发的情况下,无可厚非