Java中的面向接口编程

面向接口编程是很多软件架构设计理论都倡导的编程方式,学习Java自然少不了这一部分,下面是我在学习过程中整理出来的关于如何在Java中实现面向接口编程的知识。分享出来,有不对之处还请大家指正。

接口体现的是一种规范和实现分离的设计哲学,充分利用接口可以极好地降低程序各模块之间的耦合,从而提高系统的可扩展性和可维护性。基于这种原则,通常推荐“面向接口”编程,而不是面向实现类编程,希望通过面向接口编程来降低程序的耦合。下面分两种常用场景来示范“面向接口”编程的优势。

(一)简单工厂模式

有一个场景,假设程序中有个Comupter类需要组合一个输出设备,现在有两个选择:直接让Comupter该类组合一个Printer属性,或者让Comupter组合一个Output属性,那么到底采用哪种方式更好呢?

假设让Computer组合一个Printer属性,如果有一天系统需要重构,需要使用BetterPrinter来代替Printer,于是我们需要打开Computer类源代码进行修改。如果系统中只有一个Computer类组合了Printer属性还好,如果系统中有100个类组合了Printer属性,甚至1000个,10000个……将意味着我们要打开100个、1000个、10000类进行修改,这是多么大的工作量!

为了避免这个问题,我们让Comupter组合一个Output属性,将Comupter类与Printer类完全分离。Computer对象实际组合的是Printer对象,还是BetterPrinter对象,对Computer而言完全透明。当Printer对象切换到BetterPrinter对象时,系统完全不受影响。下面是这个Computer类定义的代码。

public class Computer

{

private Output out;

public Computer(Output out)

{

this.out = out;

}

//定义一个模拟获取字符串输入的方法

public void keyIn(String msg)

{

out.getData(msg);

}

//定义一个模拟打印的方法

public void print()

{

out.out();

}

}

上面的Computer类已经完全与Printer类分离开,只是与Output接口耦合。Computer不再负责创建Output对象,系统提供一个Output工厂来负责生成Output对象。这个OutputFactory工厂类代码如下:

public class OutputFactory

{

public Output getOutput()

{

return new Printer();

}

public static void main(String[] args)

{

OutputFactory of = new OutputFactory();

Computer c = new Computer(of.getOutput());

c.keyIn("疯狂Java讲义");

c.keyIn("轻量级J2EE企业应用实战");

c.print();

}

}

在该OutputFactory类中包含了一个getOutput方法,该方法返回一个Output实现类的实例,该方法负责创建Output实例,具体创建哪一个实现类的对象由该方法决定(具体由该方法中粗体部分控制,当然也可以增加更复杂的控制逻辑)。如果系统需将Printer改为BetterPrinter实现类,只需要让BetterPrinter实现Output接口,并改变OutputFactory类中的getOutput方法即可。

下面是BetterPrinter实现类的代码,BetterPrinter只是对原有的Printer进行简单修改,以模拟系统重构后的改进。

public class BetterPrinter implements Output

{

private String[] printData = new String[MAX_CACHE_LINE * 2];

//用以记录当前需打印的作业数

private int dataNum = 0;

public void out()

{

//只要还有作业,继续打印

while(dataNum > 0)

{

System.out.println("高速打印机正在打印:" + printData[0]);

//把作业队列整体前移一位,并将剩下的作业数减1

System.arraycopy(printData , 1, printData, 0, --dataNum);

}

}

public void getData(String msg)

{

if (dataNum >= MAX_CACHE_LINE * 2)

{

System.out.println("输出队列已满,添加失败");

}

else

{

//把打印数据添加到队列里,已保存数据的数量加1。

printData[dataNum++] = msg;

}

}

}

上面的BetterPrinter类也实现了 Output接口,因此也可当成Output对象使用,于是我们只要把OutputFactory工厂类的getOutput方法中粗体部分改为如下代码:

return new BetterPrinter();

再次运行前面的OutputFactory.java程序,发现系统运行时已经改为BetterPrinter对象,而不再是原来的Printer对象。

通过这种方式,我们把所有生成Output对象的逻辑集中在OutputFactory工厂类中管理,而所有需要使用Output对象的类只需与Output接口耦合,而不是与具体的实现类耦合。即使系统中有很多类使用了Printer对象,只要OutputFactory类的getOutput方法来生成Output对象是BetterPrinter对象,则它们全部都会改为使用BetterPrinter对象,而所有程序无需修改,只需要修改OutputFactory工厂的getOutput的方法实现即可。

