设计模式学习(三)——装饰器模式

前言

距离上一次正儿八经地写随笔已经有一段时间了,虽然2月10号有一篇关于泛型的小记,但是其实只是简单地将自己的学习代码贴上来,为了方便后续使用时查阅,并没有多少文字和理解感悟。之所以在今天觉得有必要写点东西,主要是因为对自己这一周的状态不满意。表面上看,周一到周三都是加班到比较晚,周一回到家快10点了,还看了会书到12点半,但是实际上,我知道,自己的精神状态出现了松懈,心态上有点浮躁,态度的问题是不能容忍的。因此决定在今天写点东西,希望让自己能静下来,重新出发。

一、OOP(Object Oriented Programming)中的装饰器模式

在《HeadFirst设计模式》中,作者给出的定义是:装饰者模式动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

其实从字面上,这个定义并不是那么容易理解。我自己总结了下OOP中的装饰器模式特点:

1. 装饰者与被装饰者会继承相同的父类。

2. 装饰者会有一个私有变量持有被装饰者的引用。

3. 装饰者将自己的属性附加到到被装饰者上。

为了学以致用,我想用生活中的例子来描述装饰者模式的使用姿势。

场景:最近我厂由于业务扩展,有大批职位需要招聘,而各个职位对求职者的要求参差不齐,其中有一部分岗位对人才的要求非常相近但是又不完全相同,这边提取出一些出现频率比较高的要求:计算机专业,熟悉Java,熟悉JVM,测试分析能力,自动化测试能力。

下面就是使用装饰者模式实现的代码:

Job(被装饰者)基类:

public abstract class Job {
    private String title="unknown";
    public String getTitle(){
        return title;
    };
    public void setTitle(String title){
        this.title = title;
    }
    public abstract String getJobDescription();
}

JobRequirement(装饰者)基类:

public abstract class JobRequirement extends Job {    
    //重写getDescription方法
    public abstract String getJobDescription();
}

SoftwareDeveloper(职位1):

public class SoftwareDeveloper extends Job{
    public SoftwareDeveloper(){
        this.setTitle("Software Developer");
    }
    @Override
    public String getJobDescription() {
        return "familiar with Java";
    }
}

SoftwareTester(职位2)

public class SoftwareTester extends Job{
    public SoftwareTester() {
        this.setTitle("Software Tester");
    }
    @Override
    public String getJobDescription() {
        return "familiar with test-analysis";
    }
}

AutotestRequirement(自动化测试要求):

public class AutotestRequirement extends JobRequirement{
    Job job;
    public AutotestRequirement(Job job){
        this.job=job;
    }
    @Override
    public String getTitle() {
        return job.getTitle();
    }
    @Override
    public String getJobDescription() {
        return job.getJobDescription() + "; " + "familiar with autotest";
    }
}

JVMRequiremement(JVM要求):

public class JVMRequirement extends JobRequirement {
    Job job;
    public JVMRequirement(Job job) {
        this.job = job;
    }
    @Override
    public String getTitle() {
        return job.getTitle();
    }
    @Override
    public String getJobDescription() {
        return job.getJobDescription() + "; " + "familiar with JVM";
    }    
}

MajorRequirement(专业要求):

public class MajorRequirement extends JobRequirement {
    Job job;
    public MajorRequirement(Job job) {
        this.job = job;
    }
    @Override
    public String getTitle() {
        return job.getTitle();
    }
    @Override
    public String getJobDescription() {
        return job.getJobDescription() + "; " + "major in Computer Science";
    }
}

执行Main类:

 1 public class Main {
 2     public static void main(String[] args) {
 3         //develop 
 4         Job developer = new SoftwareDeveloper();
 5         developer = new MajorRequirement(developer);
 6         developer = new JVMRequirement(developer);
 7         System.out.println("Title: " + developer.getTitle());
 8         System.out.println("JD: " + developer.getJobDescription());
 9         //tester
10         Job tester = new SoftwareTester();
11         tester = new AutotestRequirement(new MajorRequirement(tester));
12         System.out.println("Title: " + tester.getTitle());
13         System.out.println("JD: " + tester.getJobDescription());
14     }
15 }

执行结果:

Title: Software Developer
JD: familiar with Java; major in Computer Science; familiar with JVM
Title: Software Tester
JD: familiar with test-analysis; major in Computer Science; familiar with autotest

可以看到,使用装饰者的代码非常简洁,SoftwareDeveloper本是一个普通的开发岗位,你可以通过加不同的装饰器给这个岗位增加特殊的要求。当有新的职位要求要加入时,只要创建新的requirement类来装饰即可。

二、Python中的装饰器

上一节介绍了OOP中的装饰器设计模式的思想和用法,下面简单谈谈Python中的装饰器。关于Python中的装饰器使用,这里不细讲,有兴趣可以直接查询廖雪峰的Python教程:《装饰器-廖雪峰的官方网站》。这里主要谈谈两者的区别:

1. Python中的装饰器实际上是基于函数的装饰,而面向对象的装饰器思想是基于类的装饰。

Python中的装饰器,实际上是通过语法实现基于函数的装饰,增强原有函数的功能。它通过将一些公有操作(比如:日志打印,校验网站session中是否已存在登录信息等)抽取出来,在各个需要这些公有操作的函数上方通过增加“@装饰器名”的方式来增强函数的功能。缺点是:当一个方法加上装饰器后,不能再调用原来的方法。

面向对象中的装饰器,是基于类的装饰。装饰者与被装饰者继承相同的父类,准确地应该说是有相同的“祖先”。它通过一系列的封装,使得更多的类的方法可以被复用。缺点:可能会有大量的类,造成API调用时的困扰。

2. 装饰器模式是一种设计思想,Python也是一种面向对象的语言,当然也可以使用面向对象的方式来实现装饰器模式。

三、Java I/O中的装饰者模式应用

 

上图是简单的InputStream类图(有些类没有完全列出),可以看到InputSream是一个父类,下面有4个子类,其中FileInputStream、StringBufferInputStream、ByteArrayInputStream都是被装饰者,是可以被装饰者包装起来的具体组件;FilterInputSream是一个抽象装饰者,下面有4个具体的装饰者类:PushbackInputStream、BufferedInputStream、DataInputStream、LineNumberInputStream。

这样一看,Java I/O的结构关系似乎就明了了许多,但是其中具体的细节,还是必须要抽时间系统地学习一番。

四、写在最后

其实对我来说,第一季度已经过去了70%,后续还有非常繁忙地迭代测试任务,没有多少时间可以用来完成自己第一季度的计划。在这个时间点,我更应该做的是去学习自己计划中的任务(YARN)。只是发现最近这周自己的思想状态不是很好,心态上似乎也有点浮躁,所以抽时间来写点东西,让自己的心静一静。另一方面也想将自己看过的东西实践并记录下来,可以方便后续使用时查阅。希望通过这篇随笔提醒自己,珍惜时间,不忘初心,方得始终。

原文地址:https://www.cnblogs.com/znicy/p/6445359.html