`

【设计模式】 装饰者与IO/Collection框架

阅读更多

装饰者模式

      继承是OOP程序设计的一大特点,但其实对于很多复杂问题来说,利用继承关系处理问题往往具有很高的耦合性,不利于代码的维护。利用组合很大程度上可以做到降耦。多

用组合,少用继承是OOP设计的重要思想。
      装饰者模式给我们提出了一个好的OOP设计原则:类应该对扩展开放,对修改关闭 。
      这句话的意思就是,如果问题发生改变,衡量一个好的设计标准就是:你不需要修改类中的代码,只需要扩展新类来适应新的行为。
     《Head First Design Patterns》对装饰者模式说的很清楚。这里稍微注意几点:       
      (1) 装饰者和被装饰者必须具有相同的超类型。
      (2) 装饰者即可以包装被装饰者,也可以包装装饰者。往往利用多层包装来达到目的。
      (3) 装饰者中组合了被装饰者对象,这是装饰类的关键特征。正是由于这种组合,使得我们能够随心所欲的通过嵌套装饰来动态扩展行为。

 

Java IO框架的装饰者设计

在java类库中的IO流就是用装饰者模式设计的。JDK5.0中60多个IO流类组成了四大家族:InputStream,OutputStream,Reader,Writer。
      InputStream/OutputStream是对字节序列进行操作的抽象类。
      Reader/Writer是基于Unicode代码单元进行操作的抽象类。

这四大家族中大量的类都具有自己不同的功能,要做到方便的完成各种输入输出行为。必须组合使用这些类,装饰者模式是再好不过的设计了。那么IO类库如何实现装饰者模式的,我们看看几个类的部分源码: //InputStream:字节序列输入类鼻祖 public abstract class InputStream implements Closeable { //最基本的读取字节的抽象方法,供子类扩展。 public abstract int read() throws IOException; } //FileInputStream: 读取文件中的字节流类 继承InputStream public class FileInputStream extends InputStream{ //构造器 public FileInputStream(String name) throws FileNotFoundException { //....... } //本地方法,与操作系统低层交互的具体读入方法 public native int read() throws IOException; } //FilterInputStream: 过滤流类,起装饰器作用,用于对输入装配各种功能 public class FilterInputStream extends InputStream { //用于记录被装饰者,也就是需要装配新功能的InputStream对象 protected volatile InputStream in; protected FilterInputStream(InputStream in) { //构造装饰器 this.in = in; //设置需要被包装InputStream对象 } //读入字节 public int read() throws IOException { return in.read(); } } //BufferedInputStream: 使输入流具有缓冲功能,是一种可以装配缓冲功能的装饰器,继承FilterInputStream public class BufferedInputStream extends FilterInputStream { //构造器 public BufferedInputStream(InputStream in) { this(in, defaultBufferSize); //in就是被装配缓冲功能的InputStream } }


 这四个类同属于InputStream家族,他们就是一个经典的装饰器模式设计。其中
       InputStream 具有读入功能的抽象被装饰器。
       FileInputStream  具有读入文件功能的具体被装饰器
       FilterInputStream  具备装饰器的抽象意义。
       BufferedInputStream   具有具体功能(缓冲功能)的装饰器。
这个时候后我想设计一个具有缓冲功能的读取文件中的字节的行为:

public void IOTest{ //缓冲装饰器包装文件字节输入流 BufferedInputStream bis=new BufferedInputStream(new FileInputStream("C://decorator.txt")); //读取内容 bis.read(); }

 IO类库中还有很多其他的装饰器,比如处理基本数据类型的DataInputStream,处理ZIP文件流的ZipInputStream,等等。只要我们想的到的行为,都可以用这些装饰器包装组合来完成。就这一点,装饰器绝对是Perfect。

 

Java Collection框架的装饰者设计
 
在JDK类库种,集合类也使用了这种设计模式,我们看看这种设计模式给集合类库带来了什么好处?

 

问题提出:当我们即需要List结构的可重复存储,又需要Set中高效率的查找操作。怎么办?

最好的解决办法就是:先用List存储好所有的数据,当需要查找某个元素的时候,将List对象包装成Set类型进行查找,然后返回List数据结构和Set的查找结果。将两种不同类别的功能合并使用,装饰者模式(包装器)无疑是最好的设计。


所有实现Collection接口的集合类都有一种构造器,其参数是集合类的引用。

 

 

//ArrayList的包装构造器
public ArrayList(Collection<? extends E> c) { ..... }
//LinkedList的包装构造器
public LinkedList(Collection<? extends E> c) { ..... }
//HashSet的包装构造器 public HashSet(Collection<? extends E> c) { ..... }

 

我们可以通过这种构造直接将一种Collection类对象包装成另一种。

//Collection类的打包过程 import java.util.*; public class TestDemo{ public static void main(String[] args){ ArrayList<String> list=new ArrayList<String>(); list.add("a1"); list.add("a1"); list.add("b1"); list.add("c1"); System.out.println("List ="+list); HashSet<String> set=new HashSet<String>(list);//包装 System.out.println("Set ="+set); } } /*运行结果: List =[a1, a1, b1, c1] Set =[b1, a1, c1] */


值得注意的是:包装过程中集合类的存储数据类型必须兼容Collection<? extends E>。也就是被 包装 集合中数据类型必须是包装集合数据类型的子类或两者类型相同 。例如:被包装的ArrayList中的数据类型是Manager,它是包装的HashSet中数据类型Employee的子类。否则编译器不会通过。 这也是为了保证类型的自动向上转型的特性。被包装的类型可以通过包装操作自动向上转型成父类。

//包装过程中泛型类型的兼容 import java.util.*; class Employee{ } class Manager extends Employee{ } public class TestDemo{ public static void main(String[] args){ /*error : ArrayList<Employee> list=new ArrayList<Employee>(); HashSet<Manager> set=new HashSet<Manager>(list);*/ //正确: ArrayList<Manager> list=new ArrayList<Manager>(); HashSet<Employee> set=new HashSet<Employee>(list); } }


顺便提一句: 集合框架中的Map类型是不能和Collection类型互相包装的,他们的数据结构毕竟相差太大了 。(~你不想在商店买到的可乐里包装的是医用酒精吧)

 

这里提一下我一直的疑问:Collection的名字有点让人费解,刚学的时候还以为他就是所有集合类的基础接口,毕竟Collection的中文意思就是集合吗? 后来发现Map类型和

Collection没有一点关系,不知道Java的设计者是怎么取名字的,或者有难言之隐吧。

数组转化为集合:Array的asList()。
       String[] values=".....";
       HashSet<String> hs=new HashSet<String>(Array.asList(values));

集合转化为数组:Collection的toArray()。
       HashSet<String> hs=new HashSet<String>();
       Object[] o=hs.toArray();

 

千万要注意:toArray()方法返回的是一个Object[]数组。而且你无法将其强制类型转化成你需要的数组类型。(关于类类型间的强制类型转换,我在《【解惑】Java类型间的转型 》一文中有详细阐述)
               String[] array=(String[])hs.toArray(); //error

我们必须使用toArray的变体来做到这一点,为toArray传递一个长度为0的随意类型的数组。然后,返回的数组就是这个类型了。
               String[] array=hs.toArray(new String[0]);

 

分享到:
评论

相关推荐

    Java基础知识点总结.docx

    二十一、 设计模式★★★★★ 136 设计模式简介 136 单例设计模式:★★★★★ 156 工厂模式★★★★★ 159 抽象工厂模式★★★★★ 163 建造者模式 170 原型模式 177 适配器模式 182 桥接模式 188 过滤器模式 192 ...

    Java基础最全笔记文档

    Java基础笔记分为 Java基础篇 和 Java加强篇 Java基础篇包括: 1. Java环境搭建、Java快速入门、IDEA开发工具 2. Java基础语法、类型转换、运算符、Scanner 3. 分支结构、循环结构...14. XML、解析、工厂模式和装饰模式

    Java常见面试题.pdf

    Java常见面试题: 1. 什么是 Java?它的特点是什么? 2. 解释面向对象编程(OOP)的概念和特点...列举几个常见的设计模式。 19. 解释 Java 中的序列化和反序列化的概念。 20. 如何处理并发编程中的线程安全和性能问题?

    JAVA_API1.6文档(中文)

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...

    java api最新7.0

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类。...

    [Java参考文档].JDK_API 1.6

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类。...

    Java 1.6 API 中文 New

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类。...

    JavaAPI1.6中文chm文档 part1

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...

    JavaAPI中文chm文档 part2

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...

    [Java参考文档]

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...

    JDK_1_6 API

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...

    java jdk-api-1.6 中文 chmd

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...

    java 面试题 总结

    Session Bean 还可以再细分为 Stateful Session Bean 与 Stateless Session Bean ,这两种的 Session Bean都可以将系统逻辑放在 method之中执行,不同的是 Stateful Session Bean 可以记录呼叫者的状态,因此通常来...

    springmybatis

    MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plan ...

    超级有影响力霸气的Java面试题大全文档

     Session Bean 还可以再细分为 Stateful Session Bean 与 Stateless Session Bean ,这两种的 Session Bean都可以将系统逻辑放在 method之中执行,不同的是 Stateful Session Bean 可以记录呼叫者的状态,因此通常...

    java基础案例与开发详解案例源码全

    7.4 软件的可维护与复用设计原则194 7.5 本章练习199 第8章 8.1 内部类和内部接口202 8.1.1 非静态成员内部类203 8.1.2 局部内部类206 8.1.3 静态内部类209 8.1.4 匿名内部类211 8.2 对象包装器213 8.3 装箱和拆箱...

Global site tag (gtag.js) - Google Analytics