Complete Decoupling

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangbingfengf98/article/details/85471262

Whenever a method works with a class instead of an interface, we are limited to using that class or its subclass. If we would like to apply the method to a class that isn't in that hierarchy, we're out of luck. An interface relaxes this constraint considerably. As a result, we can write more resuable code.

For example:

// interfaces/Applicator.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.

import java.util.*;

class Processor {
  public String name() {
    return getClass().getSimpleName(); // since 1.5
  }

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

class Upcase extends Processor {
  @Override // Covariant return:
  public String process(Object input) {
    return ((String) input).toUpperCase();
  }
}

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

class Splitter extends Processor {
  @Override
  public String process(Object input) {
    // split() divides a String into pieces:
    return Arrays.toString(((String) input).split(" ")); // Since:
1.4
  }
}

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

  public static void main(String[] args) {
    String s = "We are such stuff as dreams are made on";
    apply(new Upcase(), s);
    apply(new Downcase(), s);
    apply(new Splitter(), s);
  }
}
/* Output:
Using Processor Upcase
WE ARE SUCH STUFF AS DREAMS ARE MADE ON
Using Processor Downcase
we are such stuff as dreams are made on
Using Processor Splitter
[We, are, such, stuff, as, dreams, are, made, on]
*/

Creating a method that behaves differently depending on the argument object you pass it is called the Strategy design pattern. The method contains the fixed part of the algorithm, and the Strategy contains the part that varies. The Strategy is the object you pass in, containing code. Here, the Processor object is the Strategy, and main() shows three different Strategies applied to the String s.

Then suppose we discover a set of electronic filters that seem like they might fit into our Applicator.apply() method:

// interfaces/filters/Waveform.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package interfaces.filters;

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

  @Override
  public String toString() {
    return "Waveform " + id;
  }
}
// interfaces/filters/Filter.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package interfaces.filters;

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

  public Waveform process(Waveform input) {
    return input;
  }
}
// interfaces/filters/LowPass.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package interfaces.filters;

public class LowPass extends Filter {
  double cutoff;

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

  @Override
  public Waveform process(Waveform input) {
    return input; // Dummy processing
  }
}
// interfaces/filters/HighPass.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package interfaces.filters;

public class HighPass extends Filter {
  double cutoff;

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

  @Override
  public Waveform process(Waveform input) {
    return input;
  }
}
// interfaces/filters/BandPass.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package interfaces.filters;

public class BandPass extends Filter {
  double lowCutoff, highCutoff;

  public BandPass(double lowCut, double highCut) {
    lowCutoff = lowCut;
    highCutoff = highCut;
  }

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

Filter has the same interface elements as Processor , but because it isn’t inherited from Processor—because the creator of the Filter class had no clue we might want to use it as a Processor —we can’t use a Filter with the Applicator.apply() method, even though it would work fine. Basically, the coupling between Applicator.apply() and Processor is stronger than it needs to be, and this prevents the Applicator.apply() code from being reused when it ought to be. Also notice that the inputs and outputs are both Waveforms. If Processor is an interface, however, the constraints are loosened enough you can reuse an Applicator.apply() that takes that interface. Here are the modified versions of Processor and Applicator:

// interfaces/interfaceprocessor/Processor.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package interfaces.interfaceprocessor;

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

  Object process(Object input);
}
// interfaces/interfaceprocessor/Applicator.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package interfaces.interfaceprocessor;

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

The first way we can reuse code is if client programmers can write their classes to conform to the interface, like this:

// interfaces/interfaceprocessor/StringProcessor.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// javac interfaces/interfaceprocessor/StringProcessor.java
// {java interfaces.interfaceprocessor.StringProcessor} or {java interfaces/interfaceprocessor/StringProcessor}
package interfaces.interfaceprocessor;

import java.util.*;

interface StringProcessor extends Processor {
  @Override // remove this works fine.
  String process(Object input); // [1]

  String S = // [2]
      "If she weighs the same as a duck, " + "she's made of wood";

  static void main(String[] args) { // [3]
    Applicator.apply(new Upcase(), S);
    Applicator.apply(new Downcase(), S);
    Applicator.apply(new Splitter(), S);
  }
}

class Upcase implements StringProcessor {
  @Override // Covariant return:
  public String process(Object input) {
    return ((String) input).toUpperCase();
  }
}

class Downcase implements StringProcessor {
  @Override
  public String process(Object input) {
    return ((String) input).toLowerCase();
  }
}

class Splitter implements StringProcessor {
  @Override
  public String process(Object input) {
    return Arrays.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]
*/

[1] This declaration is unnecessary; the compiler will not complain if we remove it. However it does notate the covariant return change from Object to String.

[2] s is automatically static and final because it’s defined inside an interface.

[3] We can even define a main() inside an interface. // Please note this.

However, we are often in the situation of being unable to modify the classes. In the case of the electronic filters, for example, the library was discovered rather than created. In these cases, you can use the Adapter design pattern. In Adapter, we write code to take the interface we have and produce the interface we need, like this:

// interfaces/interfaceprocessor/FilterProcessor.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// {java interfaces.interfaceprocessor.FilterProcessor}
package interfaces.interfaceprocessor;

import interfaces.filters.*;

class FilterAdapter implements Processor {
  Filter filter;

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

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

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

public class FilterProcessor {
  public static void main(String[] args) {
    Waveform w = new Waveform();
    Applicator.apply(new FilterAdapter(new LowPass(1.0)), w);
    Applicator.apply(new FilterAdapter(new HighPass(2.0)), w);
    Applicator.apply(new FilterAdapter(new BandPass(3.0, 4.0)), w);
  }
}
/* Output:
Using Processor LowPass
Waveform 0
Using Processor HighPass
Waveform 0
Using Processor BandPass
Waveform 0
*/

In this approach to Adapter, the FilterAdapter constructor takes the interface we have—Filter—and produces an object that has the Processor interface we need. You might also notice delegation in the FilterAdapter class. Covariance allows us to produce a Waveform from process() rather than just an Object.

扫描二维码关注公众号,回复: 4798819 查看本文章

references:

1. On Java 8 - Bruce Eckel

2. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/Applicator.java

3. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/filters/Waveform.java

4. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/filters/Filter.java

5. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/filters/LowPass.java

6. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/filters/HighPass.java

7. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/filters/BandPass.java

8. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/interfaceprocessor/Processor.java

9. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/interfaceprocessor/Applicator.java

10. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/interfaceprocessor/StringProcessor.java

11. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/interfaces/interfaceprocessor/FilterProcessor.java

猜你喜欢

转载自blog.csdn.net/wangbingfengf98/article/details/85471262