java 开发详解_面向接口编程详解-Java篇

相信看到这篇文字的人已经不需要了解什么是接口了,我就不再过多的做介绍了,直接步入正题,接口测试如何编写。那么在这一篇里,我们用一个例子,让各位对这个重要的编程思想有个直观的印象。为充分考虑到初学者,所以这个例子非常简单,望各位高手见谅。

为了摆脱新手的概念,我这里也尽量不用main方法,而采用testNG编写测试用例。

定义:现在我们要开发一个应用,模拟移动存储设备的读写,即计算机与U盘、MP3、移动硬盘等设备进行数据交换。

上下文(环境):已知要实现U盘、MP3播放器、移动硬盘三种移动存储设备,要求计算机能同这三种设备进行数据交换,并且以后可能会有新的第三方的移动存储设备,所以计算机必须有扩展性,能与目前未知而以后可能会出现的存储设备进行数据交换。各个存储设备间读、写的实现方法不同,U盘和移动硬盘只有这两个方法,MP3Player还有一个PlayMusic方法。

名词定义:数据交换={读,写}

解决方案列举

方案一:分别定义FlashDisk、MP3Player、MobileHardDisk三个类,实现各自的Read和Write方法。然后在Computer类中实例化上述三个类,为每个类分别写读、写方法。例如,为FlashDisk写ReadFromFlashDisk、WriteToFlashDisk两个方法。总共六个方法。

方案二:定义抽象类MobileStorage,在里面写虚方法Read和Write,三个存储设备继承此抽象类,并重写Read和Write方法。Computer类中包含一个类型为MobileStorage的成员变量,并为其编写get/set器,这样Computer中只需要两个方法:ReadData和WriteData,并通过多态性实现不同移动设备的读写。

方案三:与方案二基本相同,只是不定义抽象类,而是定义接口IMobileStorage,移动存储器类实现此接口。Computer中通过依赖接口IMobileStorage实现多态性。

方案四:定义接口IReadable和IWritable,两个接口分别只包含Read和Write,然后定义接口IMobileStorage接口继承自IReadable和IWritable,剩下的实现与方案三相同。

下面,我们来分析一下以上四种方案:

首先,方案一最直白,实现起来最简单,但是它有一个致命的弱点:可扩展性差。或者说,不符合“开放-关闭原则”(注:意为对扩展开放,对修改关闭)。当将来有了第三方扩展移动存储设备时,必须对Computer进行修改。这就如在一个真实的计算机上,为每一种移动存储设备实现一个不同的插口、并分别有各自的驱动程序。当有了一种新的移动存储设备后,我们就要将计算机大卸八块,然后增加一个新的插口,在编写一套针对此新设备的驱动程序。这种设计显然不可取。

此方案的另一个缺点在于,冗余代码多。如果有100种移动存储,那我们的Computer中岂不是要至少写200个方法,这是不能接受的!

再看 方案二和方案三,之所以将这两个方案放在一起讨论,是因为他们基本是一个方案(从思想层面上来说),只不过实现手段不同,一个是使用了抽象类,一个是使用了接口,而且最终达到的目的应该是一样的。

我们先来评价这种方案:首先它解决了代码冗余的问题,因为可以动态替换移动设备,并且都实现了共同的接口,所以不管有多少种移动设备,只要一个Read方法和一个Write方法,多态性就帮我们解决问题了。而对第一个问题,由于可以运行时动态替换,而不必将移动存储类硬编码在Computer中,所以有了新的第三方设备,完全可以替换进去运行。这就是所谓的“依赖接口,而不是依赖与具体类”,不信你看看,Computer类只有一个MobileStorage类型或IMobileStorage类型的成员变量,至于这个变量具体是什么类型,它并不知道,这取决于我们在运行时给这个变量的赋值。如此一来,Computer和移动存储器类的耦合度大大下降。

那么 这里该选抽象类还是接口呢?还记得第一篇文章我对抽象类和接口选择的建议吗?看动机。这里,我们的动机显然是实现多态性而不是为了代码复用,所以当然要用接口。

最后 我们再来看一看方案四,它和方案三很类似,只是将“可读”和“可写”两个规则分别抽象成了接口,然后让IMobileStorage再继承它们。这样做,显然进一步提高了灵活性,但是,这有没有设计过度的嫌疑呢?我的观点是:这要看具体情况。如果我们的应用中可能会出现一些类,这些类只实现读方法或只实现写方法,如只读光盘,那么这样做也是可以的。如果我们知道以后出现的东西都是能读又能写的,那这两个接口就没有必要了。其实如果将只读设备的Write方法留空或抛出异常,也可以不要这两个接口。总之一句话:理论是死的,人是活的,一切从现实需要来,防止设计不足,也要防止设计过度。

