设计模式之适配器模式

适配器模式

概念及作用

  • 将一种类的接口转换为另一种客户期望的接口,在适配器模式下允许有不相同的接口的类一起工作
  • 使用新接口包装现有的类,使其能够满足需求
  • 将旧组件与新系统相匹配
1
2
3
4
5
6
7
8
9
10
重复使用一直是痛苦和难以捉摸的。一个原因是设计新东西的困难,同时重用旧的东西。  
旧的和新的之间总有一些不太对的东西。它可能是物理尺寸或不对齐。它可能是定时或同步。
这可能是不幸的假设或竞争标准。

这就像在旧的双插墙插座中插入一个新的三爪电插头的问题,某种适配器或中介是必要的。

适配器是关于创建中间抽象,将旧组件转换或映射到新系统。客户端调用Adapter对象上的方法,
该方法将它们重定向到对旧组件的调用。 此策略可以通过继承或聚合实现。

适配器用作现有类的包装器或修改器。 它提供该类的不同或翻译视图。

UML

适配器模式

  • Target:该角色定义要转换成的目标接口。
  • Adapee:需要被转换成目标角色的源角色。
  • Adapter:该角色是适配器模式的核心,其职责是通过继承或者类相关的方式,将源角色转换成目标角色

优缺点

优点

  • 适配器可以让两个没有任何关系的类在一起运行。
  • 增加了类的透明性
  • 增加了类的复用性
  • 增加了代码的灵活性

缺点

原文:

1
2
3
An "off the shelf" component offers compelling functionality that you would  
like to reuse, but its "view of the world" is not compatible with the philosophy
and architecture of the system currently being developed.

  • “现成”组件提供了您想要重用的功能,但其“世界观”与当前正在开发的系统的理念和架构不兼容。

例子

现实例子

通过将一个类的接口转换为客户端期望的接口,Adapter模式允许其他不兼容的类一起工作。 套接字扳手提供了适配器的示例。
如果驱动器的尺寸相同,则插座连接到棘轮。美国的典型驱动器尺寸为1/2“和1/4”。
显然,除非使用适配器,否则1/2“驱动棘轮将不适合1/4”驱动器插座。
1/2“至1/4”适配器具有1/2“内螺纹连接,可安装在1/2”驱动棘轮上,1/4“外螺纹连接适合1/4”驱动插座。

示例代码

(一)

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
interface Shape {
void draw(int x, int y, int z, int j);
}

class Line {
public void draw(int x1, int y1, int x2, int y2) {
System.out.println("Line from point A(" + x1 + ";" + y1 + "), to point B(" + x2 + ";" + y2 + ")");
}
}

class Rectangle {
public void draw(int x, int y, int width, int height) {
System.out.println("Rectangle with coordinate left-down point (" + x + ";" + y + "), width: " + width
+ ", height: " + height);
}
}

class LineAdapter implements Shape {
private Line adaptee;

public LineAdapter(Line line) {
this.adaptee = line;
}

@Override
public void draw(int x1, int y1, int x2, int y2) {
adaptee.draw(x1, y1, x2, y2);
}
}

class RectangleAdapter implements Shape {
private Rectangle adaptee;

public RectangleAdapter(Rectangle rectangle) {
this.adaptee = rectangle;
}

@Override
public void draw(int x1, int y1, int x2, int y2) {
int x = Math.min(x1, x2);
int y = Math.min(y1, y2);
int width = Math.abs(x2 - x1);
int height = Math.abs(y2 - y1);
adaptee.draw(x, y, width, height);
}
}

public class AdapterDemo {
public static void main(String[] args) {
Shape[] shapes = {new RectangleAdapter(new Rectangle()),
new LineAdapter(new Line())};
int x1 = 10, y1 = 20;
int x2 = 30, y2 = 60;
for (Shape shape : shapes) {
shape.draw(x1, y1, x2, y2);
}
}
}

(二)

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/* The OLD */
class SquarePeg {
private double width;

public SquarePeg(double width) {
this.width = width;
}

public double getWidth() {
return width;
}

public void setWidth(double width) {
this.width = width;
}
}

/* The NEW */
class RoundHole {
private final int radius;

public RoundHole(int radius) {
this.radius = radius;
System.out.println("RoundHole: max SquarePeg is " + radius * Math.sqrt(2));
}

public int getRadius() {
return radius;
}
}

// Design a "wrapper" class that can "impedance match" the old to the new
class SquarePegAdapter {
// The adapter/wrapper class "has a" instance of the legacy class
private final SquarePeg squarePeg;

public SquarePegAdapter(double w) {
squarePeg = new SquarePeg(w);
}

// Identify the desired interface
public void makeFit(RoundHole roundHole) {
// The adapter/wrapper class delegates to the legacy object
double amount = squarePeg.getWidth() - roundHole.getRadius() * Math.sqrt(2);
System.out.println( "reducing SquarePeg " + squarePeg.getWidth() + " by " + ((amount < 0) ? 0 : amount) + " amount");
if (amount > 0) {
squarePeg.setWidth(squarePeg.getWidth() - amount);
System.out.println(" width is now " + squarePeg.getWidth());
}
}
}

public class AdapterDemoSquarePeg {
public static void main( String[] args ) {
RoundHole roundHole = new RoundHole( 5 );
SquarePegAdapter squarePegAdapter;
for (int i = 6; i < 10; i++) {
squarePegAdapter = new SquarePegAdapter((double)i);
// The client uses (is coupled to) the new interface
squarePegAdapter.makeFit(roundHole);
}
}
}

(三)

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

public interface MediaPlayer {
public void play(String audioType, String fileName);
}


public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}



public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}

@Override
public void playMp4(String fileName) {
//什么也不做
}
}


public class Mp4Player implements AdvancedMediaPlayer{

@Override
public void playVlc(String fileName) {
//什么也不做
}

@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: "+ fileName);
}
}


public class MediaAdapter implements MediaPlayer {

AdvancedMediaPlayer advancedMusicPlayer;

public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}

@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}


public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;

@Override
public void play(String audioType, String fileName) {

//播放 mp3 音乐文件的内置支持
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: "+ fileName);
}
//mediaAdapter 提供了播放其他文件格式的支持
else if(audioType.equalsIgnoreCase("vlc")
|| audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
}
else{
System.out.println("Invalid media. "+
audioType + " format not supported");
}
}
}

public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();

audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}

结果:

1
2
3
4
Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported