第九章 接口
第九章 接口
**接口和内部类为我们提供了一种将接口与现实分离的更加结构化的方法 **
抽象类,普通类与接口的一种中庸之道
9.1 抽象类和抽象方法
创建一个接口的通用理由是,不同的子类可以用不同的方式来表示此接口。通过接口建立一种基本形式,以此表示所有导出类的共同部分。另一种说法称接口为 抽象基类
创建抽象类是想通过这个通用接口操纵一系列类。
**抽象方法 ** 仅有声明而没有方法体。 abstract void f()
包含抽象方法的类叫做 抽象类,如果一个类包含一个或多个抽象方法 ,必须限定为抽象的,不能建立对象,因为是不安全的。
从一个抽象类继承,并想新建该类的对象,就必须为基类中的所有抽象方法提供方法定义。
抽象类和抽象方法非常有用:
- 类的抽象性明确起来
- 告诉用户和编译器打算怎样来使用它们
- 抽象类还是有很多有用的重构工具
练习1
含有多个抽象方法的类,必须修饰为抽象类
抽象类不能 创建对象实例 但是可以创建数组 (分配一块区域 去向下转型 为导出类)
Error: cannot instantiate abstract class:
// Rodent x = new Rodent();
// But OK to create array to be downcast to derived objects
Rodent[] rodents = new Rodent[3];
import java.util.Random;
class RandomRodentGenerator1 {
private Random rand = new Random();
public Rodent next() {
switch(rand.nextInt(3)) {
default:
case 0: return new Mouse();
case 1: return new Rat();
case 2: return new Squirrel();
}
}
}
abstract class Rodent {
private String name = "Rodent";
abstract protected void eat();
abstract protected void run();
abstract protected void sleep();
abstract public String toString();
}
class Mouse extends Rodent {
private String name = "Mouse";
protected void eat() {
System.out.println("Mouse.eat()"); }
protected void run() {
System.out.println("Mouse.run()"); }
protected void sleep() {
System.out.println("Mouse.sleep()"); }
public String toString() { return name; }
}
class Rat extends Rodent {
private String name = "Rat";
protected void eat() {
System.out.println("Rat.eat()"); }
protected void run() {
System.out.println("Rat.run()"); }
protected void sleep() {
System.out.println("Rat.sleep()"); }
public String toString() { return name; }
}
class Squirrel extends Rodent {
private String name = "Squirrel";
protected void eat() {
System.out.println("Squirrel.eat()"); }
protected void run() {
System.out.println("Squirrel.run()"); }
protected void sleep() {
System.out.println("Squirrel.sleep()"); }
public String toString() { return name; }
}
public class No1Rodent1 {
private static RandomRodentGenerator1 gen = new RandomRodentGenerator1();
public static void main(String[] args) {
// Error: cannot instantiate abstract class:
// Rodent x = new Rodent();
// But OK to create array to be downcast to derived objects:
Rodent[] rodents = new Rodent[3];
for(Rodent r : rodents) {
r = gen.next();
System.out.println(r + ": ");
r.eat();
r.run();
r.sleep();
}
}
}
==========================================================================
Rat:
Rat.eat()
Rat.run()
Rat.sleep()
Squirrel:
Squirrel.eat()
Squirrel.run()
Squirrel.sleep()
Squirrel:
Squirrel.eat()
Squirrel.run()
Squirrel.sleep()
练习2
不包含任何抽象方法的 抽象类,验证不能 创建任何实例
abstract class Nogo1 {
Nogo1() { System.out.println("Nogo1()"); }
}
abstract class Nogo2 {}
class Go1 extends Nogo1 {
Go1() { System.out.println("Go1()"); }
}
public class No2Ex {
public static void main(String[] args) {
// Nogo1 and Nogo2 cannot be instantiated:实例化
// Nogo1 ng1 = new Nogo1();
// Nogo2 ng2 = new Nogo2();
// But Nogo1() constructor called during instatiation of child: 但是 Nogo1的构造函数被调用 实例化孩子的时候
Go1 g1 = new Go1();
}
}
=======================================================================
Nogo1()
Go1()
练习3
aabstract class Dad {
protected abstract void print();
Dad() { print(); }
}
class Son extends Dad {
private int i = 1;
@Override protected void print() {
System.out.println
("Son.i = " + i); }
}
public class No3ex {
public static void main(String[] args) {
/* Process of initialization:
* 1. Storage for Son s allocated and initialized to binary zero
* 2. Dad() called
* 3. Son.print() called in Dad() (s.i = 0)
* 4. Member initializers called in order (s.i = 1)
* 5. Body of Son() called
*/
Son s = new Son();
s.print();
}
}
==========================================================================
Son.i = 0
Son.i = 1
练习4
abstract class Dad {
}
class Son extends Dad {
protected void print() {
System.out.println("Son"); }
}
abstract class SecondDad {
abstract protected void print();
}
class SecondSon extends SecondDad {
protected void print() {
System.out.println("SecondSon"); }
}
public class No4Ex {
public static void testPrint(Dad d) {
((Son)d).print();
}
public static void secondTestPrint(SecondDad sd) {
sd.print();
}
public static void main(String[] args) {
Son s = new Son();
No4Ex.testPrint(s);
SecondSon ss = new SecondSon();
No4Ex.secondTestPrint(ss);//不用向下转型
}
}
=============================================================
Son
SecondSon
9.2 接口
abstract 关键字允许人们在类中创建一个 或 多个没有任何定义的方法—— 提供了接口部分(没有具体实现,由此类的继承者去实现)
- interface 这个关键字产生一个完全抽象的类,它根本不提供任何具体实现,只提供了形式
- 任何使用 某特定接口的代码都知道可以调用该接口实现那些方法,仅需知道这些
- 接口被用来建立类与类之间的协议
- 接口可以包含域,但这些域是隐式地static 和 final地
- 要让一个类 遵循某个特定接口(或者一组),需要使用 implements关键字,声明如何工作地
在接口定义中,接口中被定义地方法必须被定义为public的,否则它们将只能得到默认地包访问权限,这样在方法被继承地过程中,其可访问权限就被降低了。
练习6
interface Ex6 {
void sayOne();
void sayTwo();
void sayThree();
}
public class Ex6b implements Ex6 {
// Error: cannot assign weaker access to public methods:
// void sayOne() { System.out.println("one"); } // implies package * // access
// protected void sayTwo() { System.out.println("two"); }
// private void sayThree() { System.out.println("three"); }
// must be maintained public:
public void sayOne() { System.out.println("one"); }
public void sayTwo() { System.out.println("two"); }
public void sayThree() { System.out.println("three"); }
}
import interfaces.ex6.*;
public class TestEx6 {
public static void main(String[] args) {
Ex6b x = new Ex6b();
x.sayOne();
x.sayTwo();
x.sayThree();
}
}
练习7
* public class RandomRodentGenerator1 {
* private Random rand = new Random();
* public Rodent next() {
* switch(rand.nextInt(3)) {
* default:
* case 0: return new Mouse();
* case 1: return new Rat();
* case 2: return new Squirrel();
* }
* }
* }
*/
import static org.greggordon.tools.Print.*;
interface Rodent {
String name = "Rodent";
void eat();
void run();
void sleep();
String toString();
}
class Mouse implements Rodent {
private String name = "Mouse";
public void eat() { println("Mouse.eat()"); }
public void run() { println("Mouse.run()"); }
public void sleep() { println("Mouse.sleep()"); }
public String toString() { return name; }
}
class Rat implements Rodent {
private String name = "Rat";
public void eat() { println("Rat.eat()"); }
public void run() { println("Rat.run()"); }
public void sleep() { println("Rat.sleep()"); }
public String toString() { return name; }
}
class Squirrel implements Rodent {
private String name = "Squirrel";
public void eat() { println("Squirrel.eat()"); }
public void run() { println("Squirrel.run()"); }
public void sleep() { println("Squirrel.sleep()"); }
public String toString() { return name; }
}
public class Rodent7 {
private static RandomRodentGenerator1 gen = new RandomRodentGenerator1();
public static void main(String[] args) {
// Error: cannot instantiate (abstract) interface:
// Rodent x = new Rodent();
// But OK to create array of implementing objects:
Rodent[] rodents = new Rodent[10];
for(Rodent r : rodents) {
r = gen.next();
println(r + ": ");
r.eat();
r.run();
r.sleep();
}
}
}
练习8
interface FastFood {
void cheeseburger();
void fries();
void softDrink();
}
class Meal {
Meal() {
System.out.println("Meal()"); }
}
class Bread {
Bread() {
System.out.println("Bread()"); }
}
class Cheese {
Cheese() {
System.out.println("Cheese()"); }
}
class Lettuce {
Lettuce() {
System.out.println("Lettuce()"); }
}
class Lunch extends Meal {
Lunch() {
System.out.println("Lunch()"); }
}
class PortableLunch extends Lunch {
PortableLunch() {
System.out.println("PortableLunch()"); }
}
public class No8Sandwich extends PortableLunch implements FastFood {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lunch l = new Lunch();
public No8Sandwich() {
System.out.println("Sandwich8()"); }
public void cheeseburger() {
System.out.println("Cheeseburger");
}
public void fries() {
System.out.println("Fries");
}
public void softDrink(){
System.out.println("Soft Drink");
}
public static void main(String[] args) {
No8Sandwich s = new No8Sandwich();
s.cheeseburger();
s.fries();
s.softDrink();
}
}
===============================================================
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Meal()
Lunch()
Sandwich8()
Cheeseburger
Fries
Soft Drink
练习9
abstract class Instrument {
private int i;
public abstract void play(Note n);
public String toString() { return "Instrument"; }
public abstract void adjust();
}
class Wind extends Instrument {
public void play(Note n) {
print(this + ".play() " + n);
}
public String toString() { return "Wind"; }
public void adjust() { print(this + ".adjust()"); }
}
class Percussion extends Instrument {
public void play(Note n) {
print(this + ".play() " + n);
}
public String toString() { return "Percussion"; }
public void adjust() { print(this + ".adjust()"); }
}
class Stringed extends Instrument {
public void play(Note n) {
print(this + ".play() " + n);
}
public String toString() { return "Stringed"; }
public void adjust() { print(this + ".adjust()"); }
}
class Brass extends Wind {
public String toString() { return "Brass"; }
}
class Woodwind extends Wind {
public String toString() { return "Woodwing"; }
}
public class Music9 {
// Doesn't care about type, so new types
// added to the system will work right:
static void tune(Instrument i) {
//...
i.play(Note.MIDDLE_C);
}
static void tuneAll(Instrument[] e) {
for(Instrument i : e)
tune(i);
}
public static void main(String[] args) {
// Upcasting during addition to the array:
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
}
练习10
interface Instrument {
// Compile-time constant:
int VALUE = 5; // static and final
// Cannot have method definitions:
void adjust();
}
interface Playable {
void play(Note n); // Automatically public
}
class Wind implements Instrument, Playable {
public void play(Note n) {
print(this + ".play() " + n);
}
public String toString() { return "Wind"; }
public void adjust() { print(this + ".adjust()"); }
}
class Percussion implements Instrument, Playable {
public void play(Note n) {
print(this + ".play() " + n);
}
public String toString() { return "Percussion"; }
public void adjust() { print(this + ".adjust()"); }
}
class Stringed implements Instrument, Playable {
public void play(Note n) {
print(this + ".play() " + n);
}
public String toString() { return "Stringed"; }
public void adjust() { print(this + ".adjust()"); }
}
class Brass extends Wind {
public String toString() { return "Brass"; }
}
class Woodwind extends Wind {
public String toString() { return "Woodwing"; }
}
public class Music10 {
// Doesn't care about type, so new types
// added to the system will work right:
static void tune(Playable p) {
//...
p.play(Note.MIDDLE_C);
}
static void tuneAll(Playable[] e) {
for(Playable p : e)
tune(p);
}
public static void main(String[] args) {
// Upcasting during addition to the array:
Playable[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
}
9.3 完全解耦
只要方法操作的是个类而非接口,那么你就只能使用这个类以及接口。如果把这个方法应用于不在此继承结构中的某个类,那么就会发生错误。接口在很大程度上就 没有限制了
touppercast() //将字符串小心字符转换成大写字符
tolowercast() //转换成小心字符
split() //方法用于把一个字符串分割成字符串数组
Class.getName();//以String的形式,返回Class对象的‘实体’名称
创建一个能够根据所传递方法不同而具有不同行为的方法被称为 策略设计模式。
- 这类方法包含所要执行的算法中固定不变的部分
- 而 策略包含变化的部分 ** 策略就是传递进去的参数对象**,它(参数对象)包含所要执行的代码
apply.process 方法 和 process之间的耦合 过紧,这就使得复用 Apply.process()的代码时,复用却被禁止了。
但是 如果 Process是一个接口,那么限制就会变得松动
想要复用接口(减少代码) 客户端程序员就要遵循该接口 来编写他们自己的类:
当你碰到你无法修改你想要使用的类,可以使用 适配器设计模式,适配器中的代码将接收你所拥有的接口,并产生你所需要的接口
适配器就像一个中间件 包容两端
练习11
public interface Processor {
String name();
Object process(Object input);
}
class Apply{
public static void process(Processor p,Object s){
System.out.println("Using StringMixerAdapter"+p.name());
System.out.println(p.process(s));
}
}
package 第九章接口;
class StringMixer {
static String process(String s) {
char[] ca = new char[s.length()];
if((s.length())%2 == 0) {
for(int i = 0; i < s.length(); i += 2) {
ca[i] = s.charAt(i + 1);//charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1
ca[i + 1] = s.charAt(i);
}
return new String(ca);
}
else {
for(int i = 0; i < s.length() - 1; i += 2) {
ca[i] = s.charAt(i + 1);
ca[i + 1] = s.charAt(i);
}
ca[s.length() - 1] = s.charAt(s.length() - 1);
return new String(ca);
}
}
}
// program takes command line String argument:
// 配置适配器
class StringMixerAdapter implements Processor {
public String name() { return "StringMixerAdapter"; }
StringMixer stringMixer;//空引用
public StringMixerAdapter(StringMixer stringMixer) {
//传入 StringMixer 实例化对象
this.stringMixer = stringMixer;
//把这个空引用 指向当前对象的stringMixer 的实例化
}
public String process(Object input) {
return stringMixer.process((String)input);//在 Apply 里面被调用
}
}
public class No10StringMixerProcessor {
public static void main(String[] args) {
String s = "asdkfs";
Apply.process(new StringMixerAdapter(new StringMixer()), s);
//给StringMixerAdapter的构造器传入StringMixer的实例化对象;
}
}
=====================================================================
Using StringMixerAdapterStringMixerAdapter
sakdsf
StringMixerAdapter构造器接收你所拥有的接口,Filter然后生成具有你所需要的Processor接口对象
9.4 Java中的多重继承
Java中可以把 各种接口和具体类作为参数,当具体对象被创建时,可以向上转型为各种接口或者类作为参数传递给方法
使用接口的核心原因:
- 能够向上转型为多个基类型(以及由此而带来的灵活性)
- 与使用抽象方法相同,防止客户端创建该类的对象,并确保这仅仅是一个接口
我们该使用接口还是抽象类?
- 如果创建一个不带任何方法变量和成员变量的基类 —— 接口
- 如果知道某事物应该成为一个基类 —— 抽象类
练习12
interface CanFight {
void fight();
}
interface CanSwim {
void swim();
}
interface CanFly {
void fly();
}
interface CanClimb {
void climb();
}
class ActionCharacter {
public void fight() {}
}
class Hero extends ActionCharacter
implements CanFight, CanSwim, CanFly,CanClimb{
public void swim() {}
public void fly() {}
public void climb(){}
}
public class No12Adventure {
public static void t(CanFight x) {
x.fight();
System.out.println("fight");
}
public static void u(CanSwim x) {
x.swim();
System.out.println("Swim");
}
public static void v(CanFly x) {
x.fly();
System.out.println("Fly");
}
public static void c(CanClimb x) {
x.climb();
System.out.println("Climb");
}
public static void w(ActionCharacter x) {
x.fight();
System.out.println("fight");
}
public static void main(String[] args) {
Hero h = new Hero();
t(h); // Treat it as a CanFight
u(h); // Treat it as a CanSwim
v(h); // Treat it as a CanFly
c(h); // Treat it as a CanClimb
w(h); // Treat it as an ActionCharacter
}
}
===========================================================
fight
Swim
Fly
Climb
fight
Java中,一个类实现某个接口,必须重写接口中的所有方法吗?
“Java中,一个类实现了某接口,则必须实现该接口中的所有方法么?”这句话其实是不准确的,因为我们还没有考虑到抽象类。
抽象类实现某个接口,可以不实现所有接口的方法,可以由它的子类实现。而普通类即非抽象类则必须实现接口里的全部方法。
同样地,再来讲述一下接口:
1 接口的设计解决了java只能单继承的缺点,可以实现多个接口来实现java的多继承。
2 实现某个接口必须覆写其中的所有方法,当然也可以是一个空的实现(方法体为空没有任何作用),但是这个类必须是非抽象类,抽象类如上文。
练习13
package 第九章接口;
interface CanDo {
void doIt();
}
interface CanDoMore extends CanDo {
void doMore();
}
interface CanDoFaster extends CanDo {
void doFaster();
}
interface CanDoMost extends CanDoMore, CanDoFaster {
void doMost();
}
class Doer implements CanDoMost {
public void doIt() {}
public void doMore() {}
public void doFaster() {}
public void doMost() {}
}
public class No13DiamondInheritance {
public static void main(String[] args) {
Doer d = new Doer();
((CanDoMore)d).doMore();
((CanDoFaster)d).doFaster();
((CanDo)d).doIt();
}
}
9.5 通过继承来扩展接口
获得新接口
- 通过继承可以容易的在接口中添加新的方法声明。
- 还可以通过继承在新接口中组合数个接口。
extends 用于单一类继承,但是可以用于组合多个接口。
练习14
package 第九章接口;
interface History {
void u();
void v();
}
interface Exam {
void w();
void x();
}
interface Labs {
void y();
void z();
}
interface Workup extends History, Exam, Labs {
void zz();
}
class Doctor {
private int i = 0;
public void doc() {}
}
class Anesthesiologist extends Doctor implements Workup {
private int j = 0;
public void u() {}
public void v() {}
public void w() {}
public void x() {}
public void y() {}
public void z() {}
public void zz() {}
}
public class No14Ex {
public static void m1(History history) { history.v(); }
public static void m2(Exam exam) { exam.w(); }
public static void m3(Labs labs) { labs.y(); }
public static void m4(Workup workup) { workup.zz(); }
public static void main(String[] args) {
Anesthesiologist anes = new Anesthesiologist();
m1(anes);
m2(anes);
m3(anes);
m4(anes);
}
}
9.5.1 组合接口时的名字冲突
java里“方法过载”也就是“方法重载”,表示类中允许出现参数不同的同名方法,比如下面的类中的B方法就是
public class A{
public void B(){}
public void B(int b){}
}
9.6 适配接口
允许同一个接口具有多个不同的具体实现
接受接口类型的方法,而该接口的实现和该方法传递的对象则取决于方法的使用者
策略设计模式
你编写一个执行某些操作的方法,该方法接收一个同样你指定的接口
你可以 用任何你想要的对象 来调用我的方法,只要你的对象遵循我的接口。
练习16
java中.hasNext() 是什么意思?
这是java的迭代器的用法。
1)使用方法 iterator()要求容器返回一个 Iterator。第一次调用Iterator 的next()方法时,它返回序列的第一个元素。
2)使用next()获得序列中的下一个元素。
3)使用hasNext()检查序列中是否还有元素。
4)使用remove()将上一次返回的元素从迭代器中移除。
Scanner 的构造器接受的就是一个 Readable接口
Readable接口中的read()方法实现了将字符串读入charBuffer中,只有在需要输出的时候才会调用
import java.util.Random;
public class No14RandomChar {
private static Random rand = new Random();
public char next() {
return (char) rand.nextInt(128);
}
}
package 第九章接口;
import java.nio.CharBuffer;
import java.util.Scanner;
public class No16AdaptedRandomChar extends No14RandomChar implements Readable {
private int count;
public No16AdaptedRandomChar(int count) {
this.count = count;
}
public int read(CharBuffer cb) {//Readable 只要实现一个read 方法
if(count-- == 0) return -1;
String result = Character.toString(next()) + " ";
cb.append(result);
System.out.print("result"+result);
return result.length();
}
public static void main(String[] args) {
Scanner s = new Scanner(new No16AdaptedRandomChar(10));
while(s.hasNext())
System.out.println(s.next() + " ");
}
}
==================================================================
resultH H
resultJ J
result? ?
result
resultn n
resulth h
resulta a
resultn n
result[ [
result
9.7 接口中的域
你放进接口中的任何域 都自动时 static 和 final 的,所以接口就成了一种很便捷的用来创建常量组的工具
在Java SE5 之后有了 enum 就用不到了。
练习17
interface Days {
int SUNDAY = 1, MONDAY = 2, TUESDAY = 3, WEDNESDAY = 4,
THURSDAY = 5, FRIDAY = 6, SATURDAY = 7;
}
class Week implements Days {
private static int count = 0;
private int id = count++;
public Week() { System.out.println("Week() " + id); }
}
public class No17Ex {
public static void main(String[] args) {
// Without any objects, static fields exist:
System.out.println(Days.SUNDAY);
System.out.println("MONDAY = " + Days.MONDAY);
Week w0 = new Week();
Week w1 = new Week();
// Error: cannot assign a value to final variable SUNDAY:
// w0.SUNDAY = 2;
// Error: cannot assign a value to final variable MONDAY:
// w1.MONDAY = w0.MONDAY;
}
}
===========================================================
1
MONDAY = 2
Week() 0
Week() 1
9.7.1 初始化接口中的域
域 是static 类第一次被加载时初始化(任何域被访问);他们不是接口的一部分,值被存储在该接口的静态存储区域内。
9.9 接口工厂
生成一个遵循某个接口的对象 的典型方式就是 工厂方法设计模式
- 与直接调用构造器不同,工厂对象上调用的是创建方法
- 而该工厂对象将生成接口的某个实现的对象
- 代码将于接口完全分离
有利于 创建框架
练习18
interface Cycle {
void ride();
}
interface CycleFactory {
Cycle getCycle();
}
class Unicycle implements Cycle {
public void ride() {
System.out.println("Ride Unicycle"); }
}
class UnicycleFactory implements CycleFactory {
public Cycle getCycle() {
return new Unicycle();
}
}
class Bicycle implements Cycle {
public void ride() {
System.out.println("Ride Bicycle"); }
}
class BicycleFactory implements CycleFactory {
public Cycle getCycle() {
return new Bicycle();
}
}
class Tricycle implements Cycle {
Tricycle() {
System.out.println("Tricycle()"); }
public void ride() {
System.out.println("Ride Tricycle"); }
}
class TricycleFactory implements CycleFactory {
public Cycle getCycle() {
return new Tricycle();
}
}
public class No18Cycles {
public static void rideCycle(CycleFactory factory) {
Cycle c = factory.getCycle();
c.ride();
}
public static void main(String [] args) {
rideCycle(new UnicycleFactory());
rideCycle(new BicycleFactory());
rideCycle(new TricycleFactory());
}
}
===============================================================
Ride Unicycle
Ride Bicycle
Tricycle()
Ride Tricycle
import java.util.Random;
interface Games {
void play();
}
interface GamesFactory {
Games getGames();
}
class CoinToss implements Games {
Random rand = new Random();
public void play() {
System.out.print("Toss Coin: ");
switch(rand.nextInt(2)) {
case 0 :
System.out.println("Heads"); return;
case 1 :
System.out.println("Tails"); return;
default:
System.out.println("OnEdge"); return;
}
}
}
class CoinTossFactory implements GamesFactory {
public Games getGames() {
return new CoinToss(); // 返回 它的实例化对象
}
}
class DiceThrow implements Games {
Random rand = new Random();
public void play() {
System.out.print("Throw Dice: " + (rand.nextInt(6) + 1));
}
}
class DiceThrowFactory implements GamesFactory {
public Games getGames() {
return new DiceThrow();
}
}
public class No19 {
public static void playGame(GamesFactory factory) {
Games g = factory.getGames();
g.play();
}
public static void main(String [] args) {
playGame(new CoinTossFactory());
playGame(new DiceThrowFactory());
}
}
===========================================================================
Toss Coin: Heads
Throw Dice: 3
9.10 总结
确定接口时理想的选择,因而选择接口而不是类 是不对的优化设计,任何抽象性都是应真正的需求产生的。当必须时,你应该重构接口而不是到处添加额外级别的间接性,并由此带来额外的复杂性、恰当的选择是优先选择类而不是接口。如何接口的必要性变得非常明确,那么就进行重构。避免滥用