在这里,我们姑且认为以后的移动存储都是能读又能写的,所以我们选方案三。

实现

下面,我们要将解决方案加以实现。我选择的语言是Java,所以使用其他语言的朋友一样可以参考。

首先编写IMobileStorage接口:

Code:IMobileStorage

1 public interfaceIMobileStorage {2

3 void Read(); //读取数据

4 void Write(); //写入数据

5

6 }

代码比较简单,只有两个方法,没什么好说的,接下来是三个移动存储设备的具体实现代码:

U盘

Code:FlashDisk

1 public class FlashDisk implementsIMobileStorage{2 @Override3 public voidRead() {4 System.out.println("Reading from FlashDisk……");5 System.out.println("Read finished!");6 }7

8 @Override9 public voidWrite() {10 System.out.println("Writing to FlashDisk……");11 System.out.println("Write finished!");12 }13 }

MP3

Code:MP3Player

public class MP3Player implementsIMobileStorage{

@Overridepublic voidRead() {

System.out.println("Reading from MP3Player……");

System.out.println("Read finished!");

}

@Overridepublic voidWrite() {

System.out.println("Writing to MP3Player……");

System.out.println("Write finished!");

}public voidPlayMusic(){

System.out.println("Music is playing……");

}

}

移动硬盘

Code:MobileHardDisk

public class MobileHardDisk implementsIMobileStorage{

@Overridepublic voidRead() {

System.out.println("Reading from MobileHardDisk……");

System.out.println("Read finished!");

}

@Overridepublic voidWrite() {

System.out.println("Writing to MobileHardDisk……");

System.out.println("Write finished!");

}

}

可以看到,它们都实现了IMobileStorage接口,并重写了各自不同的Read和Write方法。下面,我们来写Computer:

Code:Computer

public classComputer {privateIMobileStorage _usbDrive;publicIMobileStorage get_usbDrive() {return_usbDrive;

}public voidset_usbDrive(IMobileStorage _usbDrive) {this._usbDrive =_usbDrive;

}publicComputer(){}publicComputer(IMobileStorage _usbDrive) {this._usbDrive =_usbDrive;

}public voidReadData(){this._usbDrive.Read();

}public voidWriteData(){this._usbDrive.Write();

}

}

其中的UsbDrive就是可替换的移动存储设备,之所以用这个名字,是为了让大家觉得直观,就像我们平常使用电脑上的USB插口插拔设备一样。

OK!下面我们来测试我们的“电脑”和“移动存储设备”是否工作正常。我是用的Java控制台程序打印结果,具体代码如下:

Code:测试代码

