IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    设计模式之美-课程笔记46-开源实战1-JDK

    10k发表于 2023-11-12 00:00:00
    love 0

    工厂模式在Calendar类中的应用

    1. Calendar类的工厂代码和功能代码在一个类中。
    2. Calender.getInstance()的方法实现。是一个工厂模式,根据传入的时区和locale决定使用不同的历法。
    public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
      //...
      public static Calendar getInstance(TimeZone zone, Locale aLocale){
        return createCalendar(zone, aLocale);
      }
    
      private static Calendar createCalendar(TimeZone zone,Locale aLocale) {
        CalendarProvider provider = LocaleProviderAdapter.getAdapter(
            CalendarProvider.class, aLocale).getCalendarProvider();
        if (provider != null) {
          try {
            return provider.getInstance(zone, aLocale);
          } catch (IllegalArgumentException iae) {
            // fall back to the default instantiation
          }
        }
    
        Calendar cal = null;
        if (aLocale.hasExtensions()) {
          String caltype = aLocale.getUnicodeLocaleType("ca");
          if (caltype != null) {
            switch (caltype) {
              case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                break;
              case "japanese":
                cal = new JapaneseImperialCalendar(zone, aLocale);
                break;
              case "gregory":
                cal = new GregorianCalendar(zone, aLocale);
                break;
            }
          }
        }
        if (cal == null) {
          if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
            cal = new BuddhistCalendar(zone, aLocale);
          } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") {
            cal = new JapaneseImperialCalendar(zone, aLocale);
          } else {
            cal = new GregorianCalendar(zone, aLocale);
          }
        }
        return cal;
      }
      //...
    }
    

    建造者模式在Calendar类中的应用

    1. 关键点在于省略的set方法们。建造者模式和工厂模式都存在于Calendar类中,为啥?因为工厂模式是根据传入的某个参数构造不同的实体类型;而建造者模式是应对复杂的实体初始化,通过不同的set方法构造定制化的实体。还是那句话,两者的目的有所区别。
    public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
      //...
      public static Calendar getInstance(TimeZone zone, Locale aLocale){
        return createCalendar(zone, aLocale);
      }
    
      private static Calendar createCalendar(TimeZone zone,Locale aLocale) {
        CalendarProvider provider = LocaleProviderAdapter.getAdapter(
            CalendarProvider.class, aLocale).getCalendarProvider();
        if (provider != null) {
          try {
            return provider.getInstance(zone, aLocale);
          } catch (IllegalArgumentException iae) {
            // fall back to the default instantiation
          }
        }
    
        Calendar cal = null;
        if (aLocale.hasExtensions()) {
          String caltype = aLocale.getUnicodeLocaleType("ca");
          if (caltype != null) {
            switch (caltype) {
              case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                break;
              case "japanese":
                cal = new JapaneseImperialCalendar(zone, aLocale);
                break;
              case "gregory":
                cal = new GregorianCalendar(zone, aLocale);
                break;
            }
          }
        }
        if (cal == null) {
          if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
            cal = new BuddhistCalendar(zone, aLocale);
          } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") {
            cal = new JapaneseImperialCalendar(zone, aLocale);
          } else {
            cal = new GregorianCalendar(zone, aLocale);
          }
        }
        return cal;
      }
      //...
    }
    

    装饰器模式在Collections类中的应用

    1. 关键点在于UnmodifiableCollection的构造函数接收了c参数对于原来的collection进行了包裹并增强;而普通的接口实现是不做这个的。
    public class Collections {
      private Collections() {}
        
      public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
        return new UnmodifiableCollection<>(c);
      }
    
      static class UnmodifiableCollection<E> implements Collection<E>,   Serializable {
        private static final long serialVersionUID = 1820017752578914078L;
        final Collection<? extends E> c;
    
        UnmodifiableCollection(Collection<? extends E> c) {
          if (c==null)
            throw new NullPointerException();
          this.c = c;
        }
    
        public int size()                   {return c.size();}
        public boolean isEmpty()            {return c.isEmpty();}
        public boolean contains(Object o)   {return c.contains(o);}
        public Object[] toArray()           {return c.toArray();}
        public <T> T[] toArray(T[] a)       {return c.toArray(a);}
        public String toString()            {return c.toString();}
    
        public Iterator<E> iterator() {
          return new Iterator<E>() {
            private final Iterator<? extends E> i = c.iterator();
    
            public boolean hasNext() {return i.hasNext();}
            public E next()          {return i.next();}
            public void remove() {
              throw new UnsupportedOperationException();
            }
            @Override
            public void forEachRemaining(Consumer<? super E> action) {
              // Use backing collection version
              i.forEachRemaining(action);
            }
          };
        }
    
        public boolean add(E e) {
          throw new UnsupportedOperationException();
        }
        public boolean remove(Object o) {
           hrow new UnsupportedOperationException();
        }
        public boolean containsAll(Collection<?> coll) {
          return c.containsAll(coll);
        }
        public boolean addAll(Collection<? extends E> coll) {
          throw new UnsupportedOperationException();
        }
        public boolean removeAll(Collection<?> coll) {
          throw new UnsupportedOperationException();
        }
        public boolean retainAll(Collection<?> coll) {
          throw new UnsupportedOperationException();
        }
        public void clear() {
          throw new UnsupportedOperationException();
        }
    
        // Override default methods in Collection
        @Override
        public void forEach(Consumer<? super E> action) {
          c.forEach(action);
        }
        @Override
        public boolean removeIf(Predicate<? super E> filter) {
          throw new UnsupportedOperationException();
        }
        @SuppressWarnings("unchecked")
        @Override
        public Spliterator<E> spliterator() {
          return (Spliterator<E>)c.spliterator();
        }
        @SuppressWarnings("unchecked")
        @Override
        public Stream<E> stream() {
          return (Stream<E>)c.stream();
        }
        @SuppressWarnings("unchecked")
        @Override
        public Stream<E> parallelStream() {
          return (Stream<E>)c.parallelStream();
        }
      }
    }
    

    适配器模式在Collections类中的应用

    1. 老版本的 JDK 提供了 Enumeration 类来遍历容器。新版本的 JDK 用 Iterator 类替代 Enumeration 类来遍历容器。为了兼容老的客户端代码(使用老版本 JDK 的代码),保留了 Enumeration 类,并且在 Collections 类中,仍然保留了 enumaration() 静态方法(因为我们一般都是通过这个静态函数来创建一个容器的 Enumeration 类对象)。
    2. 在新版本的 JDK 中,Enumeration 类是适配器类。它适配的是客户端代码(使用 Enumeration 类)和新版本 JDK 中新的迭代器 Iterator 类。
    /**
     * Returns an enumeration over the specified collection.  This provides
     * interoperability with legacy APIs that require an enumeration
     * as input.
     *
     * @param  <T> the class of the objects in the collection
     * @param c the collection for which an enumeration is to be returned.
     * @return an enumeration over the specified collection.
     * @see Enumeration
     */
    public static <T> Enumeration<T> enumeration(final Collection<T> c) {
      return new Enumeration<T>() {
        private final Iterator<T> i = c.iterator();
    
        public boolean hasMoreElements() {
          return i.hasNext();
        }
    
        public T nextElement() {
          return i.next();
        }
      };
    }
    

    模板模式在Collections中的应用

    1. Collections.sort() 就用到了模板模式,他讲比较大小的实现委派给用户实现。sort是整个排序过程中的一步。
    2. 通过类似回调的机制进行。
    `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;
          }
        }
      }
    }
    

    观察者模式在JDK的应用

    1. Changed 用来表示observable更新了。
    2. 所以当调用notifyOverservers,需要先调用setChanged,就不会给其他的观察者通知。
      1. changed成员变量还是必须的,这么做的好处是可以将“跟踪变化”和“通知观察者”两步分开,处理一些复杂的逻辑,
    3. Observable为了保证线程安全,他的方法在调用时都会加锁,除了notifyOverservers。 这个方法是为了性能问题,用一个本地的数组快照去保存当时的观察者然后去一个个通知。
      1. 每个观察者的update方法不可知,可能造成线程等待时间过长。
      2. 而Vector是线程不安全的
      3. 拷贝出来的观察者数组(本地变量)是一个线程私有的。
        1. 可能存在新加的无法被通知,新删除的依然被通知。
    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;
        }
    }
    

    单例模式在Runtime类中的应用

    1. 每个Java应用运行时都是一个进程(唯一),所以只对应一个Runtime运行实例。用来查看JVM状态和控制JVM行为。
    2. 这个类是一个单例类,实例只能通过getRuntime来获取。
    /**
     * 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);
      }
      //...
    }
    

    其他设计模式应用汇总

    1. 在讲到模板模式的时候,我们结合 Java Servlet、JUnit TestCase、Java InputStream、Java AbstractList 四个例子,来具体讲解了它的两个作用:扩展性和复用性。

    2. 在讲到享元模式的时候,我们讲到 Integer 类中的 -128~127 之间的整型对象是可以复用的,还讲到 String 类型中的常量字符串也是可以复用的。这些都是享元模式的经典应用。

    3. 在讲到职责链模式的时候,我们讲到Java Servlet 中的 Filter 就是通过职责链来实现的,同时还对比了 Spring 中的 interceptor。实际上,拦截器、过滤器这些功能绝大部分都是采用职责链模式来实现的。
    4. 在讲到的迭代器模式的时候,我们重点剖析了 Java 中 Iterator 迭代器的实现,手把手带你实现了一个针对线性数据结构的迭代器。


沪ICP备19023445号-2号
友情链接