Java编程思想学习笔记九: 接口

    接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。这种机制在编程语言中并不通用,Java语言提供了关键字说明人们认为这些思想是重要的。还有另一个概念叫做抽象类,它是介于普通的类与接口之间的一个中庸之道。尽管在构造某些具有未实现方法的类时,你第一想到的可能是接口,但是抽象类仍旧是一种用于此目的的一种重要而必须的工具。因为你不可能总是使用纯接口。

一、抽象类和抽象方法

    在之前章节关于多态的例子中,基类方法往往没有具体的操作,它存在的目的是为了它所有的子类提供一个统一的接口。通过向上转型、动态绑定等操作来实现子类的各自多样化的功能。不同的子类可以用不同的方式表示此接口,通用接口建立起一种基本的形式,以表示所有子类的共同部分。我们将这种基类称作抽象基类,或者抽象类。
    如果我们创建了一个这样的抽象类,因为它的方法存在的意义只是提供一个接口,那么说明这个基类的对象没有任何意义。并且我们还想阻止使用者创建基类的对象,所以我们可以在抽象类中的方法返回一些错误的内容,但是这样做有两个问题,一方面错误要在运行时才能抛出,另一方面错误带来的影响会不可估计。为此Java提供了一种新的方法,叫做抽象方法,这种方法是不完整的,它只有方法的声明,没有方法体。表示该方法是抽象的,不需要在基类中实现的,只是作为所有子类的一个公共的接口。抽象方法使用abstract关键字声明:
abstract void f();

    包含上述抽象方法的类叫做抽象类,如果一个类中包含一个或多个抽象方法,那么这个类就必须被定义为抽象的,也就是抽象类。抽象类的关键字也是abstract。抽象类是不安全的,因为它不完整,所以当它试图创建对象的时候,编译器会报错。如果一个类继承自抽象类,并想创建新类的对象,那么这个类必须为基类中的所有抽象方法提供具体的方法体,否则的话这个类也要被声明为抽象类。我们也可能会创建一个没有任何抽象方法的抽象类,比如当我们一个类中的任何方法都没有存在的意义时,或者也没有被声明为abstract的意义时,恰好我们又需要让这个类不能够创建对象,那么这时一个没有任何抽象方法的抽象类就产生了它的作用。

    下面通过一个“乐器”的例子看一下抽象类和抽象方法的具体使用:
package com.chenxyt.java.test;
abstract class Instrument{
	private int i;
	public String what(){
		return "Instrumet";
	}
	public abstract void play(String s);
	public void adjust(){};
}
class Wind extends Instrument{
	public String what(){
		return "Wind";
	}
	public void play(String s){
		System.out.println("Wind.play" + s);
	}
	public void adjust(){};
}
class Percussion extends Instrument{
	public String what(){
		return "Percussion";
	}
	public void play(String s){
		System.out.println("Percussion.play" + s);
	}
	public void adjust(){};
}
class Stringed extends Instrument{
	public String what(){
		return "Stringed";
	}
	public void play(String s){
		System.out.println("Stringed.play" + s);
	}
	public void adjust(){};
}
class Brass extends Instrument{
	public String what(){
		return "Brass";
	}
	public void play(String s){
		System.out.println("Brass.play" + s);
	}
	public void adjust(){};
}
class WoodWind extends Instrument{
	public String what(){
		return "WoodWind";
	}
	public void play(String s){
		System.out.println("WoodWind.play" + s);
	}
	public void adjust(){};
}

public class Music{
	static void tune(Instrument i ){
		i.play("finish");
	}
	static void tuneAll(Instrument[] e){
		for(Instrument i:e){
			tune(i);
		}
	}
	public static void main(String[] args) {
		Instrument[] iArray = {
				new Wind(),new Percussion(),new Brass(),new Stringed(),new WoodWind()
		};
		tuneAll(iArray);
	}
}

