重温经典《Thinking in java》第四版之第九章 接口(四十七)

简介: 重温经典《Thinking in java》第四版之第九章 接口(四十七)

9.3 完全解耦

只要一个方法操作的是类而非接口,那么你就只能使用这个类及其子类。如果你想要将这个方法应用于不在此继承结构中的某个类,那么你就会触霉头了。接口可以很大程度上放宽这种限制,因此,它使得我们可以编写复用性更好的代码。

例如,假设有一个Processor的类,它有一个name()方法,另外还有一个process()方法,该方法接受输入参数,修改它的值,然后产生输出。这个类作为基类而被扩展,用来创建各种不同类型的Processor。在本例中,Processor的子类将修改String对象(注意,返回类型可以是协变类型,而非参数类型):

packageinterfaces.classprocessor; 
importjava.util.*; 
importstaticnet.mindview.util.Print.*; 
classProcessor { 
publicStringname() { 
returngetClass().getSimpleName(); 
    } 
Objectprocess(Objectinput) { returninput; } 
} 
classUpcaseextendsProcessor { 
Stringprocess(Objectinput) { // Covariant return return ((String)input).toUpperCase(); 
    } 
} 
classDowncaseextendsProcessor { 
Stringprocess(Objectinput) { 
return ((String)input).toLowerCase(); 
    } 
} 
classSplitterextendsProcessor { 
Stringprocess(Objectinput) { 
// The split() argument divides a String into pieces: returnArrays.toString(((String)input).split(" ")); 
    } 
} 
publicclassApply { 
publicstaticvoidprocess(Processorp, Objects) { 
print("Using Processor "+p.name()); 
print(p.process(s)); 
    } 
publicstaticStrings="Disagreement with beliefs is by definition incorrect"; 
publicstaticvoidmain(String[] args) { 
process(newUpcase(), s); 
process(newDowncase(), s); 
process(newSplitter(), s); 
    } 
}

/* Output:

Using Processor Upcase

DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT

Using Processor Downcase

disagreement with beliefs is by definition incorrect

Using Processor Splitter

[Disagreement, with, beliefs, is, by, definition, incorrect]

*///:~

Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。像本例这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式。这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。这里,Processor对象就是一个策略,在main()中可以看到有三种不同的策略应用到了String类型的s对象上。

 

现在嘉禾我们发现了一组电子滤波器,它们看起来好像适用于Apply.process()方法:

//: interfaces/filters/Waveform.java packageinterfaces.filters; 
publicclassWaveform { 
privatestaticlongcounter; 
privatefinallongid=counter++; 
publicStringtoString() { return"Waveform "+id; } 
} ///:~ //: interfaces/filters/Filter.java packageinterfaces.filters; 
publicclassFilter { 
publicStringname() { 
returngetClass().getSimpleName(); 
    } 
publicWaveformprocess(Waveforminput) { returninput; } 
} ///:~ //: interfaces/filters/LowPass.java packageinterfaces.filters; 
publicclassLowPassextendsFilter { 
doublecutoff; 
publicLowPass(doublecutoff) { this.cutoff=cutoff; } 
publicWaveformprocess(Waveforminput) { 
returninput; // Dummy processing     } 
} ///:~ //: interfaces/filters/HighPass.java packageinterfaces.filters; 
publicclassHighPassextendsFilter { 
doublecutoff; 
publicHighPass(doublecutoff) { this.cutoff=cutoff; } 
publicWaveformprocess(Waveforminput) { returninput; } 
} ///:~ //: interfaces/filters/BandPass.java packageinterfaces.filters; 
publicclassBandPassextendsFilter { 
doublelowCutoff, highCutoff; 
publicBandPass(doublelowCut, doublehighCut) { 
lowCutoff=lowCut; 
highCutoff=highCut; 
    } 
publicWaveformprocess(Waveforminput) { returninput; } 
}

///:~

Filter与Processor具有相同的接口元素,但是它并非继承自Processor——因为Filter类的创建者压根不清楚你想要将它用作Processor——因此你不能将Filter用于Apply.process()方法,即便这样做可以正常运行。这里主要是因为Apply.process()方法和Processor之间的耦合过紧,已经超出了所需要的程度,这就使得应该复用Apply.process()的代码时,复用却被禁止了。另外还需要注意的是它们的输入和输出都是Waveform。

但是,如果Processor是一个接口,那么这些限制就会变得松动,使得你可以复用该接口的Apply.process()。下面是Processor和Apply的修改版本:

//: interfaces/interfaceprocessor/Processor.java packageinterfaces.interfaceprocessor; 
publicinterfaceProcessor { 
Stringname(); 
Objectprocess(Objectinput); 
} ///:~ //: interfaces/interfaceprocessor/Apply.java packageinterfaces.interfaceprocessor; 
importstaticnet.mindview.util.Print.*; 
publicclassApply { 
publicstaticvoidprocess(Processorp, Objects) { 
print("Using Processor "+p.name()); 
print(p.process(s)); 
    } 
}

///:~

复用代码的第一种方式是客户端程序员遵循该接口来编写他们自己的类,就像下面这样:

//: interfaces/interfaceprocessor/StringProcessor.java packageinterfaces.interfaceprocessor; 
importjava.util.*; 
publicabstractclassStringProcessorimplementsProcessor{ 
publicStringname() { 
returngetClass().getSimpleName(); 
    } 
publicabstractStringprocess(Objectinput); 
publicstaticStrings="If she weighs the same as a duck, she’s made of wood"; 
publicstaticvoidmain(String[] args) { 
Apply.process(newUpcase(), s); 
Apply.process(newDowncase(), s); 
Apply.process(newSplitter(), s); 
    } 
} 
classUpcaseextendsStringProcessor { 
publicStringprocess(Objectinput) { // Covariant return return ((String)input).toUpperCase(); 
    } 
} 
classDowncaseextendsStringProcessor { 
publicStringprocess(Objectinput) { 
return ((String)input).toLowerCase(); 
    } 
} 
classSplitterextendsStringProcessor { 
publicStringprocess(Objectinput) { 
returnArrays.toString(((String)input).split(" ")); 
    } 
}

/* Output:

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]

*///:~

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

//: interfaces/interfaceprocessor/FilterProcessor.java packageinterfaces.interfaceprocessor; 
importinterfaces.filters.*; 
classFilterAdapterimplementsProcessor { 
Filterfilter; 
publicFilterAdapter(Filterfilter) { 
this.filter=filter; 
    } 
publicStringname() { returnfilter.name(); } 
publicWaveformprocess(Objectinput) { 
returnfilter.process((Waveform)input); 
    } 
} 
publicclassFilterProcessor { 
publicstaticvoidmain(String[] args) { 
Waveformw=newWaveform(); 
Apply.process(newFilterAdapter(newLowPass(1.0)), w); 
Apply.process(newFilterAdapter(newHighPass(2.0)), w); 
Apply.process( 
newFilterAdapter(newBandPass(3.0, 4.0)), w); 
    } 
}

/* Output:

Using Processor LowPass

Waveform 0

Using Processor HighPass

Waveform 0

Using Processor BandPass

Waveform 0

*///:~

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

目录
相关文章
|
4天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
5天前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
16 1
|
10天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
38 4
|
17天前
|
安全 Java
在 Java 中使用实现 Runnable 接口的方式创建线程
【10月更文挑战第22天】通过以上内容的介绍,相信你已经对在 Java 中如何使用实现 Runnable 接口的方式创建线程有了更深入的了解。在实际应用中,需要根据具体的需求和场景,合理选择线程创建方式,并注意线程安全、同步、通信等相关问题,以确保程序的正确性和稳定性。
|
15天前
|
Java
Java基础(13)抽象类、接口
本文介绍了Java面向对象编程中的抽象类和接口两个核心概念。抽象类不能被实例化,通常用于定义子类的通用方法和属性;接口则是完全抽象的类,允许声明一组方法但不实现它们。文章通过代码示例详细解析了抽象类和接口的定义及实现,并讨论了它们的区别和使用场景。
|
15天前
|
Java 测试技术 API
Java零基础-接口详解
【10月更文挑战第19天】Java零基础教学篇,手把手实践教学!
16 1
|
20天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
15 3
|
20天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
29 2
|
20天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
27 2
|
20天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
27 1