Java编程思想,读书笔记七(第9章 接口)

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u010882234/article/details/79230915

       第9章 接口

       接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。

       抽象类,普通类与接口之间的一种中庸之道。抽象类的对象几乎没有任何意义,创建抽象类是希望通过这个通用接口操作一系列类。Java提供一个叫做抽象方法的机制,这个方法是不完整的,仅有声明而没有方法体。形如:abstract void f();包含抽象方法的类叫抽象类。如果一个类包含一个或多个抽象方法,这个类必须声明为抽象的,否则编译器就会报错。给抽象类创建对象是不安全的,编译器会给出一条出错信息。从抽象类继承得到新类,如果想要给新类创建对象,则必须为基类的所有抽象方法提供方法定义。如果不这么做(全部方法定义),则新类也必须是抽象类。创建抽象类和抽象方法非常有用,因为它们可以使类的抽象性明确起来,并告诉用户和编译器怎么使用它们。抽象类还是很重要的重构工具,因为它们可以使得我们可以很容易地将公共方法沿着继承层次结构向上移动。

      interface关键字使抽象的概念更向前迈进了一步。abstract关键字允许人们在类中创建一个或多个没有任何定义的方法--提供了接口部分,但是没有提供任何相应的具体实现,这些实现是由此类的继承者实现的。interface关键字产生一个完全抽象的类,它根本就没有提供任何具体实现。它允许创建者确定方法名、参数列表和返回类型,但没有任何方法体。接口提供了形式,而没有提供任何具体实现。

     一个接口表示:“所有实现了该特定接口的类看起来就像这样”。接口被用来建立类与类之间的协议。但是interface不仅仅是一个极其抽象的类,因为它允许人们通过创建一个能够被向上转型为多种基类的类型,来实现某种类似多重继承变种的特性。接口里也可以包含域,但是这些域是隐式地是static和final的。要让一个类遵循一个或多个接口,需要使用implements关键字,它表示:interface只是它的外貌,但是现在我要声明它是如何工作的。可以在接口里显式的声明为public,但是没有必要,它们自动是public的。

       完全解耦。要使用一个类的方法,必须使用它创建的对象,而接口不必如此,它使我们可以编写可复用性更好的代码。例如,Processor类有一个name()方法和process()方法,这个类作为基类而被扩展。Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。像本例这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略(Strategy)设计模式。这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。这里Processor对象就是一个策略,在main里可以看到三种不同类型的策略应用到了String类型的s对象上。

package interfaces;

import java.util.Arrays;

class Processor {
	public String name() {
		return getClass().getSimpleName();
	}
	Object process(Object input) {
		return input;
	}
}

class Upcase extends Processor{
	String process(Object input) {
		return ((String)input).toUpperCase();
	}
}

class Downcase extends Processor{
	String process(Object input) {
		return ((String)input).toLowerCase();
	}
}

class Splitter extends Processor{
	String process(Object input) {
		return Arrays.toString(((String)input).split(" "));
	}
}

public class Apply {
	public static void process(Processor p,String s) {
		System.out.println("Using Processor,"+p.name());
		System.out.println(p.process(s));
	}
	public static String s = "Time and tide, wait for no man!";
	public static void main(String[] args) {
		process(new Upcase(), s);
		process(new Downcase(), s);
		process(new Splitter(), s);
	}
}
Using Processor,Upcase
TIME AND TIDE, WAIT FOR NO MAN!
Using Processor,Downcase
time and tide, wait for no man!
Using Processor,Splitter
[Time, and, tide,, wait, for, no, man!]

       现在假设发现了一组电子滤波器(Waveform),它看起来适用于Apply.process()方法,但是Apply.process()方法和Processor之间的耦合过紧,已经超出了Waveform需要的程度。

package filter;

public class Waveform {
	private static long counter;
	private final long id = counter++;
	public String toString() {
		return "Waveform "+id;
	}
}