运行结果:

    
    如上可见,创建抽象类和抽象方法非常的有用,因为他们使类的抽象性更加明确,并告诉用户和编译器打算怎么样使用他们。抽象类还是一个很有用的重构工具,因为他们使得我们可以很容易的将公共方法沿着继承的层次向上移动。

二、接口

    interface关键字使抽象的概念更加迈进了一步,abstract关键字允许我们在类中创建一个没有方法实现的方法,方法的具体实现由继承它的子类创建。interface关键字产生一个完全抽象的类,它根本就没有提供任何具体的实现,它允许创建者确定具体的方法名,参数列表和返回值,但是没有任何方法体,接口只提供了对外的形式,但是没有提供任何具体实现。一个接口表示“所有实现该接口的类看起来都一样”,所以任何使用特定接口的类都知道他们可以调用哪些方法,因此接口用来实现类与类之间的一种协议关系。
    接口的创建使用interface关键字代替class关键字,访问权限控制与class相同,接口可以包含域,但是这些域隐式的被定义为static和final。
    要想具体的实现某一个接口的方法,需要创建一个类,并使用implements关键字关联所要实现的方法的接口,表示我们是这个接口的内部实现。当要实现一个接口的时候,在接口中被定义的方法必须是public,否则只能得到默认的包访问权限,这在继承关系中会使访问权限被降低,这是编译器不允许的。接口中的方法只有声明存在,这是编译器允许在接口中存在的唯一事物。
    修改抽象类中的示例为接口的形式:
package com.chenxyt.java.test;
interface Instrument{
	public void play(String s);
}
class Wind implements Instrument{
	public String what(){
		return "Wind";
	}
	public void play(String s){
		System.out.println("Wind.play" + s);
	}
	public void adjust(){};
}
class Percussion implements Instrument{
	public String what(){
		return "Percussion";
	}
	public void play(String s){
		System.out.println("Percussion.play" + s);
	}
	public void adjust(){};
}
class Stringed implements Instrument{
	public String what(){
		return "Stringed";
	}
	public void play(String s){
		System.out.println("Stringed.play" + s);
	}
	public void adjust(){};
}
class Brass implements Instrument{
	public String what(){
		return "Brass";
	}
	public void play(String s){
		System.out.println("Brass.play" + s);
	}
	public void adjust(){};
}
class WoodWind implements Instrument{
	public String what(){
		return "WoodWind";
	}
	public void play(String s){
		System.out.println("WoodWind.play" + s);
	}
	public void adjust(){};
}

public class Music{
	static void tune(Instrument i ){
		i.play("finish");
	}
	static void tuneAll(Instrument[] e){
		for(Instrument i:e){
			tune(i);
		}
	}
	public static void main(String[] args) {
		Instrument[] iArray = {
				new Wind(),new Percussion(),new Brass(),new Stringed(),new WoodWind()
		};
		tuneAll(iArray);
	}
}

结果如下:

    
可见程序的运行结果是相同的,只是接口中不允许出现带有方法体的方法。

三、完全解耦

    只要一个方法操作的是类而非接口,那么你就只能使用这个类及其子类。如果你想将这个方法应用在不在此继承结构中的某个类,那么使用接口将很大程度的放宽这种限制。因此,它可以使我们编写可复用性更好的代码。
    例如,有一个Processor类,它有一个name()方法,还有一个process()方法,该方法接受输入参数,修改输入的值然后进行输出。这个类作为基类被扩展,子类创建各种不同类型的Processor,在本例中,Processor子类通过process()方法修改String对象的值,返回类型可以是协变类型,而非参数类型。
package com.chenxyt.java.test;
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 input.toString().toUpperCase();
	}
}
class DownCase extends Processor{
	String process(Object input){
		return input.toString().toLowerCase();
	}
}
class Splitter extends Processor{
	String process(Object input){
		return Arrays.toString(input.toString().split(" "));
	}
}
public class Apply{
	public static void process(Processor p,Object s){
		System.out.println("Using Processor:" + p.name());
		System.out.println(p.process(s));
	}
	public static final String S = "Disagreement with beliefs is by definition incorrect";
	public static void main(String[] args) {
		process(new UpCase(), S);
		process(new DownCase(), S);
		process(new Splitter(), S);
	}
}