public classToTest {

@Testpublic voidprogram1(){

Computer computer= newComputer();

IMobileStorage mp3Player= newMP3Player();

IMobileStorage flashDisk= newFlashDisk();

IMobileStorage moblieHardDisk= newMobileHardDisk();

System.out.println("I inserted my MP3 Player into my computer and copy some music to it:");

computer.set_usbDrive(mp3Player);

computer.WriteData();

System.out.println("====================");

System.out.println("Well,I also want to copy a great movie to my computer from a mobile hard disk:");

computer.set_usbDrive(moblieHardDisk);

computer.ReadData();

System.out.println("====================");

System.out.println("OK!I have to read some files from my flash disk and copy another file to it:");

computer.set_usbDrive(flashDisk);

computer.ReadData();

computer.WriteData();

System.out.println();

}

运行结果如下:

81db5f27258695569bb5da454d66d4fe.png

图2.1 各种移动存储设备测试结果

好的,看来我们的系统工作良好。

后来……

刚过了一个星期,就有人送来了新的移动存储设备NewMobileStorage,让我测试能不能用,我微微一笑,心想这不是小菜一碟,让我们看看面向接口编程的威力吧!将测试程序修改成如下:

(NewMobileStorage的类请参照u盘、移动硬盘等类编写……也可以自创)

测试代码

@Testpublic voidprogram2(){

Computer computer= newComputer();

IMobileStorage newMobileStorage= newNewMoblieStorage();

computer.set_usbDrive(newMobileStorage);

newMobileStorage.Write();

newMobileStorage.Read();

}

运行结果:

a23d76452386e9b582d5323217d3d9d9.png

图2.2 新设备扩展测试结果

又过了几天,有人通知我说又有一个叫SuperStorage的移动设备要接到我们的Computer上,我心想来吧,管你是“超级存储”还是“特级存储”,我的“面向接口编程大法”把你们统统搞定。

但是,当设备真的送来,我傻眼了,开发这个新设备的团队没有拿到我们的IMobileStorage接口,自然也没有遵照这个约定。这个设备的读、写方法不叫Read和Write,而是叫rd和wt,这下完了……不符合接口啊,插不上。但是,不要着急,我们回到现实来找找解决的办法。我们一起想想:如果你的Computer上只有USB接口,而有人拿来一个PS/2的鼠标要插上用,你该怎么办?想起来了吧,是不是有一种叫“PS/2-USB”转换器的东西?也叫适配器,可以进行不同接口的转换。对了!程序中也有转换器。

这里,我要引入一个设计模式,叫“Adapter”。它的作用就如现实中的适配器一样,把接口不一致的两个插件接合起来。由于本篇不是讲设计模式的,而且Adapter设计模式很好理解,所以我就不细讲了,先来看我设计的类图吧:

如图所示,虽然SuperStorage没有实现IMobileStorage,但我们定义了一个实现IMobileStorage的SuperStorageAdapter,它聚合了一个SuperStorage,并将rd和wt适配为Read和Write,SuperStorageAdapter(这里注意自行编写SuperStorage的类和他用到的接口)

8bcf6343b52e94fd06c94836d6fb3edc.png

图2.3 Adapter模式应用示意

具体代码如下:

Code:SuperStorageAdapter

1 public class SuperStorageAdapter implementsIMobileStorage {2 privateSuperStorage _superStorage;3

4 publicSuperStorage get_superStorage() {5 return_superStorage;6 }7

8 public voidset_superStorage(SuperStorage _superStorage) {9 this._superStorage =_superStorage;10 }11

12 @Override13 public voidRead(){14 this._superStorage.rd();15 }16

17 @Override18 public voidWrite() {19 this._superStorage.wt();20 }21 }

好,现在我们来测试适配过的新设备,测试代码如下:

Code:测试代码

@Testpublic voidprogram3(){

Computer computer= newComputer();

SuperStorageAdapter superStorageAdapter= newSuperStorageAdapter();

SuperStorage superStorage= newSuperStorage();

superStorageAdapter.set_superStorage(superStorage);

System.out.println("Now,I am testing the new super storage with adapter:");

computer.set_usbDrive(superStorageAdapter);

computer.ReadData();

computer.WriteData();

System.out.println();

}

运行结果:

26127719417a07436fcc4da22113f480.png

图2.4 利用Adapter模式运行新设备测试结果

OK!虽然遇到了一些困难,不过在设计模式的帮助下,我们还是在没有修改Computer任何代码的情况下实现了新设备的运行。希望各位朋友结合第一篇的理论和这个例子,仔细思考面向接口的问题。当然,不要忘了结合现实。

原文是C#的语言,在这里做了java的转换编写,感谢原作者,转自C站的一小平民:http://blog.csdn.net/boer521314/article/details/40378151

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/551015.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java入栈_java中代码块的执行,也会有入栈的步骤吗?

首先这个问题很有意思,不过题主没具体指明放在何处的代码块。这里至少有三种情况,第一种就是在普通的方法里面,第二种是实例初始化代码块,第三种是静态初始化代码块。第一种情况使用javap反汇编了一下有代码块的代码和无代码块的代…

java md5 密钥_Java 生成16/32位 MD5密钥串

注意!网上广为流传的MD5计算的版本,与标准MD5计算结果不同(原因可能是编码方式的不同)。请注意甄别。以下代码是经过测试的正确版本。public class MD5 {private static final char HEX_DIGITS[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F };p…

java observer模式_Java观察者模式(Observer)详解及应用

Java观察者模式(Observer)详解及应用(2011-12-15 14:03:30)标签:杂谈Java观察者模式(Observer)详解及应用由于网站带有弱sns功能,因此需要设计关注和被关注的消息或是动作通知,那么将这个需求抽象出来的时候就会发现正好符合java中订阅者模式…

java 反射创建对象并赋值_java使用反射创建并操作对象的方法

Class 对象可以获得该类里的方法(由 Method 对象表示)、构造器(由 Constructor 对象表示)、成员变量(由 Field 对象表示),这三个类都位于 java.lang.reflect 包下,并实现了 java.lang.reflect.Member 接口。程序可以通过对象来执行对应的方法&#xff0c…

cad注释比例和打印比例不一样_cad注释比例(cad注释比例与打印比例)

这两个比例有什么区别 分别代表的是什么意思 麻烦说详细点 谢谢是物体被缩小了50倍后的图形这两个比例。但是一张图纸不允许出现两个或以上的比例(局部放大除外),但是标注的数字是实际尺寸,两个没有区别画的是800*500mm的线,注释比例用的1:1&…

java bundle管理_java.util.ResourceBundle使用详解

一、认识国际化资源文件这个类提供软件国际化的捷径。通过此类,可以使您所编写的程序可以:轻松地本地化或翻译成不同的语言一次处理多个语言环境以后可以轻松地进行修改,支持更多的语言环境说的简单点,这个类的作用就是读取资源属…

java mac postgresql_PostgreSQL 的安装与使用 for mac

##安装 在 mac 下,可以利用 homebrew 直接安装 PostgreSQL:brew install postgresql安装好之后有一个数据库(postgres),如需要重新初始化数据库:initdb /usr/local/var/postgres_mao启动:pg_ctl -D /usr/local/var/pos…

疯狂java讲义价格_疯狂java讲义

封装:封装:将对象状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是用类所提供的方法访问和操作。访问控制符:private(当前类访问权) ,protected(子类访问),public(公共访问),default(包访问…

java源代码实例倒计时_Java倒计时三种实现方式代码实例

写完js倒计时,突然想用java实现倒计时,写了三种实现方式一:设置时长的倒计时;二:设置时间戳的倒计时;三:使用java.util.Timer类实现的时间戳倒计时代码如下:package timer;import ja…

java 加密 encrypt_JAVA Encrypter 加密算法

Java代码/*** 加密者** time: 2007-8-29 下午05:45:36*/public final class Encrypter {private static Cipher ecipher;private static Cipher dcipher;// 必须24个字符private static final String key "*:1$7!a*:1$7!a*:1$7!^";private static final String alg …

java base64 加解密_java Base64加解密

import sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder;public class Base64Util {public static void main( String[] args ) {String data2 " var re1/[a-zA-Z]/g; return (FIELD_VALUE.match(re1)).length;";try {// BASE64加密BASE64Encoder encoder n…

python svn库_python实现svn新老库迁移

1 #!/usr/bin/env python2 #codingutf-834 #启动环境:linux,安装了python,svn客户端,并且具备python部分依赖包,xlrd,shutil,如果没有网上搜索安装5 #启动方式:将文件MvSvnLibToNew.…

python二维表转一维表_二维表格转换成一维表格

# 加载数据import pandas as pddf_old1 pd.read_excel(r"D:\Jupyter\data\Python.xlsx",sheet_name "变一维")df_old1# 数据清洗,把第一列设为索引列df_old2 pd.read_excel(r"D:\Jupyter\data\Python.xlsx",sheet_name "变一…

java web后台_java web 后台那些事

java web 后台运行原理当Web服务器接收到一个HTTP请求时,它会先判断请求内容——如果是静态网页数据,Web服务器将会自行处理,然后产生响应信息;如果牵涉到动态数据,Web服务器会将请求转交给Servlet容器。此时Servlet容…

MyBatis的缓存!!!!

1.一级缓存&#xff08;默认开启&#xff09; (1) 首先在UserMapper接口定义两个方法&#xff1a; package com.by.mapper;import com.by.pojo.User; import org.apache.ibatis.annotations.Param; import org.junit.Test;import java.util.List;/*** <p>Project: myb…

java动态语言_探秘Java 7:JVM动态语言支持详解

JDK 7 增加了对 JSR 292 的支持&#xff0c;在 JVM 中动态类型语言的运行速度将变得更快。这一支持的关键在于增加了新的 Java 字节码&#xff0c;invokedynamic&#xff0c;它用于方法调用&#xff0c;还有新的连接机制&#xff0c;其中包含了一个新的构造&#xff1a;方法句柄…

java 反射机制 视频_JAVA反射机制及其原理实现

9.1 概念JAVA反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意方法和属性&#xff1b;public、protected、private。OO(面向对象)&#xff0c;private私有的&#x…

python模型的属性是什么_python – Django:为什么Django模型字段的类属性?

不,原因与此相同&#xff1a;>>> class Foo(object):... bar Foo attribute...>>> f Foo()>>> f.barFoo attribute>>> Foo.barFoo attribute>>> f.bar instance attribute>>> f.barinstance attribute>>> F…

java 安卓下载文件_GitHub - Charay/downloadfile: 使用Retrofit2+Rxjava+Rxandroid+okhttp的方式下载文件并存储到sd卡指定目录...

downloadfile使用Retrofit2RxjavaRxandroidokhttp的方式下载文件并存储到sd卡指定目录使用&#xff1a;gradleStep 1.在工程build.gradle文件中加入maven地址repositories:allprojects {repositories {...maven { url https://jitpack.io }}}Step 2. 在module的build.gradle中添…

java map 队列_Java:queue队列,map集合

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼Queue&#xff1a; 基本上&#xff0c;一个队列就是一个先入先出(FIFO)的数据结构Queue接口与List、Set同一级别&#xff0c;都是继承了Collection接口。LinkedList实现了Deque接 口示例代码&#xff1a;1. import java.util.Linke…