设计模式之适配器模式与外观模式

时间:2021-10-08 21:59:10

适配器模式

适配器模式就是将一个接口转化成另一个接口。

一个常见的例子是插头转换器

我们知道英式插头长这样:

设计模式之适配器模式与外观模式

而国内的插头一般是这样的

设计模式之适配器模式与外观模式

这时候,你就需要一个转换插头了

设计模式之适配器模式与外观模式

 

而适配器的工作就像是一个转换插头。你不需要从新买一个电器,也不用换一个插孔,只需要一个转换器即可。

 

假设已有一个软件系统,你希望它能和新的厂商类库搭配使用,但是这个新厂商设计出来的接口,不同于旧厂商的接口。

你不想改变现有的代码,也不能改变厂商的代码,于是你可以写一个类,将新厂商接口转成你所希望的接口。

设计模式之适配器模式与外观模式

情景:

首先你有一个鸭子接口(这书有毒啊啊啊这都什么破例子)

public interface Duck {
public void quack();
public void fly();
}

有一个鸭子的子类

// 绿头鸭
public class MallardDuck implements Duck {

@Override
public void quack() {
System.out.println(
"Quack");
}

@Override
public void fly() {
System.out.println(
"I'm flying");
}

}

然后现在有一只火鸡

public interface Turkey {
public void gobble();
public void fly();
}

 

public class WildTurkey implements Turkey {

@Override
public void gobble() {
System.out.println(
"Gobble gobble");
}

@Override
public void fly() {
System.out.println(
"I'm flying a short diatance");
}

}

 

因为你想要鸭子,鸭子不够用了,但是我们有火鸡,于是你决定为火鸡写一个配适器,偷偷地假装它们是鸭子。。。。

当需要鸭子叫的时候,就让火鸡叫,当需要鸭子飞的时候,就让火鸡飞。火鸡飞的距离短,所以还要多飞几次。

public class TurkeyAdapter implements Duck {
Turkey turkey;

public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}

public void quack() {
turkey.gobble();
}

public void fly() {
// 火鸡飞行距离短 要连续飞五次才能对应鸭子的飞行
for (int i = 0; i < 5; ++i) {
turkey.fly();
}
}

}

然后测试一下:

public class DuckTestDrive {
public static void main(String[] args) {
MallardDuck duck
= new MallardDuck();

WildTurkey turkey
= new WildTurkey();
Duck turkeyAdapter
= new TurkeyAdapter(turkey);

System.out.println(
"The turkey says...");
turkey.gobble();
turkey.fly();

System.out.println(
"\nThe Duck says...");
testDuck(duck);

System.out.println(
"\nThe TurkeyAdapter says...");
testDuck(turkeyAdapter);
}

private static void testDuck(Duck duck) {
duck.quack();
duck.fly();
}
}

输出:

The turkey says...
Gobble gobble
I
'm flying a short diatance

The Duck says...
Quack
I
'm flying

The TurkeyAdapter says...
Gobble gobble
I
'm flying a short diatance
I'm flying a short diatance
I'm flying a short diatance
I'm flying a short diatance
I'm flying a short diatance

我们就可以把火鸡当成鸭子了~(真·有毒。。。。

 

适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

类图:

设计模式之适配器模式与外观模式

上面的类图表现的是“对象”适配器,还有一种适配器,被称作“类”适配器,需要多重继承才能实现,所以在Java中是不可能的。

设计模式之适配器模式与外观模式

现实世界一个简单的适配器。

在早期的集合实现如Vector,Stack,HashTable都实现了一个elements()的方法,该方法会返回一个Enumeration,这个接口可以逐一走过集合内的每一个元素。

而新的集合类,使用Iterator来遍历集合。

现在,将枚举适配到迭代器。

设计模式之适配器模式与外观模式

import java.util.Enumeration;
import java.util.Iterator;

public class EnumerationIterator implements Iterator {

Enumeration enu;

public EnumerationIterator(Enumeration enu) {
this.enu = enu;
}

@Override
public boolean hasNext() {
return enu.hasMoreElements();
}

@Override
public Object next() {
return enu.nextElement();
}

public void remove() {
throw new UnsupportedOperationException();
}

}

反过来也可以将迭代器适配到枚举

import java.util.Enumeration;
import java.util.Iterator;

public class IteratorEnumeration implements Enumeration {
Iterator iterator;

public IteratorEnumeration(Iterator iterator) {
this.iterator = iterator;
}

@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}

@Override
public Object nextElement() {
return iterator.next();
}
}