运行结果:

    
前边学习多态的时候有过类似的例子,Apply.process()方法可以接收Processor类型跟它的子类,并将它应用到了Object对象,然后打印。像这种,根据继承关系,创建一个能够根据所传递的参数对象不同而具有不同行为的方法,称为策略设计模式。这类方法包含索要执行的方法中固定不变的部分(如本例的name()方法),而“策略”包含变化的部分(如本例的process())方法。策略就是传递的参数对象,它包含要执行的代码。这类Processor对象就是一个策略,在main()方法中可以看到三种不同类型的策略应用到Obejct对象上。
    下面有如下4个类,他们看起来也适用Apply.process()
package com.chenxyt.java.practice;
public class Filter{
	public String name(){
		return getClass().getSimpleName();
	}
	public WaveForm process(WaveForm input){
		return input;
	}
}

Filter类,看上去与Processor类似,都有name()方法和process()方法,区别在于方法的参数类型和返回类型不同。

package com.chenxyt.java.practice;
public class HighPass extends Filter{
	double cutoff;
	public HighPass(double cutoff){
		this.cutoff = cutoff;
	}
	public WaveForm prcess(WaveForm input){
		return input;
	}
	
}

HighPass类,继承自Filter类

package com.chenxyt.java.practice;
public class BandPass extends Filter {
	double lowCutoff,highCutoff;
	public BandPass(double lowCutoff,double highCutoff){
		this.lowCutoff = lowCutoff;
		this.highCutoff = highCutoff;
	}
	public WaveForm process(WaveForm input){
		return input;
	}
}

BandPass类,继承自Filter类

package com.chenxyt.java.practice;
public class WaveForm{
	private static long counter;
	private final long id = counter++;
	public String toString(){
		return "Wave Form" + id;
	}
	
}

WaveForm类,前边方法的参数类型和返回类型。

    Filter与Processor类具有相同的内部接口元素(两个类的方法名都相同),但是由于Filter类并非继承自Processor类,因此当Apply.process()方法传入参数Filter的时候,由于Filter类的创建者并不知道要当做Processor类使用,并且它也不能通过向上转型的方式变成Processor类,因此不能将Filter类应用到Apply.process()方法。这主要是因为Apply.process()方法和Processor类的耦合度太高了,已经超出了所需要的程度。这就是Apply.process()方法只能接收Processor类或者其子类,而面对新的类的时候,Apply.process()方法就无能为力了,对其的复用也就被禁止了。
    但是,正如前文所说,如果操作的是接口而不是类的时候,那么这些限制就会变得松动,使得你可以复用接口的Apply.process()方法,下面是修改为接口的版本。
package com.chenxyt.java.practice;
public interface Processor{
	String name();
	Object process (Object input);
}

    此时Processor类变成了一个接口,复用代码的形式就是之前继承它的类,可以改为实现它的接口,并且Filter类,也可以编程实现Processor类的接口,这样Apply.process()方法的耦合度就降低了,并且支持了其它的类型。还有一种情况,假如一个类是被发现的,而不是被我们自己创建的,那么这个类就无法实现Processor接口,比如说,如果Filter类是在类库中的类,那么这个类就无法主动实现Processor接口,这时候可以使用适配器模式,在这个类的外部封装一层,作为适配器来实现要实现的接口。如下:

package com.chenxyt.java.practice;
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 filter.process((WaveForm)input)
	}
}
public class FilterProcessor{
	public static void main(String[] args) {
		WaveForm w = new WaveForm();
		Apply.process(new FilterAdapter(new LowPass(1.0)),w);
		Apply.process(new FilterAdapter(new HighPass(2.0)),w);
		Apply.process(new FilterAdapter(new BandPass(3.0,4.0)),w);
	}
}

    在这种使用适配器的方式中,FilterAdapter的构造器接受了Filter参数,然后生成对应接口Processor的对象。

    本节主要的内容是使用接口的方式将只有基类和其子类的使用方法解耦出来,便于程序更好的进行复用。

