09 接口

接口

解耦

使用接口可以实现完全解耦,解耦的字面意思就是解除耦合。

什么是耦合?

物理上的耦合是指:耦合是指两个或两个以上的体系或两种运动形式间通过相互作用而彼此影响以至联合起来的现象。
软件工程中的耦合是指:对象之间的耦合度就是对象之间的依赖性。通俗的说就是,我在一个具体的对象A里用了另一个具体的对象B。

耦合就是硬编码,把代码写死。

耦合度高有什么影响?

耦合度越高,代码之间的关联越大,影响是双向的,降低的代码的复用性和
比如在对象A中调用了对象B

  1. 下对上的影响:修改B的代码(甚至是删掉B的代码)可能导致A中调用的B的方法丢失,造成错误。

    举个例子:电脑拔掉了鼠标依然可以运行,就是解耦。

  2. 上对下的影响:A中的方法可能只适用于B,而不适用于其他的类。

    同样的例子:换个鼠标就用不了了。

解耦的核心思想是什么?

把关联依赖降到最低,实现模块化编程,而不至于牵一发而动全身,提高代码的复用和高可用,便于开发和维护。

使用接口如何实现解耦了?

我们先来看看没有使用接口的耦合案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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() argument divides a String into pieces:
return Arrays.toString(((String) input).split(" "));
}
}

public class Apply {
public static void process(Processor p, Object s) {
print("Using Processor " + p.name());
print(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 Splitter(), s);
}
}

上面是使用继承实现的策略设计模式

什么是策略设计模式

一个方法,由于传进来的参数的不同,表现不同的行为,这样的一种模式就是策略设计模式。
策略就是具体的参数对象,它是体现这个方法的不同的地方。

哪里耦合了?

之前的案例中的方式只能针对Processor以及它的子类,如果我的需求更改,要让另一个类Filter以及它的子类也能运用Apply中的process方法,这时,只能修改我们的Apply类或者另建一个新类。

使用接口如何实现解耦?

话不多说,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
public interface Processor {
String name();
Object process(Object input);
}

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

如果要让另一个类Filter以及它的子类也能运用Apply中的process方法,即让Filter也实现Processor即可。

使用实现接口比继承类好(更加解耦)的原因是:

  1. 接口其实是一个中间商,它并不偏袒任何一方,只是它提供了一个约定,要求双方都去遵循这个约定:对于实现方而言,接口要求你必须实现了我这些约定的方法;对于调用方而言,接口要求你只能调用我约定的方法,无法得知更多的信息。

多重继承

  1. 类不存在多重继承,也就意味着他只有一个父类,也就意味着它只能展示一个Process的多态性,如果要求它同时展示Process2的多态性的时候,就不行了,这其实就是耦合的一种表现形式。

接口可以继承,类可以实现多个接口。

适配接口

  1. 在我们之前提到的策略模式里,我们说让Filter也实现Processor就可以了,但是如果这个Filter是从其他地方引入进来的。而我们没法修改它的代码的时候怎么办?使用适配器模式,这也是接口完全解耦了的一个原因,类的话是无法实现适配器模式的。-

什么是适配器模式?

适配器模式是通过实现接口来包装一个已经存在的不兼容类,使其能和其它类中的方法兼容的工作模式。
可以类比生活中插头的例子,香港的插头只适用于香港那边的接口,如果要把香港的插头插到国内,就需要适配器(这里也清楚为什么使用类无法做到了,因为类无法多重继承)。

一般都过组合或者继承的方式来实现。
继承:

1
public class AdaptedRandomDoubles extends RandomDoubles implements Readbale{}

组合:

1
2
3
public class AdaptedRandomDoubles implements Readbale{
RandomDoubles randomDoubles = new RandomDoubles();
}

使用组合和继承有什么区别?

组合是对象适配器,只会包装对象使其工作。
继承是类适配器,能包装一个类以及它的所有子类。

适配器如何解决了上述问题?

直接上源码:

1
2
3
4
5
6
7
8
9
10
11
12
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() + " ");
}
}

这是一个没有实现Readable的类,如果想要Scanner中的方法去使用这个策略,需要使用适配器:

1
public class AdaptedRandomDoubles extends RandomDoubles implements Readbale

用接口实现工厂方法设计模式

工厂是指什么?

工厂是用来创建对象的接口。

什么是工厂方法设计模式?

让实现工厂接口的子类去决定具体创建哪一个对象的模式就是工厂方法设计模式。
案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Blacksmith {
Weapon manufactureWeapon(WeaponType weaponType);
}

public class ElfBlacksmith implements Blacksmith {
public Weapon manufactureWeapon(WeaponType weaponType) {
return new ElfWeapon(weaponType);
}
}

public class OrcBlacksmith implements Blacksmith {
public Weapon manufactureWeapon(WeaponType weaponType) {
return new OrcWeapon(weaponType);
}
}

为什么要使用工厂来创建对象?

使用工厂模式的目的之一还是为了提高代码的复用,减少耦合。
如果不使用工厂模式,就会在代码中使用到很多的 new 构造器()。
倘若以后这些构造器发生变化,又或者我需要换用其他的构造器,这样就需要在代码中一一修改。
那么这时候就可以使用工厂方法解决这个问题。
把new的过程放到工厂里,我在其他地方放心的调用工厂即可。

为什么不让类返回自己的实例?

如果说使用工厂方法是为了new一个实例,那用下面的方法也可以实现:
将构造器私有化,然后暴露一个返回实例对象的方法(实际上这已经有些类似单例模式了),如下:

1
2
3
4
5
6
7
8
9
interface Weapon{
Weapon getWeapon (WeaponType weaponType);
}
class ElfWeapon implements Weapon{
private ElfWeapon(WeaponType weaponType)_{}
public Weapon getWeapon(WeaponType weaponType) {
return new ElfWeapon(weaponType);
}
}

在我认为,上诉方法只是工厂方法的一个变形,类自身作为自身的工厂,代码上可行,但是不符合面向对象的程序设计,但是我们可以这样改进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Weapon{
}
public class ElfBlacksmith implements Blacksmith {
public Weapon manufactureWeapon(WeaponType weaponType) {
return new ElfWeapon(weaponType);
}
}
class ElfWeapon implements Weapon{
private ElfWeapon(WeaponType weaponType)_{}
public static GanmeFactory factory = new ElfBlacksmith();

public Weapon getWeapon(WeaponType weaponType) {
return factory.manufactureWeapon(weaponType);
}
}

但是我们依然发现有一个遗憾,这个工厂类的定义似乎有些多余了,因为它只会被使用一次,所以依然有改进的余地,至于如何改进,会在匿名内部类中讲解。

本文标题:09 接口

文章作者:Sun

发布时间:2018年10月01日 - 18:10

最后更新:2018年12月12日 - 16:12

原始链接:https://sunyi720.github.io/2018/10/01/THING IN JAVA/09 接口/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。