Skip to content

Commit 837cbcf

Browse files
authored
Merge pull request lingcoder#113 from xiangflight/master
[feat 10](截至 完全解耦)
2 parents c96e965 + aea2f51 commit 837cbcf

File tree

1 file changed

+244
-1
lines changed

1 file changed

+244
-1
lines changed

docs/book/10-Interfaces.md

Lines changed: 244 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,22 +760,265 @@ Woodwind.play() MIDDLE_C
760760

761761
## 抽象类和接口
762762

763+
尤其是在 Java 8 引入 **default** 方法之后,区分何时抽象类还是接口是最好的选择变得更加令人困惑。下表做了明确的区分:
763764

765+
| 特性 | 接口 | 抽象类 |
766+
| :------------------: | :--------------------------------------------------------: | :--------------------------------------: |
767+
| 组合 | 新类可以组合多个接口 | 只能继承单一抽象类 |
768+
| 状态 | 不能包含属性(除了静态属性,不支持对象状态) | 可以包含属性,非抽象方法可能引用这些属性 |
769+
| 默认方法 和 抽象方法 | 不需要在子类中实现默认方法。默认方法可以引用其他接口的方法 | 必须在子类中实现抽象方法 |
770+
| 构造器 | 没有构造器 | 可以有构造器 |
771+
| 可见性 | 隐式 **public** | 可以是 **protected** 或友元 |
764772

773+
抽象类仍然是一个类,在创建新类时只能继承它一个。而创建类的过程中可以实现多个接口。
774+
775+
有一条实际经验:尽可能地抽象。因此,更倾向使用接口而不是抽象类。只有当必要时才使用抽象类。除非必须使用,否则不要用接口和抽象类。大多数时候,普通类已经做得很好,如果不行的话,再移动到接口或抽象类中。
765776

766777
<!-- Complete Decoupling -->
778+
767779
## 完全解耦
768780