四、Java中的多重继承

    C++中允许多重继承,并且每一个继承的类都可以有一个实现,Java中是不允许的,Java中可以实现多个接口,每个接口名字在implements后边用逗号隔开,并且,Java中只能继承一个类。下面的例子说明一个具体的类组合实现多个接口产生一个新类:

package com.chenxyt.java.practice;
interface CanFight{
	void Fight();
}
interface CanSwim{
	void Swim();
}
interface CanFly{
	void Fly();
}
class ActionChracter{
	public void Fight(){
		//---
	};
}
class Hero extends ActionChracter implements CanFight,CanSwim,CanFly{
	public void Fly(){
		//---
	};
	public void Swim(){
		//--
	};
}
public class Adventure{
	public static void t(CanFight x){
		x.Fight();
	}
	public static void u(CanSwim x){
		x.Swim();
	}
	public static void v(CanFly x){
		x.Fly();
	}
	public static void w(ActionChracter x){
		x.Fight();
	}
	public static void main(String[] args) {
		Hero h = new Hero();
		t(h);
		u(h);
		v(h);
		w(h);	
	}
}

    可以看到Hero类组合具体类ActionChracter和另外的三个接口,当通过这种方式将类和接口组合在一起时,这个类必须放在前边,接口放在后边,否则编译器会报错。同时我们注意到,CanFight接口与ActionChracter类中的Fight()方法相同,而且Hero中并没有提供Fight()的具体定义。可以扩展接口,当想要创建对象的时候,所有的定义必须都存在,即使Hero没有显示的定义Fight()方法,由于其继承了ActionChracter类,所以定义随之而来,这使创建对象变成了可能。这里的意思是说,一个类实现了某些接口,这些接口中所有的定义在这个类中必须要有相关的实现(编译器会主动提示),然后因为这个类继承了一个类(ActionChracter),所以如果基类有实现了接口中的方法,那么子类就可以不显示的实现这个方法。(区别在于基类不是实现了这个方法,只是方法签名相同)

    这个例子中,给出的四个方法分别使用接口作为了参数,所以在Hero作为参数传递的时候,它被依次进行了向上转型,Java中的接口设计,使得这项功能并不复杂。这个例子所展示的是使用接口的核心原因:为了能够向上转型为多个基本类型,提升程序的灵活性。使用接口的第二个原因与抽象类相同,防止程序员在使用的过程中创建该类的对象。当然关于这一点是使用抽象类还是接口,当要创建的类中没有任何方法定义和成员变量的定义是,选择接口是合适的,并且当知道某事物应当成为一个基类的时候,那么第一选择是应当使它成为接口。

五、通过继承扩展接口

    接口也可以继承!没错,通过继承可以很容易的在接口中添加新的方法声明,还可以通过继承在新接口中组合数个接口。
package com.chenxyt.java.practice;
interface Monster{
	void menace();
}
//新接口继承原来的接口
interface DangerousMonster extends Monster{
	void destroy();
}
interface Lethal{
	void kill();
}
//实现接口 要依次定义这个接口的方法以及它继承接口的方法  编译器自动补充
class DragonZill implements DangerousMonster{
	@Override
	public void menace() {
		// TODO Auto-generated method stub
	}
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}
}
//接口可以多重继承
interface Vampire extends DangerousMonster ,Lethal{
	void drinkblood();
}
//继承多个接口 都要把定义实现
class VeryBadVampire implements Vampire{

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void menace() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void kill() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void drinkblood() {
		// TODO Auto-generated method stub
		
	}
}

     代码中标注了,接口可以使用extends继承多个,但是这一形式不适用于普通的类。

    这里说到了上边例子中的CanFight类和ActionChracter类都有一个相同的方法,如果方法只是名字相同,参数类型不同,返回类型不同,那么将带来逻辑上很大的问题。因此在继承、实现接口、覆盖或者重载的时候,应尽量避免重名的问题出现。

