09 我读Thinking in java 接口

接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。
以上是引用文中作者原话,对于这句话个人觉得目前还是以接口为准,我们先只需要理解“接口为我们提供了一种将接口与实现分离的更加结构化的方法”,暂时忽略内部类,等到后面我们学到内部类的时候在回过头来一起学习。(第一个接口我们可以理解为interface,第二个接口我们理解为method)
好了,相信对于大多数初学java web方面的同学,或者入职一两年的同学都有过这样的实践,那就是在我们的web应用中一定会有这样的三类到四类的包结构:实体类DTO(vo、bo、po)、接口类(service、dao)、控制类(controller、action)等等。当我们在成为某一个行业内的职业工作者的时候,我们是否真的对于我们现有的工作有过更多的了解?我之前毕业的一两年时间里,我为了完成领导安排的工作,我不在乎时间,用尽当时自己的所有能力来完成工作,完全不去考虑工作以外的任何事情,当然,这种使用鲁莽的方式在工作,在两年后就让我在这个领域内慢慢脱离了出来,我变得迷茫起来。开始找工作,但对于有工作经验的人,也只有选择同类行业的工作性质,对于个人而言无非就是提升自身的薪资待遇而已,职业发展仍然是未知数,不知道是否有同学跟我有同样的经历和感受。总是忍不住扯些别的,好了,在我们做开发的时候,对于最基本的MVC三层架构的web应用来说,我们需要编写大量的业务逻辑的代码,无论JavaScript,ActionScript3,CSS,Html,JSP,Extjs,JQuery(我目前只是做了这些),还是后台业务逻辑,我们要像个机器一样不停的学习、开发、调试,然后你有没有注意到在业务层的实现总是需要implements某一个接口,然后再去写具体的实现。为什么?现在我们再来回顾作者的这句话“接口为我们提供了一种将接口与实现分离的更加结构化的方法”(暂时忽略内部类)。

9.1抽象类和抽象方法
语法:
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。(否则,编译器会报错)
如果一个抽象类不完整,那么当我们试图产生该类的对象时,编译器会怎样处理呢?由于为抽象类创建对象是不安全的,所以我们会从编译器那里得到一条出错信息,这样,编译器会确保抽象类的纯粹性,我们不必担心会误用它。
如果从一个抽象类继承,并想创建改新类的对象,那么就必须为基类中的所有抽象方法提供方法定义。如果不这样做(可以不这样做),那么导出类便也是抽象类,且编译器将会强制我们用abstract关键字限制这个类。

以上是书中作者的定义,个人理解如下:
1、具有抽象方法的类,一定是抽象类,但是抽象类不一定具有抽象方法。
2、抽象类继承抽象类可以不需要实现基类的抽象方法,类继承抽象类需要实现抽象方法。
对于抽象类,书中说:“可以使类的抽象性明确起来,并告诉用户和编译器打算怎样使用它们。抽象类还是很有用的重构工具,因为它们使得我们可以很容易地将公共方法沿着继承层次结构向上移动。”本人对此理解的仍不够深入,在实际开发中,只是在部分接口需要导出类自行确定其实现时才考虑使用抽象类。但对于刚开始看一些开源框架的源码时,经常跟进方法就跟到一个抽象类或者抽象方法,所以其实感觉作者总结的“重构工具”还是很经典的,需要我们站在一个模块,或者一个系统的角度来考虑。

9.2接口
一个接口表示“所有实现了该特定接口的类看起来都像这样”。因此,任何使用某些特定接口的代码都知道可以调用该接口的哪些方法,而且仅需知道这些。因此,接口被用来建立类与类之间的协议。(某些面向对象变成语言用关键字protocol来完成这一功能。)
但是,interface不仅仅是一个极度抽象的类,因为它允许人们通过创建一个能够被向上转型为多种类的类型,来实现某种类似多重继变种的特性。

要创建一个接口,需要用interface关键字来替代class关键字。就像类一样,可以在interface关键字前面添加public关键字。如果不添加public关键字,则它只具有包访问权限,这样它就只能在同一个包内可用。接口也可以包含域,但是这些隐式地是static和final的。
可以选择在接口中显示地将方法声明为public的,但即使你不这么做,它们也是public的。因此,当要实现一个接口时,在接口中被定义的方法必须被定义为public的;否则,它们将只能得到默认的包访问权限,这样子在方法被继承的过程中,其可访问权限就被降低了,这是java编译器所不允许的。
个人总结:
1、interface声明接口,内部方法默认public访问权限,没有方法体,内部域默认为static和final。
2、类通过implements实现一个或多个接口,同时,多个接口间通过逗号分开。必须实现接口内所有的方法。

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

以下是例子代码:

import java.util.*;

public class Apply {

    public static void process(Processor p,Object s){
        System.out.println("Using processer "+p.name());
        System.out.println(p.process(s));
    }

    public static 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 Spliter(),s);
    }
}

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 Spliter extends Processor{
    String process(Object input){
        return Arrays.toString(((String)input).split(" "));
    }
}

输出结果:

Using processer Upcase
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT
Using processer Downcase
disagreement with beliefs is by definition incorrect
Using processer Spliter
[Disagreement, with, beliefs, is, by, definition, incorrect]

Apply.process()方法可以接收任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。像本例这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被成为策略设计模式。这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。这里,Processor对象就是一个策略。(上面的代码跟接口半毛钱关系没有)
继续代码:

public class Waveform {
    private static long counter;
    private final long id = counter++;
    public String toString(){
        return "Waveform "+id;
    }
}
public class Filter {
    public String name(){
        return getClass().getSimpleName();
    }
    public Waveform process(Waveform input){
        return input;
    }
}
public class LowPass extends Filter {
    double cutoff;
    public LowPass(double cutoff){
        this.cutoff = cutoff;
    }
    public Waveform process(Waveform input){
        return input;
    }
}
public class HighPass extends Filter {
    double cutoff;
    public HighPass(double cutoff){
        this.cutoff = cutoff;
    }
    public Waveform process(Waveform input){
        return input;
    }
}
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;
    }
}

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

public interface Processor {
    String name();
    Object process(Object input);
}
public class Apply {
    public static void process(Processor p, Object s){
        System.out.println(p.name());
        System.out.println(p.process(s));
    }
}

复用代码的第一种方式是客户端程序员遵循该接口来编写它们自己的类。如下:


import java.util.Arrays;

public abstract class StringProcessor implements Processor{

    public String name(){
        return getClass().getSimpleName();
    }

    public abstract Object process(Object input);

    public static String s = "Disagreement with beliefs is by definition incorrect";

    public static void main(String[] args) {
        Apply.process(new Upcase(),s);
        Apply.process(new Downcase(),s);
        Apply.process(new Spliter(),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 Spliter extends StringProcessor {
    public String process(Object input){
        return Arrays.toString(((String)input).split(" "));
    }
}

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

public class FilterAdapter implements Processor{

    private Filter filter;

    public FilterAdapter(Filter filter){
        this.filter = filter;
    }

    @Override
    public Waveform process(Object input) {
        return filter.process((Waveform) input);
    }

    @Override
    public String name(){
        return getClass().getSimpleName();
    }
}

在这种使用适配器的方式中,FilterAdapter的构造器接受你所拥有的接口Filter,然后生成具有你所需要的Processor接口的对象。你可能还注意到了,在FilterAdapter中使用了代理。(我目前没看明白在哪里)
将接口从具体的实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也就更具可复用性。
个人理解:
作者对于完全解耦这块的阐述还是别有用心的,从一个简单的Processor类,被多处覆盖其process方法,到引入类似于Processor的Filter类,这样就产生了一种使用传统的继承方式,生成导出类覆盖其接口,导致耦合度很紧的弊端(注意Apply.prcesee(Processor p..)这个参数,可能你会想到,用Processor替换Filter不就可以了吗?虽然作者用破折号给了一个前提,但是如果Filter中有一些Processor没有的方法,是不是就要改变Processor的代码?而且改变的代码对于之前的Processor的导出类显然没有任何用处。所以作者后续给出的实现接口的第二种方式)从而引出接口的优势,同时还为客户实现接口的方式引出适配器模式。个人了解适配器模式包含类适配器和对象适配器两种方式,但具体模式还需结合实际应用场景来理解,最后再回归概念。同样不应为了使用模式而嵌套模式,适合的才是最好的。

9.4Java中的多重继承
接口不仅仅只是一种纯粹形式的抽象类,它的目标比这要高。因为接口是根本没有任何实现的——也就是说,没有任何与接口相关的存储;因此,也就无法阻止多个接口的组合。这一点是很有价值的,因为你有时候需呀去表示“一个x是一个a和一个b以及一个c”。C++中,组合多个类的接口被称作多重继承。它可能会使你背负很沉重的包袱,因为每个类都有一个具体实现。在java中,你可以执行相同的行为,但是只有一个类可以有具体实现;

在导出类中,不强制要求必须有一个是抽象的或“具体的(没有任何抽象方法的)”基类。如果要从一个非接口的类继承,那么只能从一个类去继承。其余的基元素都必须是接口。需要将所有的接口都置于imples关键字之后,用逗号将它们一一隔开。可以继承任意多个接口,并可以向上转型为每个接口,因为每一个接口都是一个独立类型
个人理解:
Class A extends B implements C,D,E
C c = new A();
B b = new A();
相信这样更简介一些。

作者在例子后面总结了这样一段话:“我们应该使用接口还是抽象类?如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。事实上,如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口”,后面的总结有这样一句话:恰当的原则应该是优先使用类而不是接口。从类开始,如果接口的必需性变得非常明确,那么就进行重构。接口时一种重要工具,但是它们容易被滥用

9.5通过继承来扩展接口
通过继承,可以很容易地在接口中添加新的方法声明,还可以通过继承在新接口中组合数个接口。这两种情况都可以获得新的接口。

个人理解:
interface A extends B,C,D

9.5.1组合接口时的名字冲突
在打算组合的不同接口中使用相同的方法名通常会造成代码可读性的混乱,请尽量避免这种情况。

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

代码如下:

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Random;
import java.util.Scanner;

public class RandomWords implements Readable {

    private static Random rand = new Random(47);

    private static final char[] captials = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();

    private static final char[] lowers = "abcdefghijklmnopqrstuvwxyz".toCharArray();

    private static final char[] vowels = "aeiou".toCharArray();

    private int count;

    public RandomWords(int count){
        this.count = count;
    }

    @Override
    public int read(CharBuffer cb) throws IOException {
        if(count-- == 0){
            return -1;
        }
        cb.append(captials[rand.nextInt(captials.length)]);
        for (int i = 0; i < 4; i++) {
            cb.append(vowels[rand.nextInt(vowels.length)]);
            cb.append(lowers[rand.nextInt(lowers.length)]);
        }
        cb.append(" ");
        return 10;
    }

    public static void main(String[] args) {
        Scanner s = new Scanner(new RandomWords(10));
        while (s.hasNext())
            System.out.println(s.next());
    }
}

上例的代码实现Readable接口后,即可将RandomWords作用在Scanner中。其实这个很类似于前面的Processor,只是直接这里面的Processor是接口Readable,作者之所以引入这部分代码示例,个人认为除了使用JDK中的实现作为有力的论证以外,还为了下面的适配使用接口的强大做出铺垫。
下面是一个未实现Readable接口的类,如何让Scanner作用于它?

public class RandomDoubles {

    private static Random rand = new Random(47);

    public double next(){
        return rand.nextDouble();
    }

    public static void main(String[] args) {
        RandomDoubles rd = new RandomDoubles();
        for (int i = 0; i < 7; i++) {
            System.out.print(rd.next()+" ");
        }
    }
}

下面是通过使用适配器模式

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Scanner;

public class AdapterRandomDoubles extends RandomDoubles implements Readable {

    private int count;

    public AdapterRandomDoubles(int count){
        this.count = count;
    }

    @Override
    public int read(CharBuffer cb) throws IOException {
        if(count-- == 0){
            return -1;
        }
        String result = Double.toString(next()) + " ";
        cb.append(result);
        return result.length();
    }

    public static void main(String[] args) {
        Scanner s = new Scanner(new AdapterRandomDoubles(7));
        while (s.hasNextDouble())
            System.out.print(s.nextDouble() + " ");
    }
}

请注意String result = Double.toString(next()) + " ";这段代码,相信你会体会到作者的用意。(如果没体会到没关系,下面作者的总结会让你豁然开朗)

总结:因为在这种方式中,我们可以在任何现有类之上添加新接口,所以这意味着让方法接受接口类型,是一种让任何类都可以对该方法进行适配的方式。这就是使用接口而不是类的强大之处。

9.7接口中的域
因为你放入接口中的任何域都自动是static和final的,所以接口就成为了已中很便捷的用来创建常量组的工具。在Java SE5中,这是产生与C或C++中enum(枚举类型)具有相同效果的类型的唯一途径。因此在Java SE5之前的代码中你会看到下面这样的代码:

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;
}

对于以上这种情况,大家只要只要这个域隐形的是public static final即可,SE5之后会有enum更好的实现,以后会学到。

9.7.1初始化接口中的域
在接口中定义的域不能是“空final”,但是可以被非常量表达式初始化。

interface RandVals{
    Random RAND = new Random(47);
    int RANDOM_INT = RAND.nextInt(10);
    long RANDOM_LONG = RAND.nextLong();
    float RANDOM_FLOAT = RAND.nextFloat();
    double RANDOM_DOUBLE = RAND.nextDouble();
}

当然,这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域内。

9.8嵌套接口
接口可以嵌套在类或其他接口中,这揭示了许多非常有趣的特性

class A {

    interface B{
        void f();
    }

    public class BImpl implements B{
        public void f() {}
    }

    private class BImpl2 implements B{
        public void f() {}
    }

    public interface C{
        void f();
    }

    class CImpl implements C{
        public void f() {}
    }

    private class CImpe2 implements C{
        public void f() {}
    }

    private interface D{
        void f();
    }

    private class DImpl implements D{
        public void f() {}
    }

    public class DImp2 implements D{
        public void f() {}
    }

    class DImpl3 implements D{
        public void f() {}
    }

    public D getD(){
        return new DImp2();
//        return new DImpl();
    }

    private D dRef;

    public void received(D d){
        dRef = d;
        dRef.f();
    }

}

interface E{

    interface G{
        void f();
    }

    public interface H{
        void f();
    }

    void g();
}

public class NestingInterfaces {

    public class BImpl implements A.B{
        public void f() {}
    }

    class CImpl implements A.C{
        public void f() {}
    }
    //cannot implement a private interface except
    //with that interface's defining class
    //!class DImp implements A.D{
    //! public void f() {}
    //! }
    class EImpl implements E{
        public void g() {}
    }

    class EGImpl implements E.G{
        public void f() {}
    }

    class EImp2 implements E{
        public void g() {}
        class EG implements E.G{
            public void f() {}
        }
    }

    public static void main(String[] args) {
        A a = new A();
        // Can't access A.D;
        //! A.D ad = a.getD();
        //Doesn't return anything but A.D
        //! A.DImp2 di2 = a.getD();
        // Cannot access a member of the interface;
        //! a.getD().f();
        // Only another A can do anything with getD();
        A a2 = new A();
        a2.received(a.getD());
    }
}

说实话,这段代码我真的不想看,头好晕,但慢慢看下来还是可以理解一部分的。
在类中嵌套接口的语法是相当显而易见的,就像非嵌套接口一样,可以拥有public和“包访问”两种可视性。
作为一种新添加的方式,接口也可以被实现为private的,就像在A.D中所看到的(相同的语法既适用于嵌套接口,也适用于嵌套类)。那么private的嵌套接口能带来什么好处呢?读者可能会猜想,它只能够被实现为DImpl中的一个private内部类,但是A.DImpl2展示了它同样可以被实现为public类。但是,A.DImpl2只能被其自身所使用。你无法说它实现了一个private接口D。因此,实现一个private接口只是一种方式,它可以强制该接口中的方式定义不要添加任何类型信息(也就是说,不允许向上转型)。
getD()方法使我们进入一个进退两难的境地,这个问题与private接口相关:它是一个返回对private接口的引用的public方法。你对这个方法的返回值能做些什么呢?在main()中,可以看到数次尝试使用返回值的行为都失败了。只有一种方式可以成功,那就是将返回值交给有权使用它的对象。在本例中,是另一个A通过receiveD()方法来实现的。
接口E说明接口彼此之间也可以嵌套。然而,作用于接口的各种规则,特别是所有的接口元素都必须是public的,在此都会被严格执行。因此,嵌套在另一个接口中的接口自动就是public的,而不能声明为private的。
NestingInterfaces展示了嵌套接口的各种实现方式。特别要注意的是,当实现某个接口时,并不需要实现嵌套在其内部的任何接口。而且,private接口不能在定义它的类之外被实现。
添加这些特性的最初原因可能是出于对严格的语法一致性的考虑,但是我总认为,一但你了解了某种特性,就总能够找到它的用武之地。(对于此处的理解我很小心的不敢提出任何便捷简单的理解,所以将作者原文一一摘录进来)
个人理解:
对于访问权限private的理解,我们很容易被类的封装所固化,可能我们写过很多实体类,无论vo、bo、po哪个层次的实体,都习惯于private声明属性,生成public的get和set方法,时间长了我们

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

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

interface ServiceFactory{
    Service getService();
}

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

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

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

class Implemention2Factory implements ServiceFactory{
    public Service getService() {
        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 Implemention1Factory());
        serviceConsumer(new Implemention2Factory());
    }
}

如果不是工厂方法,你的代码就必须在某处指定将要创建的Service的确切类型,以便调用合适的构造器。
为什么我们想要添加这种额外的间接性呢?一个常见的原因是想要创建框架:假设你正在创建一个对弈游戏系统,例如,在相同的棋盘上下国际象棋和西洋跳棋:

interface Game{
    boolean move();
}
interface GameFactory{
    Game getGame();
}
class Checkers implements Game{
    private int moves = 0;
    private static final int MOVES = 3;
    public boolean move() {
        System.out.println("Checkers move "+moves);
        return ++moves != MOVES;
    }
}
class CheckersFactory implements GameFactory{
    public Game getGame() {
        return new Checkers();
    }
}

class Chess implements Game{
    private int moves = 0;
    private static final int MOVES = 3;
    public boolean move() {
        System.out.println("Chess move "+moves);
        return ++moves != MOVES;
    }
}

class ChessFactory implements GameFactory{
    public Game getGame() {
        return new Chess();
    }
}

public class Games {
    public static void playGame(GameFactory factory){
        Game s = factory.getGame();
        while(s.move())
            System.out.println("...");
    }

