我们讲解了工厂模式,建造者模式,装饰器模式,适配器模式的应用,其中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的设计就是多此一举了,但是如果出现了高并发,那么直接去尝试直接执行更新操作可能会是一个非常漫长的等待,于是利用一个简单的标识位,并加上了锁来进行了修改,在高并发的情况下,无可厚非

发表评论

邮箱地址不会被公开。 必填项已用*标注