六、适配接口

    接口最吸引人的地方,就是允许同一个接口具有多个不同的实现。简单来说,就是一个接受接口类型的方法,而该接口的实现和向该接口传递的对象取决于方法的使用者。因此常用的方式就是前边的策略模式,此时你编写一个执行某些操作的方法,该方法接受一个同样是你指定的接口,你主要就是声明”你可以用任何你想要的对象来调用我的方法,只要你的对象遵循我的接口“这使你的方法更加灵活。
    这里把我把上边适配接口的例子全部的代码撸了一遍,并分析了一下,具体的关于适配器模式后边会专门再学习一下,此处只是将书中的例子学习了一下。
首先有个Processor接口,该接口有两个方法声明
package com.chenxyt.java.practice;
public interface Processor{
	String name();
	Object process(Object input);
}

然后是个Apply类,这个类有个静态方法process,为了解耦,传递的参数为接口类型,接口的方法作用于一个Object对象
package com.chenxyt.java.practice;
public class Apply {
	public static void process(Processor p,Object s){
		System.out.println("Using Processor" + p.name());
		System.out.println(p.process(s));
	}
}

这时我们发现了一个Filter类,因为这个类是发现的,所以看上去它跟Procesor接口有相同的方法,只是类型不同,所以可以直接实现该方法,但是由于这个类是已经写好了的。所以它不可以被修改了。
package com.chenxyt.java.practice;
public class Filter{
	public String name(){
		return getClass().getSimpleName();
	}
	public Waveform process(Waveform input){
		return input;
	}
}

然后是一个Waveform类,这个类作为Filter类process方法的返回值,定义了个toString方法,所以在打印的时候会调用这个toString方法并把输入的内容返回
package com.chenxyt.java.practice;
public class Waveform {
	private static long counter;
	private final long id = counter++;
	public String toString(){
		return "Waveform" + id;
	}
}

然后是Filter类的三个子类,分别实现了父类的process方法
package com.chenxyt.java.practice;
public class LowPass extends Filter{
	double cutoff;
	public LowPass(double cutoff) {
		this.cutoff = cutoff;
	}
	public Waveform process(Waveform input){
		return input;
	}
}
package com.chenxyt.java.practice;
public class HighPass extends Filter{
	double cutoff;
	public HighPass(double cutoff) {
		this.cutoff = cutoff;
	}
	public Waveform process(Waveform input){
		return input;
	}
}
package com.chenxyt.java.practice;
public class BandPass extends Filter {
	double lowCutoff,highCutoff;
	public BandPass(double lowCutoff,double highCutoff){
		this.lowCutoff = lowCutoff;
		this.highCutoff = highCutoff;
	}
	public Waveform process(Waveform input){
		return input;
	}
}

这个时候问题来了,因为Appply类的process方法传参数是接口,这时Filter类已经存在,想直接使用Apply的process方法行不通,想实现Processor接口已经来不及。那么就要使用适配器模式啦。
package com.chenxyt.java.practice;

class FilterAdapter implements Processor{
	Filter filter = new Filter();
	public FilterAdapter(Filter filter) {
		this.filter = filter;
	}
	@Override
	public String name() {
		// TODO Auto-generated method stub
		return filter.name();
	}
	public Waveform process(Object input) {
		// TODO Auto-generated method stub
		return filter.process((Waveform)input);
	}
}
public class FilterProcessor{
	public static void main(String[] args) {
		Waveform w = new Waveform();
		Apply.process(new FilterAdapter(new LowPass(1.0)),w);
		Apply.process(new FilterAdapter(new HighPass(2.0)),w);
		Apply.process(new FilterAdapter(new BandPass(3.0,4.0)),w);
	}
}

这里我简单的理解了一下这里的适配器模式,就是写了一个适配器的类,这个类实现了Processor接口,内部接受了Filter对象参数,然后生成了你想要的Processor接口对象,达到了预期的目的。这里有关具体的设计模式分析,后边会继续学习。

