适配器模式
包装某些对象,让它们的接口看起来不像自己而像是别的东西。
将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
比如现有一个软件系统,有一些接口,但是与所需的接口不太匹配,而又不想或者不能修改软件系统的接口并且需要使用,这时需要一个适配器将软件系统接口转换成所需的接口。在生活中就好比转接口的作用。
客户使用适配器过程:
- 客户通过目标接口调用适配器的方法对适配器发出请求。
- 适配器使用被适配者接口把请求转换成被适配者的一个或多个调用接口。
- 客户接收到调用的结果,但未察觉是适配器所起的转换作用。
类适配器:
Adapter 类继承 Adaptee (被适配类),同时实现 Target 接口(因为 Java 不支持多继承,所以只能通过接口的方法来实现多继承),在 Client 类中我们可以根据需要选择并创建任一种符合需求的子类,来实现具体功能。
只能采用某个特定的被适配类,不需要重新实现整个被适配者,可以适配被适应者的行为,因为利用了继承的方式。
对象适配器:
不使用多继承或继承的方式,而是使用直接关联,或者称为委托的方式。
使用组合,不仅可以适配某个类,也可以适配该类的任何子类。
示例
/**
* 鸭子接口
*
* @author NNroc
* @date 2020/8/14 11:06
*/
public interface Duck {
public void quack();
public void fly();
}
/**
* 绿头鸭
*
* @author NNroc
* @date 2020/8/14 11:07
*/
public class MallardDuck implements Duck {
public void quack() {
System.out.println("Quack");
}
public void fly() {
System.out.println("I'm flying");
}
}
/**
* 火鸡
*
* @author NNroc
* @date 2020/8/14 11:08
*/
public interface Turkey {
public void gobble();
public void fly();
}
/**
* 正常的火鸡
*
* @author NNroc
* @date 2020/8/14 11:11
*/
public class WildTurkey implements Turkey {
public void gobble() {
System.out.println("Gobble gobble");
}
public void fly() {
System.out.println("I'm flying a short distance");
}
}
/**
* 火鸡对象伪造鸭子对象
*
* @author NNroc
* @date 2020/8/14 11:09
*/
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();
}
}
}
/**
* @author NNroc
* @date 2020/8/14 11:13
*/
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);
}
static void testDuck(Duck duck) {
duck.quack();
duck.fly();
}
}
当下正在使用的适配器
集合类型:Vector、Stack、Hashtable 等都实现了一个名字为 elements() 的方法。该方法会返回一个 Enumeration 枚举器接口。这个接口用来遍历集合中的每个元素,而无需知道它们在集合内是如何被管理的。
Iterator 迭代器接口,在遍历集合中删除元素使用,与 Enumeration 接口不同的是 Iterator 提供了删除元素的能力。
将枚举适配到迭代器
EnumerationIterator 适配器
import java.util.Enumeration;
import java.util.Iterator;
/**
* @author NNroc
* @date 2020/8/14 14:09
*/
public class EnumerationIterator implements Iterator {
Enumeration enumeration;
public EnumerationIterator(Enumeration enumeration) {
this.enumeration = enumeration;
}
public boolean hasNext() {
return enumeration.hasMoreElements();
}
public Object next() {
return enumeration.nextElement();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
IteratorEnumeration 适配器
import java.util.Enumeration;
import java.util.Iterator;
/**
* @author NNroc
* @date 2020/8/14 14:27
*/
public class IteratorEnumeration implements Enumeration {
Iterator iterator;
public IteratorEnumeration(Iterator iterator) {
this.iterator = iterator;
}
public boolean hasMoreElements() {
return iterator.hasNext();
}
public Object nextElement() {
return iterator.next();
}
}
外观模式
把对象包装起来以简化其接口。
提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
设计原则:
- 最少知识原则:只和你的密友谈话。(不要让太多的类耦合在一起,免得修改系统的一部分会影响到其它部分。防止许多类互相依赖,以防系统就会变成一个易碎的系统,需要花许多时间维护,且不容易被人理解)
如何实现这个原则?
在该对象的方法内,我们只应该调用属于以下范围的方法:
- 对该对象本身。
- 被当做方法的参数而传递进来的对象。
- 此方法所创建或实例化的任何对象。
- 对象的任何组件。
示例
/**
* @author NNroc
* @date 2020/8/14 14:42
*/
public class Thermometer {
private double temperature = 36.6;
public double getTemperature() {
return temperature;
}
public void setTemperature(double temperature) {
this.temperature = temperature;
}
}
/**
* @author NNroc
* @date 2020/8/14 14:38
*/
public class WeatherStation {
Thermometer thermometer;
public WeatherStation() {
thermometer = new Thermometer();
}
public Thermometer getThermometer() {
return thermometer;
}
public void setThermometer(Thermometer thermometer) {
this.thermometer = thermometer;
}
}
/**
* @author NNroc
* @date 2020/8/14 14:38
*/
public class House {
WeatherStation station;
// 其他的方法和构造器
// 违反最少知识原则,因为此调用的方法属于另一侧调用返回的对象
// public double getTemp(){
// return station.getThermometer().getTemperature();
// }
// 没有违反最少知识原则,但是没什么意义
public double getTemp() {
Thermometer thermometer = station.getThermometer();
return getTempHelper(thermometer);
}
public double getTempHelper(Thermometer thermometer) {
return thermometer.getTemperature();
}
}
电影播放示例
model
/**
* 影院灯光
*
* @author NNroc
* @date 2020/8/14 14:51
*/
public class TheaterLights {
String description;
public TheaterLights(String description) {
this.description = description;
}
public void on() {
System.out.println(description + " 打开");
}
public void off() {
System.out.println(description + " 关闭");
}
public void dim(int level) {
System.out.println(description + " 亮度调节到:" + level + "%");
}
public String toString() {
return description;
}
}
/**
* 屏幕
*
* @author NNroc
* @date 2020/8/14 14:51
*/
public class Screen {
String description;
public Screen(String description) {
this.description = description;
}
public void up() {
System.out.println(description + " 上升");
}
public void down() {
System.out.println(description + " 下降");
}
public String toString() {
return description;
}
}
/**
* 音响
*
* @author NNroc
* @date 2020/8/14 14:52
*/
public class Amplifier {
String description;
public Amplifier(String description) {
this.description = description;
}
public void on() {
System.out.println(description + " 打开");
}
public void off() {
System.out.println(description + " 关闭");
}
//立体声
public void setStereoSound() {
System.out.println(description + " 立体声模式");
}
//环绕声
public void setSurroundSound() {
System.out.println(description + " 环绕声模式");
}
public void setVolume(int level) {
System.out.println(description + " 调整音量到: " + level);
}
public String toString() {
return description;
}
}
/**
* 播放器
*
* @author NNroc
* @date 2020/8/14 14:53
*/
public class DvdPlayer {
String description;
int currentTrack;
Amplifier amplifier;
String movie;
public DvdPlayer(String description, Amplifier amplifier) {
this.description = description;
this.amplifier = amplifier;
}
public void on() {
System.out.println(description + " 播放");
}
public void off() {
System.out.println(description + " 关闭");
}
public void play(String movie) {
this.movie = movie;
currentTrack = 0;
System.out.println(description + " 播放 \"" + movie + "\"");
}
public String toString() {
return description;
}
}
/**
* 投影仪
*
* @author NNroc
* @date 2020/8/14 14:52
*/
public class Projector {
String description;
DvdPlayer dvdPlayer;
public Projector(String description, DvdPlayer dvdPlayer) {
this.description = description;
this.dvdPlayer = dvdPlayer;
}
public void on() {
System.out.println(description + " 打开");
}
public void off() {
System.out.println(description + " 关闭");
}
public void wideScreenMode() {
System.out.println(description + " 调整到宽屏模式");
}
public void tvMode() {
System.out.println(description + " 调整到tv模式");
}
public String toString() {
return description;
}
}
测试
/**
* @author NNroc
* @date 2020/8/14 14:50
*/
public class TestWithoutFacade {
public static void main(String[] args) {
Amplifier amp = new Amplifier("Top-O-Line 扬声器");
DvdPlayer dvd = new DvdPlayer("Top-O-Line DVD播放器", amp);
Projector projector = new Projector("Top-O-Line 投影仪", dvd);
TheaterLights lights = new TheaterLights("客厅灯");
Screen screen = new Screen("投影仪银幕");
System.out.println("准备看电影...");
lights.dim(10);
screen.down();
projector.on();
projector.wideScreenMode();
amp.on();
amp.setSurroundSound();
amp.setVolume(5);
dvd.on();
dvd.play("电影名");
}
}
/**
* 内部已经封装好,可直接调用
* @author NNroc
* @date 2020/8/14 14:55
*/
public class TestWithFacade {
public static void main(String[] args) {
String name="电影名";
//看电影
HomeTheaterFacade.watchMovie(name);
}
}
/**
* 封装类
*
* @author NNroc
* @date 2020/8/14 14:56
*/
public class HomeTheaterFacade {
public static void watchMovie(String name) {
Amplifier amp = new Amplifier("Top-O-Line 扬声器");
DvdPlayer dvd = new DvdPlayer("Top-O-Line DVD播放器", amp);
Projector projector = new Projector("Top-O-Line 投影仪", dvd);
TheaterLights lights = new TheaterLights("客厅灯");
Screen screen = new Screen("投影仪银幕");
System.out.println("准备看电影...");
lights.dim(10);
screen.down();
projector.on();
projector.wideScreenMode();
amp.on();
amp.setSurroundSound();
amp.setVolume(5);
dvd.on();
dvd.play(name);
}
}