package filter;

public class Filter {
	public String name() {
		return getClass().getSimpleName();
	}
	public Waveform process(Waveform input) {
		return input;
	}
}
package filter;

public class HighPass extends Filter{
	double cutoff;
	public HighPass(double cutoff) {
		this.cutoff = cutoff;
	}
	public Waveform process(Waveform input) {
		return input;
	}
}
package filter;

public class LowPass extends Filter{
	double cutoff;
	public LowPass(double cutoff) {
		this.cutoff = cutoff;
	}
	public Waveform process(Waveform input) {
		return input;
	}
}

package filter;

public class BandPass extends Filter{
	double lowCutoff,highCutoff;
	public BandPass(double lowCut,double highCut) {
		lowCutoff = lowCut;
		highCutoff = highCut;
	}
	public Waveform process(Waveform input) {
		return input;
	}
}

      Filter和Processor具有相同的接口元素,但是它并非继承自Processor--因为Filter类的创建者压根不清楚你想要将它用作Processor--因此你不能将Filter用于Apply.process()方法,即便这样做可以正常运行。这里主要是因为Apply.process()方法和Processor之间的耦合过紧,已经超出了所需要的限度。这就使得复用Apply.process()方法时,复用却被禁止了。但是如果Processor是一个接口,那么这些限制就会变得松动,使得你可以复用结构该借款Apply.process()方法。下面是Processor和Apply的修改版本。

package filter;

public interface Processor {
	String name();
	Object process(Object input);
	
}

package filter;

public class Apply {
	public static void process(Processor p,Object s) {
		System.out.println("Using Processor ,"+p.name());
		System.out.println(p.process(s));
	}
}
      复用代码的第一种方式,是客户端程序员遵循接口来编写自己的类,就像下面这样:
package filter;

import java.util.Arrays;

/**
 * 实现Processor接口
 * @author Administrator
 *
 */
public abstract class StringProcessor implements Processor{
	public String name() {
		return getClass().getSimpleName();
	}
	public abstract String process(Object input);
	public static String s = "If she weighs the same as a duck, she's made of wood";
	public static void main(String[] args) {
		Apply.process(new Upcase(), s);
		Apply.process(new Downcase(), s);
		Apply.process(new Splitter(), s);
	}
}

class Upcase extends StringProcessor{
	public String process(Object input) {
		return ((String)input).toUpperCase();
	}
}

class Downcase extends StringProcessor{
	public String process(Object input) {
		return ((String)input).toLowerCase();
	}
}

class Splitter extends StringProcessor{
	public String process(Object input) {
		return Arrays.toString(((String)input).split(" "));
	}
}
      输出打印结果
Using Processor ,Upcase
IF SHE WEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD
Using Processor ,Downcase
if she weighs the same as a duck, she's made of wood
Using Processor ,Splitter
[If, she, weighs, the, same, as, a, duck,, she's, made, of, wood]

      但是,你经常碰到的情况是你无法修改你想要使用的类,因为这些类可能已经被使用或者不应该被修改。例如,在电子滤波器的例子中,类库是被发现而不是被创建的。这样的情况下,可以使用适配器(Adapter)设计模式。适配器中的代码将接受你所拥有的接口,并产生你所需要的接口,就像下面这样:

package filter;
/**
 * 适配器模式,FilterAdapter实现Processor接口,提供Waveform所需要的方法,达到复用Apply.process()方法
 * @author Administrator
 *
 */
class FilterAdapter implements Processor{
	Filter filter;
	public FilterAdapter(Filter filter) {
		this.filter = filter;
	}
	public String name() {
		return filter.name();
	}
	public Waveform process(Object input) {
		return (Waveform)input;
	}
}

public class FilterProcessor{
	public static void main(String[] args) {
		Waveform waveform = new Waveform();
		Apply.process(new FilterAdapter(new LowPass(1.0)), waveform);
		Apply.process(new FilterAdapter(new HighPass(2.0)), waveform);
		Apply.process(new FilterAdapter(new BandPass(3.0,4.0)), waveform);
	}
}
输出打印

Using Processor ,LowPass
Waveform 0
Using Processor ,HighPass
Waveform 0
Using Processor ,BandPass
Waveform 0

       在这种使用适配器的方式中,FilterAdapter的构造器接受你所拥有的接口Filter,然后生成具有你所需要的Processor接口的对象。而且,FilterAdapter中用到了代理。将接口从具体实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也就更具有复用性。

       Java中的伪多重继承机制。在C++中,组合多个类的接口的行为被称为多重继承。它可能会使你背上沉重的包袱,因为每个类都有一个具体实现。在Java中,你同样可以执行相同的行为,但是,Java是单根继承的。只有一个类可以继承(extends),可以有具体实现;与此同时,可以实现(implements)多个接口(interface)。使用接口的核心原因:一是为了能够向上转型为多个基类型,以及由此而带来的灵活性;二是防止客户端程序员创建该类的对象,并确保这仅仅是建立了一个接口。我们需要选择抽象类还是接口呢?如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。如果知道某事物需要成为一个基类,那么第一选择应该是使它成为一个接口。

      通过继承来扩展接口。接口可以继承(extends)接口,使用extends可以扩展接口。其中需要注意的是,一般情况下,只可以将extends用于单一类,但是可以引用多个基类接口,只需用逗号将接口名一一隔开即可。

      接口最吸引人的原因之一就是允许同一个接口具有多个不同的实现。在最简单的情况中,它的体现形式通常是一个接受接口类型的方法,而该接口的实现和向该方法传递的对象则取决于方法的使用者。因此,接口的一种常见用法就是前面提到的策略设计模式,此时你编写一个执行某些操作的方法,而该方法将接受一个同样是你指定的接口。你主要就是要声明:“你可以用任何你想要对象来调用我的方法,只要你的对象遵循我的接口。”这使得你的方法更加灵活、通用,并更加可复用性。

      接口与工厂。接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法(factory)设计模式。这与直接调用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象理论上,通过这种方式,我们的代码将完全与接口的实现分离,这就使得我们可以透明地将某个实现替换为另一个实现。下面我们实例展示工厂方法的结构:

package filter;

interface Service{
	void method1();
	void method2();
}

interface ServiceFactory{
	Service getService();
}

class Implementation1 implements Service{
	Implementation1() {}
	public void method1() {
		System.out.println("Implementation1 method1");
	}
	public void method2() {
		System.out.println("Implementation1 method2");
	}
	
}

class Implementation1Factory implements ServiceFactory{
	public Service getService() {
		return new Implementation1();
	}
} 

class Implementation2 implements Service{
	Implementation2() {}
	public void method1() {
		System.out.println("Implementation2 method1");
	}
	public void method2() {
		System.out.println("Implementation2 method2");
	}
	
}

class Implementation2Factory implements ServiceFactory{
	public Service getService() {
		return new Implementation2();
	}
}

public class Factories {
	public static void serviceConsumer(ServiceFactory factory) {
		Service service = factory.getService();
		service.method1();
		service.method2();
	}
	public static void main(String[] args) {
		serviceConsumer(new Implementation1Factory());
		serviceConsumer(new Implementation2Factory());
		
	}
}
      输出打印如下:

Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2

      如果不是工厂方法,你的代码就必须在某处指定要创建的Service的确切类型,以便调用合适的构造器。为什么我们想要添加这种额外级别的间接性呢?一个常见的原因就是要创建框架。当然,对于创建类,在任何时刻,都可以替代为创建一个接口和一个工厂。

      下一篇: Java编程思想,读书笔记八(第10章 内部类) - CSDN博客  

猜你喜欢

转载自blog.csdn.net/u010882234/article/details/79230915