测试一下:

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;

public class ArrayListEnumTest {
public static void main(String[] args) {
ArrayList list
= new ArrayList();
list.add(
"适");
list.add(
"配");
list.add(
"器");
list.add(
"模");
list.add(
"式");
Iterator iterator
= list.iterator();
Enumeration ei
= new IteratorEnumeration(iterator);
showListByEnum(ei);
}

// 一些遗留的“客户代码”仍然使用依赖于枚举接口
public static void showListByEnum(Enumeration enumeration) {
while (enumeration.hasMoreElements()) {
System.out.print(enumeration.nextElement());
}
}
}

 

 

外观模式

情景:当你需要看一场电影的时候,你可能需要调灯光放屏幕打开投影机选择模式……每一次都是一样的步骤,等结束的时候,又是一系列繁琐的动作。

如果提供一个简单的类,能够一下就完成这一系列工作就好了。

这时就需要用外观模式,将一系列子类包装起来,提供一个简单的接口,来替代访问一系列子系统的接口。

  • 外观模式不只简化了接口,也将客户从组件的子系统中解耦。
  • 外观模式和适配器模式可以包装许多类,但外观的意图是简化接口,而适配器的意图是将接口转换成不同接口。

 

原谅自己只能写一个拙劣的例子,折叠掉。。。

设计模式之适配器模式与外观模式设计模式之适配器模式与外观模式
package com.wenr.chapter7;

public class FacadeTest {
public static void main(String[] args) {
A a
= new A();
B b
= new B();
C c
= new C();
Facade facade
= new Facade(a, b, c);
facade.ABC1();
System.out.println();
facade.ABC2();
}
}


class A {
public void a1() {
System.out.println(
"a1");
}
public void a2() {
System.out.println(
"a2");
}
}

class B {
public void b1() {
System.out.println(
"b1");
}
public void b2() {
System.out.println(
"b2");
}
}

class C {
public void c1() {
System.out.println(
"c1");
}
public void c2() {
System.out.println(
"c2");
}
}

class Facade {
A a;
B b;
C c;
public Facade(A a, B b, C c) {
this.a = a;
this.b = b;
this.c = c;
}
public void ABC1() {
a.a1();
b.b1();
c.c1();
}
public void ABC2() {
a.a2();
b.b2();
c.c2();
}
}
View Code

 

外观模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

类图:

设计模式之适配器模式与外观模式

设计原则:最少知识原则(又称作墨忒耳法则,Law of Demeter)

只和你的密友交谈。

这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中的一部分,会影响到其他部分。

怎么才能避免这样?这个原则提供了一些方针:就任何对象而言,在该对象的方法内,我们只应调用属于以下范围的方法:

  • 该对象本身
  • 被当做方法的参数而传递进来的对象
  • 此方法所创建或实例化的任何对象
  • 对象的任何组件

举个栗子:

不使用这个原则:

public float getTemp() {
Thermometer thermometer
= station.getThermometer();
return thermometer.getTemperature();
}

使用这个原则:

public float getTemp() {
return station.getTemperature();
}

应用此原则时,在气象站中加一个方法,用来向温度计请求温度。这可以减少我们所依赖的类的数目。

 

综上:

当需要使用一个现有的类而其接口并不符合你的需求时,就使用适配器。

当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观。