781+
当方法操纵的是一个类而非接口时,它就只能作用于那个类或其子类。如果想把方法应用于那个继承层级结构之外的类,就会触霉头。接口在很大程度上放宽了这个限制,因而使用接口可以编写复用性更好的代码。
782+
783+
例如有一个类 **Process** 有两个方法 `name()``process()``process()` 方法接受输入,修改并输出。把这个类作为基类用来创建各种不同类型的 **Processor**。下例中,**Processor** 的各个子类修改 String 对象(注意,返回类型可能是协变类型而非参数类型):
784+
785+
```java
786+
// interfaces/Applicator.java
787+
import java.util.*;
788+
789+
class Processor {
790+
public String name() {
791+
return getClass().getSimpleName();
792+
}
793+
794+
public Object process(Object input) {
795+
return input;
796+
}
797+
}
798+
799+
class Upcase extends Processor {
800+
// 返回协变类型
801+
@Override
802+
public String process(Object input) {
803+
return ((String) input).toUpperCase();
804+
}
805+
}
806+
807+
class Downcase extends Processor {
808+
@Override
809+
public String process(Object input) {
810+
return ((String) input).toLowerCase();
811+
}
812+
}
813+
814+
class Splitter extends Processor {
815+
@Override
816+
public String process(Object input) {
817+
// split() divides a String into pieces:
818+
return Arrays.toString(((String) input).split(" "));
819+
}
820+
}
821+
822+
public class Applicator {
823+
public static void apply(Processor p, Object s) {
824+
System.out.println("Using Processor " + p.name());
825+
System.out.println(p.process(s));
826+
}
827+
828+
public static void main(String[] args) {
829+
String s = "We are such stuff as dreams are made on";
830+
apply(new Upcase(), s);
831+
apply(new Downcase(), s);
832+
apply(new Splitter(), s);
833+
}
834+
}
835+
```
836+
837+
输出:
838+
839+
```
840+
Using Processor Upcase
841+
WE ARE SUCH STUFF AS DREAMS ARE MADE ON
842+
Using Processor Downcase
843+
we are such stuff as dreams are made on
844+
Using Processor Splitter
845+
[We, are, such, stuff, as, dreams, are, made, on]
846+
```
847+
848+
**Applicator**`apply()` 方法可以接受任何类型的 **Processor**,并将其应用到一个 **Object** 对象上输出结果。像本例中这样,创建一个能根据传入的参数类型从而具备不同行为的方法称为*策略*设计模式。方法包含算法中不变的部分,策略包含变化的部分。策略就是传入的对象,它包含要执行的代码。在这里,**Processor** 对象是策略,`main()` 方法展示了三种不同的应用于 **String s** 上的策略。
849+
850+
`split()`**String** 类中的方法,它接受 **String** 类型的对象并以传入的参数作为分割界限,返回一个数组 **String[]**。在这里用它是为了更快地创建 **String** 数组。
851+
852+
假设现在发现了一组电子滤波器,它们看起来好像能使用 **Applicator**`apply()` 方法:
853+
854+
```java
855+
// interfaces/filters/Waveform.java
856+
package interfaces.filters;
857+
858+
public class Waveform {
859+
private static long counter;
860+
private final long id = count++;
861+
862+
@Override
863+
public String toString() {
864+
return "Waveform " + id;
865+
}
866+
}
867+
868+
// interfaces/filters/Filter.java
869+
package interfaces.filters;
870+
871+
public class Filter {
872+
public String name() {
873+
return getClass().getSimpleName();
874+
}
875+
876+
public Waveform process(Waveform input) {
877+
return input;
878+
}
879+
}
880+
881+
// interfaces/filters/LowPass.java
882+
package interfaces.filters;
883+
884+
public class LowPass extends Filter {
885+
double cutoff;
886+
887+
public LowPass(double cutoff) {
888+
this.cutoff = cutoff;
889+
}
890+
891+
@Override
892+
public Waveform process(Waveform input) {
893+
return input; // Dummy processing 哑处理
894+
}
895+
}
896+
897+
// interfaces/filters/HighPass.java
898+
package interfaces.filters;
899+
900+
public class HighPass extends Filter {
901+
double cutoff;
902+
903+
public HighPass(double cutoff) {
904+
this.cutoff = cutoff;
905+
}
906+
907+
@Override
908+
public Waveform process(Waveform input) {
909+
return input;
910+
}
911+
}
912+
913+
// interfaces/filters/BandPass.java
914+
package interfaces.filters;
915+
916+
public class BandPass extends Filter {
917+
double lowCutoff, highCutoff;
918+
919+
public BandPass(double lowCut, double highCut) {
920+
lowCutoff = lowCut;
921+
highCutoff = highCut;
922+
}
923+
924+
@Override
925+
public Waveform process(Waveform input) {
926+
return input;
927+
}
928+
}
929+
```
930+
931+
**Filter** 类与 **Processor** 类具有相同的接口元素,但是因为它不是继承自 **Processor** —— 因为 **Filter** 类的创建者根本不知道你想将它当作 **Processor** 使用 —— 因此你不能将 **Applicator**`apply()` 方法应用在 **Filter** 类上,即使这样做也能正常运行。主要是因为 **Applicator**`apply()` 方法和 **Processor** 过于耦合,这阻止了 **Applicator**`apply()` 方法被复用。另外要注意的一点是 Filter 类中 `process()` 方法的输入输出都是 **Waveform**
932+
933+
但如果 **Processor** 是一个接口,那么限制就会变得松动到足以复用 **Applicator**`apply()` 方法,用来接受那个接口参数。下面是修改后的 **Processor****Applicator** 版本:
934+
935+
```java
936+
// interfaces/interfaceprocessor/Processor.java
937+
package interfaces.interfaceprocessor;
938+
939+
public interface Processor {
940+
default String name() {
941+
return getClass().getSimpleName();
942+
}
943+
944+
Object process(Object input);
945+
}
946+
947+
// interfaces/interfaceprocessor/Applicator.java
948+
package interfaces.interfaceprocessor;
949+
950+
public class Applicator {
951+
public static void apply(Processor p, Object s) {
952+
System.out.println("Using Processor " + p.name());
953+
System.out.println(p.process(s));
954+
}
955+
}
956+
```
957+
958+
复用代码的第一种方式是客户端程序员遵循接口编写类,像这样:
959+
960+
```java
961+
// interfaces/interfaceprocessor/StringProcessor.java
962+
// {java interfaces.interfaceprocessor.StringProcessor}
963+
package interfaces.interfaceprocessor;
964+
import java.util.*;
965+
966+
interface StringProcessor extends Processor {
967+
@Override
968+
String process(Object input); // [1]
969+
String S = "If she weighs the same as a duck, " + "she's made of wood"; // [2]
970+
971+
static void main(String[] args) { // [3]
972+
Applicator.apply(new Upcase(), S);
973+
Applicator.apply(new Downcase(), S);
974+
Applicator.apply(new Splitter(), S);
975+
}
976+
}
977+
978+
class Upcase implements StringProcessor {
979+
// 返回协变类型
980+
@Override
981+
public String process(Object input) {
982+
return ((String) input).toUpperCase();
983+
}
984+
}
985+
986+
class Downcase implements StringProcessor {
987+
@Override
988+
public String process(Object input) {
989+
return ((String) input).toLowerCase();
990+
}
991+
}
992+
993+
class Splitter implements StringProcessor {
994+
@Override
995+
public String process(Object input) {
996+
return Arrays.toString(((String) input).split(" "));
997+
}
998+
}
999+
```
1000+
1001+
输出:
1002+
1003+
```
1004+
Using Processor Upcase
1005+
IF SHE WEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD
1006+
Using Processor Downcase
1007+
if she weighs the same as a duck, she's made of wood
1008+
Using Processor Splitter
1009+
[If, she, weighs, the, same, as, a, duck,, she's, made, of, wood]
1010+
```
7691011

7701012
<!-- Combining Multiple Interfaces -->
1013+
7711014
## 多接口结合
7721015

7731016

7741017
<!-- Extending an Interface with Inheritance -->
7751018
## 使用继承扩展接口
7761019

777-
7781020
<!-- Adapting to an Interface -->
1021+
7791022
## 接口适配
7801023

7811024

0 commit comments

Comments
 (0)