    public static void main(String[] args) {
        playGame(new CheckersFactory());
        playGame(new ChessFactory());
    }
}

如果Games类表示一段复杂的代码,那么这种方式就允许你在不同类型的游戏中复用这段代码。
个人理解:
对于工厂模式,其实我的理解并不深入,曾经我一再的问自己,为什么用工厂模式,工厂模式的好处是什么?我相信使用spring的同学一定都用过spring的beanFactory,当我们还停留在使用它的时候,我们是不是应该想一下为什么要这样用?那么看过上面的作者的讲解,我想我们应该可以初步的了解了,在这里我阐述一下我自己的理解:
spring的ioc其实就是使用java的反射机制,那么java的反射的代码比较复杂,并且如果使用反射来创建对象的话,那么大家其实调用的是同样的代码(这就是上面作者说的复用),所以Spring提供给我们一个beanFactory,我们将此bean的地址传给spring,spring来复用那段反射的代码帮我们生成bean对象。所以,至此你是不是对为什么使用工厂模式有一点不一样的理解了呢?我在网上和书上了解了一些对工厂模式的阐述,我浅显的理解为,一个工厂,根据流水线的配置(复杂代码),生成同样的产品(这里拿上面的Game为例)。不同的工厂会有不同的流水线,所以其实工厂模式我们不要理解的太复杂,根据名字来理解其实会让我们更清晰,至于它的好处,我们只要理解工厂里面流水线的好处就可以了。

9.10总结
“确定接口是理想选择,因而应该总是选择接口而不是具体的类。”这其实是一种引诱。当然,对于创建类,几乎在任何时刻,都可以替代为创建一个接口和一个工厂。
许多人都掉进了这种诱惑的陷阱,只要有可能就去创建接口和工厂。这种逻辑看起来好像是因为需要使用不同的具体实现,因此总是应该添加这种抽象性。这实际上已经变成了一种草率的设计优化。
任何抽象性都应该是应真正的需求而产生的。当必须时,你应该重构接口而不是导出添加额外级别的间接性,并由此带来的额外的复杂性。这种额外的复杂性非常显著,如果你让某人去处理这种复杂性,只是因为你意识到由于以防万一而添加了新接口,而没有其他更有说服力的原因,那么好吧,如果我碰上这种事,那么就会质疑此人所作的所有设计了。
恰当的原则应该是优先选择类而不是接口。从类开始,如果接口的必需性变得非常明确,那么就进行重构。接口是一种重要的工具,但是它们容易被滥用。

以上作者的总结,我不敢做任何修改,我相信读此书的同学,或者由于工作需要想要读此书,却没有足够的条件的同学,肯定愿意看到这段作者原文的总结,也许你看到我整理的这个文章只为最后的这段总结而来,为此我感到非常荣幸。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值