七、接口中的域

    在接口中的域,会被自动的隐式转换为static final类型,所以接口就可以很便捷的创建一组常量值,也就是枚举。在JavaSE5之前,没有枚举的概念之前,可以使用接口来创建常量组。
package com.chenxyt.java.practice;
public interface Months{
	int JANUARY = 1,FEBRUARY=2,MARCH=3,APRIL=4,
			MAY=5,JUNE=6,JULY=7,AUGUST=8,SEPTEMBER=9,OCTOBER=10,
			NOVEMBER=11,DECEMBER=12;
}

    当然这种形式在后来已经被enum取代了。因为是final类型,所以必须显示的指定初始化的值,同时因为是static域,所以它们在第一次访问的时候被初始化,并且这些域不属于接口的一部分,它们的值存储在接口的静态存储区域。

八、嵌套接口

    接口可以嵌套在类或者其它的接口中,个人觉得这种设计会使程序变得更加复杂不易读。
package com.chenxyt.java.practice;
class A{
	interface B{
		void f();
	}
	public class BImp implements B{
		@Override
		public void f() {
			// TODO Auto-generated method stub
		}
	}
	public class BImp2 implements B{
		@Override
		public void f() {
			// TODO Auto-generated method stub
		}
	}
	public interface C{
		void f();
	}
	class CImp implements C{
		@Override
		public void f() {
			// TODO Auto-generated method stub
		}
	}
	private class CImp2 implements C{
		@Override
		public void f() {
			// TODO Auto-generated method stub
		}
	}
	private interface D{
		void f();
	}
	private class DImp implements D{
		@Override
		public void f() {
			// TODO Auto-generated method stub
		}
	}
	private class DImp2 implements D{
		@Override
		public void f() {
			// TODO Auto-generated method stub
		}
	}
	public D getD(){
		return new DImp();
	}
	private D dRef;
	public void reveiveD(D d){
		dRef = d;
		dRef.f();
	}
}
interface E{
	interface G{
		void f();
	}
	public interface H{
		void f();
	}
	void g();
	//强制必须为public
	//private interface I{};
}

public class NestingInterfaces{
	public class BImp implements A.B{
		@Override
		public void f() {
			// TODO Auto-generated method stub
		}
	}
	class CImp implements A.C{
		@Override
		public void f() {
			// TODO Auto-generated method stub
		}
	}
	//因为接口D是私有的 所以不能被实现
	//class DImp implements A.D{);
	class EImp implements E{
		@Override
		public void g() {
			// TODO Auto-generated method stub
		}
	}
	class EGImp implements E.G{
		@Override
		public void f() {
			// TODO Auto-generated method stub
		}
	}
	class EImp2 implements E{
		@Override
		public void g() {
			// TODO Auto-generated method stub
		}
		class EG implements E.G{
			@Override
			public void f() {
				// TODO Auto-generated method stub
			}
		}
	}
	public static void main(String[] args) {
		A a = new A();
		//D是private 不能实例化
		//A.D ad = new A.D();
		//getD()方法只能返回D
		//A.DImp2 di2 = a.getD();
		//private接口的域不能被访问
		//a.getD().f();
		//可以通过内部返回域的方法获取
		A a2 = new A();
		a2.reveiveD(a.getD());
	}
}

    这里主要要说明的就是private interface接口的作用,就像A.D接口一样,它能够被实现为DImp的一个内部类,也同样可以像DImp2一样实现为public类,但是正如main方法中倒数第4行代码一样,A.DImp2只能被自己使用,因为你无法说你实现了一个private接口D,因此这个实现只是一种形式而已,它可以强制该方法的定义不带有任何类型信息,即不可以向上转型。所以我们在get方法中return new DImp2 的时候并没有获取到预期的值,因为DImp2是一个实现了private接口的public类,最终我们还是通过receiveD方法获取到了相应的实例。
    接口E说明了接口之间的嵌套关系,因为接口内部所有元素都是public的,所以不能指明嵌套在内部的接口为private。NestingInterfaces展示了嵌套接口的几种形式,特别注意的是,在实现一个接口的时候,不需要实现其内部嵌套接口的方法。而且private接口不能在定义它的类外部被实现,比如上述代码中的A.D

