打造山寨产品和伪造产品的利器——装饰模式

来源:互联网 发布:酒精测试软件 编辑:程序博客网 时间:2024/06/08 05:28

【文章标题】打造山寨产品和伪造产品的利器——装饰模式

【文章作者】曾健生

【作者邮箱】zengjiansheng1@126.com

【作者QQ190678908

【作者博客】http://blog.csdn.net/newjueqi

【编程环境】JDK 1.6.0_01

【作者声明】欢迎转载文章,但转载请保留文章的完整性以及注明文章的出处。

 

*******************************************************************************

      

现在“山寨文化”和“伪造产品”大行其道,“山寨春晚”“山寨明星”“山寨手机”等等,“伪造产品”就更加不用说了,广大群众也深受其害(本人也是其中一位受害者!!!)不说不知道,在学设计模式的过程中本人一不小心发现了装饰模式是有很强的“伪造功能”(“伪造功能”是指把别人写的类,功能模块等包装一下换成自己写的,但其实核心还是别人写的代码)和“山寨功能”(利用类提供的接口添加代码增强类的功能)。

    《设计模式:可复用面向对象软件的基础》是这样定义装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来来说,装饰模式比生成子类更加灵活。

       一般来说,实现装饰器模式可分为下面3步:

1. 建立包装类,定义构造函数接收包装对象

2. 建立成员引用,让被包装对象作用于整个包装类

3.根据需要增加一些功能函数已增强类的功能

 

如果我们把装饰器模式实现“伪造功能”,就分为以下3步(非常邪恶的!!!):

1. 建立包装类,定义构造函数接收包装对象的参数

2. 建立成员引用,让被包装对象作用于整个包装类

3. 用自定义的函数封装包装对象的函数

 

以前有个著名的“汉芯造假”事件,大概就是说把某个外国芯片的标识去掉,然后改头换面,就变成了所谓的“汉芯”了。用装饰器模式实现“伪造功能”模拟这个过程的代码如下:

 

//一个美国芯片类,这个芯片类有三个功能:

//1.显示自身的信息

//2.获得两个数相加的结果

//3.获得两个数相减的结果

class AmericanChip

{    

       //美国芯片类的构造函数

       AmericanChip( ){}

      

       //美国芯片类显示自身信息的函数

       public void printInfo()

       {

              System.out.println( "我是美国的芯片" );

       }

      

       //获得两个数相加的结果

       public double add( double x, double y )

       {

              return x+y;

       }

      

       //获得两个数相减的结果

       public double sub( double x, double y  )

       {

              return x-y;

       }    

}

 

//这是造假芯片类,把美国的芯片改头换面后完成的

//步骤分为三步:

//1.建立包装类,定义构造函数接收包装对象的参数

//2.建立成员引用,让被包装对象作用于整个包装类

//3.用自定义的函数封装包装对象的函数

class FakeChip

{

       //建立被包装类的引用

       private AmericanChip americanChip;

      

       FakeChip(  )

       {

              //构造美国的芯片类的实例

              this.americanChip=new AmericanChip();

       }

      

       //造假芯片类显示自身信息的函数,这个函数就相同于把美国芯片的标识去掉,换上“汉芯”的标识

       public void printInfoFake()

       {

              System.out.println( "我是造假芯片" );

       }

      

       //造假芯片类通过美国芯片类获得两个数相加的结果

       public double addFake( double x, double y )

       {

              return americanChip.add( x, y );  

       }

                           

       //造假芯片类通过美国芯片类获得两个数相减的结果

       public double subFake( double x, double y  )

       {

              return americanChip.sub( x, y );

 

       }

      

}

 

造假芯片类FakeChip共暴露了3个接口:

1. public void printInfoFake()

2. public double addFake( double x, double y )

3. public double subFake( double x, double y  )

 

       从代码可看出,除了printInfoFake()方法是显示自身的信息外(造假产品要先显示一下自身的产品信息使别人误认为是正牌货),其他的两个方法addFakehe subFake实际上是调用了美国芯片类的相关方法,这就模拟了“汉芯”造假的过程。

       可能有的读者可能问使用继承不是能实现这个结果吗?选用装饰器模式而不用继承有两个原因:

1.       装饰器模式可选择需要暴露被包装类(美国芯片类AmericanChip)的哪些公有函数,虽然也可以用继承后覆盖同名函数的方法实现,但如果包装类(美国芯片类AmericanChip)的公有函数非常多的话,那真的写代码写得人崩溃!!!

2.       可把被包装类(美国芯片类AmericanChip)的公有函数用间接实现改名调用,真正实现改头换面的目的。

 

虽然本文前面论述的内容是怎么用装饰器模式实现“造假”过程,但我们必须要明白一个事实:装饰器模式的出现是为了给子类添加新的功能提供方便性和灵活性,这个才是装饰器模式的最主要用途。

javaIO类中有个类FileInputStream用来处理文件数据的,另外还有一个类BufferedInputStream提供了缓冲区的功能,大家认真比较一下BufferedInputStreamFileInputStream的函数,发现多了缓冲的功能外,其他的实现都是很相近的,另外查找一下BufferedInputStream的构造函数BufferedInputStream(InputStream in),能发现BufferedInputStream的实例化是传入了InputStream的对象,大家有没有发现和前面论述的实现装饰器模式的三步非常像,虽然在源码里BufferedInputStream的实现比较麻烦,但我们可用装饰器模式的思想,实现一个“山寨版BufferedInputStream”类。

 

 