(二)命令模式

考虑这样一种场景:某个方法需要完成某一个行为,但这个行为的具体实现无法确定,必须等到执行该方法时才可以确定。具体一点:假设有个方法需要遍历某个数组的数组元素,但无法确定在遍历数组元素时如何处理这些元素,需要在调用该方法时指定具体的处理行为。

这个要求看起来有点奇怪:这个方法需要不仅要普通数据可以变化,甚至还有方法执行体也需要变化,难道我们能把“处理行为”作为一个参数传入该方法?

对于这样一个需求,我们必须把“处理行为”作为参数传入该方法,这个“处理行为”用编程来实现就是一段代码。那如何把这段代码传入该方法呢?

因为Java不允许代码块单独存在,因此我们使用一个Command接口来定义一个方法,用这个方法来封装“处理行为”。下面是该Command接口代码。

public interface Command

{

//接口里定义的process方法用于封装“处理行为”

void process(int[] target);

}

上面的Command接口里定义了一个process方法,这个方法用于封装“处理行为”,但这个方法没有方法体——因为现在还无法确定这个处理行为。

下面是需要处理数组的处理类,在这个处理类中包含一个process方法,这个方法无法确定处理数组的处理行为,所以定义该方法时使用了一个Command参数,这个Command参数负责对数组的处理行为。该类的程序代码如下。

public class ProcessArray

{

public void process(int[] target , Command cmd)

{

cmd.process(target);

}

}

通过一个Command类,就实现了让ProcessArray类和具体“处理行为”的分离,程序使用Command接口代表了对数组的处理行为。Command接口也没有提供真正的处理,只有等到需要调用ProcessArray对象process方法时,才真正传入一个Command对象,才确定对数组的处理行为。

下面程序示范了对数组的两种处理方式:

public class TestCommand

{

public static void main(String[] args)

{

ProcessArray pa = new ProcessArray();

int[] target = {3, -4, 6, 4};

//第一次处理数组,具体处理行为取决于PrintCommand

pa.process(target , new PrintCommand());

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

//第二次处理数组,具体处理行为取决于AddCommand

pa.process(target , new AddCommand());

}

}

下面分别是PrintCommand类和AddCommand类的代码。

public class PrintCommand implements Command

{

public void process(int[] target)

{

for (int tmp : target )

{

System.out.println("迭代输出目标数组的元素:" + tmp);

}

}

}

public class AddCommand implements Command

{

public void process(int[] target)

{

int sum = 0;

for (int tmp : target )

{

sum += tmp;

}

System.out.println("数组元素的总和是:" + sum);

}

}

对于PrintCommand和AddCommand两个实现类而言,实际有意义的部分就是process(int[] target)方法,该方法的方法体就是传入ProcessArray类里process方法的“处理行为”,通过这种方式就可实现process方法和“处理行为”的分离。

时间: 2024-08-06 13:13:31

Java中的面向接口编程的相关文章

java中面向接口编程

面向接口编程详解(一)——思想基础 我想,对于各位使用面向对象编程语言的程序员来说,“接口”这个名词一定不陌生,但是不知各位有没有这样的疑惑:接口有什么用途?它和抽象类有什么区别?能不能用抽象类代替接口呢?而且,作为程序员,一定经常听到“面向接口编程”这个短语,那么它是什么意思?有什么思想内涵?和面向对象编程是什么关系?本文将一一解答这些疑问. 1.面向接口编程和面向对象编程是什么关系 首先,面向接口编程和面向对象编程并不是平级的,它并不是比面向对象编程更先进的一种独立的编程思想,而是附属于面向

java面向接口编程

在oop中有一种设计原则是面向接口编程,面向接口编程有非常多优点,详细百度一大片.我来谈一下详细的使用中的一些不成熟的见解.! 首先面向接口编程能够消除类之间的依赖关系,使得业务仅仅依赖接口. 这样有什么优点呢? 这种优点大大的.比方说我们声明一个car的接口.我们对车有下面行为,我们能够开车.洗车,推车,修车,拆车等等, 详细用代码实现: package com.panther.dong.faceinterface; /** * Created by panther on 15-8-9. */

