`

【设计模式】 装饰者与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]);

 

分享到:
评论

相关推荐

    apache commons lang、io、collection源码与UT

    - 源码结构和设计模式:观察类和方法的设计,了解常见的设计模式如工厂模式、单例模式和装饰器模式。 - 异常处理:学习如何优雅地处理异常,避免程序中断。 - 性能优化:查找和理解性能关键部分,如内存管理、循环...

    JAVA设计模式在JDK中的应用

    ### JAVA设计模式在JDK中的应用 #### 一、引言 在软件开发过程中,设计模式作为一套被广泛接受的解决方案,能够帮助开发者解决常见的设计问题。Java作为一门流行的编程语言,其标准库(JDK)中巧妙地融入了多种设计...

    Go-支持不同数据源的ORMGo包

    本篇文章将详细探讨`upper.io/db`库,这是一个强大的Go语言ORM框架,它提供了一个统一的接口来与多种数据源进行交互。 `upper.io/db`的核心特性在于其适配器模式。适配器模式允许开发者通过一个标准化的接口与各种...

    jdk中设计模式

    【设计模式概述】 设计模式是软件工程中经过实践验证的解决常见问题的可复用解决方案。它们是经验的结晶,旨在提升代码的可读性、可维护性和灵活性。设计模式通常分为三大类:创建型模式(关注对象的创建)、结构型...

    java开发面试题.zip

    10. **设计模式**:单例、工厂、观察者、装饰者、适配器、代理等23种设计模式的应用场景和实现。 11. **微服务**:Docker容器化、Spring Cloud组件(Eureka、Zuul、Ribbon、Hystrix、Feign等)的使用。 12. **数据...

    Java-Interview-Collection.rar_java 面试

    8. **设计模式**:常见的23种设计模式,如单例、工厂、观察者、装饰者、代理等。面试官通常会询问你熟悉哪些模式,并在实际开发中如何应用。 9. **JVM优化**:JVM参数设置、内存调优、性能监控工具的使用。面试官...

    200+最常见Java面试题参考答案(嗯嗯).pdf

    为了能够帮助求职者更好地准备面试,这类面试题集和答案通常会涵盖Java的基础知识点、面向对象、集合框架、异常处理、多线程编程、JVM知识、IO/NIO、网络编程、数据库连接、设计模式、框架使用等重要部分。...

    2020 java经典面试题总汇.zip

    - 单例模式、工厂模式、装饰者模式、观察者模式等常见设计模式的实现与应用场景 - 模板方法、策略、适配器等行为设计模式的理解 - AOP(面向切面编程)与DI(依赖注入)的概念,Spring框架中的实现 这份面试题...

    Java最实用面试大全.zip

    设计模式是解决常见软件设计问题的模板,Java面试中常问到的有工厂模式、单例模式、建造者模式、装饰器模式、适配器模式、观察者模式、代理模式、模版方法模式等23种设计模式的应用与理解。 七、框架篇 对于有经验...

    java面试笔试题大汇总

    8. **设计模式**:熟练掌握单例、工厂、抽象工厂、建造者、适配器、装饰器、代理、观察者、模板方法、策略、命令、职责链、状态、享元、访问者等常见设计模式。 9. **Spring框架**:作为企业级应用的主流框架,对...

    javaEE面试宝典共228页.docx

    - 设计模式是解决常见问题的最佳实践,如单例模式、工厂模式、观察者模式、装饰者模式等,它们在实际开发中有着广泛的应用。 6、**Linux** - Linux系统操作,包括命令行操作、文件系统、进程管理、网络配置、权限...

    Collection制作相片查看器

    9. **设计模式**:为保持代码的可维护性和可扩展性,可能使用到单例模式(如图片缓存服务)、观察者模式(更新UI以反映模型的变化)等。 10. **异常处理**:处理可能出现的错误,如文件不存在、内存不足等,都需要...

    JAVA程序员面试大全(包括笔试和面试)

    如单一职责原则、开闭原则、里氏替换原则、依赖倒置原则等,以及常见的设计模式,如工厂模式、单例模式、装饰器模式、观察者模式、策略模式、适配器模式等,这些都是评估你解决问题能力的重要标准。 Java的IO/NIO/...

    【Java核心知识面试】-各大公司Java后端开发面试题总结.zip

    8. **设计模式**:单例、工厂、观察者、装饰器、代理、适配器等常见的设计模式,这些模式是解决软件设计问题的常用工具。 9. **数据库操作**:SQL语言的基础及高级应用,包括查询优化、事务处理、索引原理等。同时...

    Java常见面试题.pdf

    常见的设计模式有单例模式(Singleton)、工厂模式(Factory)、建造者模式(Builder)、观察者模式(Observer)、装饰器模式(Decorator)等,它们有助于提高代码的可维护性、可扩展性和可复用性。19. Java 中的...

    java面试知识点

    在Java面试中,面试官通常会关注应聘者的基础语法知识、面向对象编程理解、集合框架、多线程、异常处理、IO流、网络编程、JVM原理以及设计模式等多个方面。以下是对这些关键知识点的详细阐述: 1. **基础语法**:...

    java经典面试题目大全

    8. **设计模式**:设计模式是解决软件设计中常见问题的模板,面试者应熟悉单例、工厂、观察者、装饰者、适配器、桥接、代理、建造者、责任链、命令等常见设计模式,并能结合实际场景进行应用。 9. **JVM**:理解...

    Java 面試資料,~~~~~~~~

    在Java面试中,面试官通常会考察应聘者的编程基础、数据结构与算法、多线程、集合框架、异常处理、IO流、网络编程、设计模式等多个方面的能力。以下是一些重要的Java面试知识点: 1. **Java基础**:了解Java的基本...

    ssh框架技术支持

    MVC(Model-View-Controller)模式是一种广泛应用于Web开发的设计模式,主要用于将应用程序逻辑分为三个部分: - **View**:负责展示数据给用户。 - **Model**:存储应用程序的数据及其业务逻辑。 - **Controller**...

    JAVA面试题

    - 单例模式、工厂模式、代理模式、装饰器模式等常见设计模式的应用与理解。 9. **JVM优化** - 参数调整:JVM启动参数设置,如堆大小、GC策略等。 - 性能监控:JConsole、VisualVM等工具的使用。 10. **Spring...

Global site tag (gtag.js) - Google Analytics