Java之完全解耦

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

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

package interfaces.classprocessor;

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) {
        //Covariant return
        return ((String) input).toUpperCase();
    }
}

class Downcase extends Processor {
    String process(Object input) {
        return ((String) input).toLowerCase();
    }
}

class Splitter extends Processor {
    String process(Object input) {
        //the split() arguments divides a String into pices:
        return Arrays.toString(((String) input).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 String s = "Disagreement with beliefs if 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对象上,然后打印结果。创建一个能够根据所传递的参数对象的不同而执行不同行为的方法,被称为策略模式。这种方法包含所要执行的算法中固定不变的部分,而“策略”包含在变化的部分。策略就是传递进去的参数对象,它包含索要执行的代码。这里Processor对象就是一个策略,在main方法中,可以看到有三种不同类型的策略应用到了String类型的s对象上。

split()方法是String类型的一部分,他接受String类型的对象,并以传递进来的参数作为边界,将该String类型的对象分割开,然后返回一个数据String[]。它在这里被用来当做创建String数组的快捷方式。

现在假设我们发现了一组电子滤波器,他们看起来好像适用于Apply.process()方法。

package filters;

class Waveform {
    private static long counter;
    private final long id = counter++;

    public String toString() {
        return "Waveform " + id;
    }
}


package filters;

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

    public Waveform process(Waveform input) {
        return input;
    }
}

package filters;

public class LowPass extends Filter{
    double cutoff;

    public LowPass(Double cutoff) {
        this.cutoff = cutoff;
    }

    public Waveform process(Waveform input) {
        return input;//Dummy processing
    }
}

package filters;

public class HighPass extends Filter {
    double cutoff;

    public HighPass(double cutoff) {
        this.cutoff = cutoff;
    }

    public Waveform process(Waveform input) {
        return input;//Dummy processing
    }
}

package filters;

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;//Dummy processing
    }
}

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

package interfaceprocessor;

public interface Processor {
    String name();

    Object process(Object input);
}

package interfaceprocessor;

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

package interfaceprocessor;

import java.util.Arrays;

//复用代码的第一种方式是客户端程序员遵循该接口来编写他们自己的类,
public abstract class StringProcessor implements Processor {
    public String name() {
        return getClass().getSimpleName();
    }

    public abstract String process(Object imput);

    public static String s = "If she weights the same as a duck, she's made of wood";

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

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

package interfaceprocessor;

import filters.Filter;
import filters.Waveform;

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

package interfaceprocessor;

import filters.BandPass;
import filters.HighPass;
import filters.LowPass;
import filters.Waveform;

public class FilterProcessor {
    public static void main(String[] arg) {
        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()接口的对象。在FilterAdapter类中用到了代理。将接口从具体实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也更加具有可复用性。

文章内容出自《Thinking in Java》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容