九、接口与工厂

    接口是实现多重继承的重要途径,而生成遵循某个接口对象的典型方式就是工厂方法设计模式。由此可见设计模式的重要性,我自己最近也在学习这一块的内容。希望能够有所提高。
    使用工厂方法与直接调用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。理论上来说,我们通过这种方式可以将我们的代码与接口的实现完全分离,这就使我们可以透明的将某个实现替换成另一个实现。如下示例:
package com.chenxyt.java.practice;
interface Service{
	void method1();
	void method2();
}
interface ServiceFactory{
	Service getService();
}
class Implementation1 implements Service{
	Implementation1() {
		// TODO Auto-generated constructor stub
	}
	@Override
	public void method1() {
		// TODO Auto-generated method stub
		System.out.println("Implementation1 method1");
	}
	@Override
	public void method2() {
		// TODO Auto-generated method stub
		System.out.println("Implementation1 method2");	
	}
}
class Implementation1Factory implements ServiceFactory{
	@Override
	public Service getService() {
		// TODO Auto-generated method stub
		return new Implementation1();
	}
}
class Implementation2 implements Service{
	Implementation2() {
		// TODO Auto-generated constructor stub
	}
	@Override
	public void method1() {
		// TODO Auto-generated method stub
		System.out.println("Implementation2 method1");
	}
	@Override
	public void method2() {
		// TODO Auto-generated method stub
		System.out.println("Implementation2 method2");
	}
}
class Implementation2Factory implements ServiceFactory{
	@Override
	public Service getService() {
		// TODO Auto-generated method stub
		return new Implementation2();
	}
}
public class Factories {
	public static void serviceConsumer(ServiceFactory fact){
		Service s = fact.getService();
		s.method1();
		s.method2();
	}
	public static void main(String[] args) {
		serviceConsumer(new Implementation1Factory());
		serviceConsumer(new Implementation2Factory());
	}
}

    这里如果不是使用工厂方法,代码中就要指定Service的确切类型,以便调用合适的构造器。使用工厂方法设计模式的原因是想要创建框架,提高代码的复用性。如下:
package com.chenxyt.java.practice;
interface Game{
	boolean move();
}
interface GameFactory{
	Game getGame();
}
class Checkers implements Game{
	private int moves = 0;
	private static final int MOVES = 3;
	@Override
	public boolean move() {
		// TODO Auto-generated method stub
		System.out.println("Checkers moves" + moves);
		return ++moves != MOVES;
	}
}
class CheckersFactory implements GameFactory{
	@Override
	public Game getGame() {
		// TODO Auto-generated method stub
		return new Checkers();
	}
}

class Chess implements Game{
	private int moves = 0;
	private static final int MOVES = 4;
	@Override
	public boolean move() {
		// TODO Auto-generated method stub
		System.out.println("Chess move" + moves);
		return ++moves != MOVES;
	}
}
class ChessFactory implements GameFactory{
	@Override
	public Game getGame() {
		// TODO Auto-generated method stub
		return new Chess();
	}
}
public class Games {
	public static void PlayGame(GameFactory fact){
		Game s = fact.getGame();
		while(s.move()){
			//--
		}
	}
	public static void main(String[] args) {
		PlayGame(new CheckersFactory());
		PlayGame(new ChessFactory());
	}
}

    如果Games类表示一段复杂的代码,那么这种方式就允许你在不同的游戏类型中复用这段代码。

十、总结

    抽象类跟接口是将具体方法更加抽象的一种形式,这一章节主要讲了抽象类、抽象方法的形式以及使用场景,比较重要的一点是关于接口的使用,如何解耦,接口可以多重继承,接口可以嵌套等应用场景。关于这一章节中的设计模式,还要继续深入研究下去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值