//这是山寨版的BufferedInputStreamMyBufferedInputStream

 

package newjueqi.net.csdn.MyBufferedInputStream;

 

//这是山寨版的BufferedInputStreamMyBufferedInputStream

 

import java.io.*;

class MyBufferedInputStream

{

    private int num=0;//读入字节的个数

    private int pos=0;//当前在数组中的下标

   

    //实现装饰器模式步骤2:建立成员引用,让被包装对象作用于整个包装类

    private InputStream inputstream_temp=null; //字节流的引用

   

    private byte buf[]=new byte[1024]; //缓冲区数组

    byte temp=0;

   

    //实现装饰器模式步骤1:建立包装类,定义构造函数接收包装对象

    MyBufferedInputStream( InputStream inputstream_temp )

    {

       this.inputstream_temp=inputstream_temp;

    }

   

    //实现装饰器模式步骤3:根据需要增加一些功能函数已增强类的功能

    //用缓冲区增强了读取的功能

    public int myRead()

    {

       //把缓冲区的数据填满

       if( num==0 )

       {

           try {

              //num中保存了读入缓冲区的字节的个数

              num=inputstream_temp.read(buf);

              pos=0;

             

              //表示已读到文件尾,返回-1

              if( num<0 )

                  return -1;

              temp=buf[pos];

              num--;

              pos++;

             

              //为了防止读入数据如果为11111111后类型转换后变成

              //-1,所以要把多余位上的1去掉用&运算

              return temp&0xff;

           } catch (IOException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

              return -1;

           }         

       }

       else if( num>0 )  //取出缓冲区中的数据

       {

           temp=buf[pos];

           num--;

           pos++;

           return temp&0xff;

       }

       return -1;

    }

   

    //关闭字节流

    public void close()

    {

       try {

           inputstream_temp.close();

       } catch (IOException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

    }

   

}

下面用这个山寨版的BufferedInputStreamMyBufferedInputStream,实现把C://1.mp34M)复制到D盘下,看一下复制所需的时间:

 

测试代码如下:

 

package newjueqi.net.csdn.MyBufferedInputStream;

//测试山寨版的BufferedInputStreamMyBufferedInputStream

import java.io.*;

class TestMyBufferedInputStreamDemo

{

    public static void main(String args[])

    {

       //记录程序开始运行的时间

       long starttime = System.currentTimeMillis();

      

       //记录程序结束运行的时间

       long endtime=0;

      

       try {

           FileInputStream fis=new FileInputStream( "c://1.mp3" );

           MyBufferedInputStream mbis=new MyBufferedInputStream( fis );

          

           FileOutputStream fos=new FileOutputStream( "d://1.mp3");

           BufferedOutputStream bos=new BufferedOutputStream( fos );

          

           int num=0;   

           while( (num=mbis.myRead())!=-1 )

           {

              bos.write( num );

           }

          

           //关闭流

           mbis.close();

           bos.close();

       } catch (FileNotFoundException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       } catch (IOException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

      

       //记录程序结束运行的时间

       endtime=System.currentTimeMillis();      

       System.out.println( "山寨类MyBufferedInputStream共用时"+(endtime-starttime)+"毫秒");

             

    }

}

 

 

运行的结果如下图1

 

                                          1

 

下面我们用java提供的BufferedInputStream类实现上面的文件复制功能,看一下用了多少时间:测试代码如下:

 

//测试BufferedInputStream

 

package newjueqi.net.csdn.MyBufferedInputStream;

import java.io.*;

class TestBufferedInputStreamDemo

{

    public static void main(String args[])

    {

       //记录程序开始运行的时间

       long starttime = System.currentTimeMillis();

      

       //记录程序结束运行的时间

       long endtime=0;

      

       try {

           FileInputStream fis=new FileInputStream( "c://1.mp3" );

           BufferedInputStream mbis=new BufferedInputStream( fis );

          

           FileOutputStream fos=new FileOutputStream( "d://1.mp3");

           BufferedOutputStream bos=new BufferedOutputStream( fos );

          

           int num=0;   

           while( (num=mbis.read())!=-1 )

           {

              bos.write( num );

           }

          

           //关闭流

           mbis.close();

           bos.close();

       } catch (FileNotFoundException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       } catch (IOException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

      

       //记录程序结束运行的时间

       endtime=System.currentTimeMillis();      

       System.out.println( "java提供的BufferedInputStream共用时"+(endtime-starttime)+"毫秒");

             

    }

}

 

运行结果如图2

 

                                   2

 

当然,由于系统方面的原因(而且山寨代码也有很多情况没考虑),并不能以一次数据的测试为准,但从图1和图2的比较可看出,山寨版的BufferedInputStreamMyBufferedInputStream性能还是可以的^-^

本人热切期盼和大家多交流,另外对文章中出现的错误,敬请各位指出,联系方式:

       博客:http://blog.csdn.net/newjueqi

       邮箱:zengjiansheng1@126.com

       QQ190678908

 

原创粉丝点击