关于Java中继承和接口的理解

关于Java中继承和接口的理解 Java语言中,为了实现代码重用,设计了继承这一机制,但是,其设计成单继承,这样设计是有原因的,如下图: Figure1:deadly diamond of death 此图问题称为菱形问题(diamond problem),就是说,当A的子类B和C同时实现了A中的方法,则同时继承了B和C的子类D在调用该方法时会出现混乱,无法得知该调用哪一个方法. 既然不能实现多继承,我们就会考虑把很多方法就写在父类里,或者继承抽象类,实现其方法,但是,这样会导致一个问题,比如说

从头认识设计模式-策略模式-05-引入设计原则:面向接口编程

这一章节我们来讨论一下怎么解决上一章节扩展性差的问题. 1.解决方案 面向接口编程 2.思路 使用java的多态性,动态的设置导入导出的行为 3.代码清单 在Base里面使用导入导出的接口,然后增加一个通用的导出导入方法,下面的实现中,我们只需要设置不同的导入导出行为,即可通过导入导出方法来实现不同的导入导出结果. package com.raylee.designpattern.strategy.ch05; /** * 1.0 这个类是我们需要使用设计模式改进的原始类,也就是策略模式应用的初始

java中抽象类和接口的区别?

简单来说,接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私有方法或私有变量的, 另外,实现接口的一定要实现接口里定义的所有方法,而实现抽象类可以有选择地重写需要用到的方法, 一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现. 还有,接口可以实现多重继承,而一个类只能继承一个超类, 但可以通过继承多个接口实现多重继承, 接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量全是常量)的作用. 接口和抽象类都是继承树的上层,他

面向接口编程

系列——MEF实现设计上的“松耦合”(终结篇:面向接口编程) 序:忙碌多事的八月带着些许的倦意早已步入尾声,金秋九月承载着抗战胜利70周年的喜庆扑面而来.没来得及任何准备,似乎也不需要任何准备,因为生活不需要太多将来时.每天忙着上班.加班.白加班,忘了去愤,忘了去算计所谓的价值.天津爆炸事故时刻警示着我们生命的无常,逝者安息,活着的人生活还得继续,珍惜生命,远离伤害.武汉,这座炙热的城市,虽值金秋,却依然经受着“秋老虎”的烘烤,马路上蒸腾的热气迎面袭来,全身毛孔张开,汗流不止,在这般高温下,似乎

那些年搞不懂的高深术语——依赖倒置?控制反转?依赖注入?面向接口编程

那些年,空气中仿佛还能闻到汉唐盛世的余韵,因此你决不允许自己的脸上有油光,时刻保持活力.然而,你一定曾为这些“高深术语”感到过困扰.也许时至今日,你仍对它们一知半解.不过就在今天,这一切都将彻底改变!我将带领你以一种全新的高清视角进入奇妙的编程世界,领略涵泳在这些“高深术语”中的活泼泼的地气,以及翩跹于青萍之末的云水禅心. ·内聚 内聚,通俗的来讲,就是自己的东西自己保管,自己的事情自己做. 经典理论告诉我们,程序的两大要素:一个是数据(data),一个是操作(opration).而 PASCA

面向“接口”编程和面向“实现”编程

来自http://www.vaikan.com/program-to-an-interface-fool/ 面向'接口'编程,而不是面向'实现'. 这是什么意思? 首先我们需要理解什么是'接口',什么是'实现'.简言之,一个接口就是我们要调用的一系列方法的集合,有对象将会响应这些方法调用. 一个实现就是为接口存放代码和逻辑的地方. 本质上讲,这个原则倡导的是,当我们写一个函数或一个方法时,我们应该引用相应的接口,而不是具体的实现类. 面向'实现'编程 首先我们看看,如果不遵循这个原则会发生什么.

Java中的图形界面编程

前言 正文 Java中的图形界面编程 AWT/Swing AWT(Abstract Window ToolKits,抽象窗口工具集) 1.容器类:用来存储组件,实现容器布局 2.组件类:实现界面的一些特定功能 一个容器可以包涵多个组件,组件必须存放在容器中 3.布局管理器:实现容器的布局设置 4.图形类:包括一些基本图形 Swing是AWT的一个轻量级框架 java.lang.Object java.awt.Component java.awt.Container java.awt.Window