适配器模式
将一个类的接口转换成客户端期望的另一个接口,解决接口不兼容问题。
适配器模式由四部分组成:
客户端:即需要使用目标接口的类
目标接口
需要适配的类,也就是已经存在好的功能,但客户端通过目标接口没办法使用这个类
适配器,会实现目标接口,然后耦合需要适配的类,调用类的功能
分为类适配器和对象适配器
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee's specificRequest");
}
}
// 类适配器
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Target target = new Adapter();
target.request(); // Output: Adaptee's specificRequest
}
}
对象适配器: 通过组合,让适配器类持有现有类的实例,并实现目标接口**。对象适配器使用组合关系来实现接口的适配**,较为常用。
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee's specificRequest");
}
}
// 对象适配器
public class Adapter implements Target { //这里不再是和类适配器一样继承适配器,而是在类中装配适配器对象
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.request(); // Output: Adaptee's specificRequest
}
}
Java 日志中的 slf4j 其实就是使用了适配器模式来统一不同日志框架接口,使得我们不需要修改代码就可以替换不同的底层日志实现。
桥接模式
主要的作用是将抽象和实现解耦,使它们可以独立地变化。
我们熟知的JDBC 就使用了桥接模式,JDBC定义了抽象的规范,不同的数据库厂商遵循这些规范,但是它们各自又会有不同的实现。
在使用中,如果我们数据库是 mysql,则传入 com.mysql.jdbc.Driver 驱动实现类,如果要换成 oracle替换实现类为oracle.jdbc.driver.OracleDriver 即可,这就是典型的抽象与实现解耦。
代码实例
实现形状颜色解耦
实现化(Implementor):定义颜色接口
public interface Color { void applyColor(); }
具体实现化(Concrete Implementor):实现具体颜色
public class RedColor implements Color { @Override public void applyColor() { System.out.println("Applying red color"); //红色 } } public class BlueColor implements Color { //蓝色 @Override public void applyColor() { System.out.println("Applying blue color"); } }
抽象化(Abstraction):定义形状接口
public abstract class Shape { protected Color color; public Shape(Color color) { this.color = color; } public abstract void draw(); }
细化抽象化(Refined Abstraction):实现具体形状
public class Circle extends Shape { public Circle(Color color) { super(color); } @Override public void draw() { System.out.print("Drawing a circle with "); color.applyColor(); } } public class Rectangle extends Shape { public Rectangle(Color color) { super(color); } @Override public void draw() { System.out.print("Drawing a rectangle with "); color.applyColor(); } }
客户端代码
public class Client { public static void main(String[] args) { // 创建红色圆形 Shape redCircle = new Circle(new RedColor()); redCircle.draw(); // 输出: Drawing a circle with Applying red color // 创建蓝色矩形 Shape blueRectangle = new Rectangle(new BlueColor()); blueRectangle.draw(); // 输出: Drawing a rectangle with Applying blue color } }
组合模式
将对象组合成树状结构以表示“整体——部分”的层次关系
以部门为示例,展示组织内的部门和人员信息
// 抽象组织
interface OrganizationComponent {
void showDetails();
}
// 组织人员
class Employee implements OrganizationComponent {
private String name;
private String position;
public Employee(String name, String position) {
this.name = name;
this.position = position;
}
@Override
public void showDetails() {
System.out.println("Employee: " + name + ", Position: " + position);
}
}
// 组织部门
class Department implements OrganizationComponent {
private String name;
private List<OrganizationComponent> components = new ArrayList<>();
public Department(String name) {
this.name = name;
}
@Override
public void showDetails() {
System.out.println("Department: " + name);
for (OrganizationComponent component : components) {
component.showDetails();
}
}
public void add(OrganizationComponent component) { //往部门添加人员 或 部门
components.add(component);
}
public void remove(OrganizationComponent component) {
components.remove(component);
}
}
// 使用代码
public class Client {
public static void main(String[] args) {
// 创建人员
OrganizationComponent employee1 = new Employee("John Doe", "Developer");
OrganizationComponent employee2 = new Employee("Jane Smith", "Designer");
OrganizationComponent employee3 = new Employee("Emily Davis", "Manager");
// 创建部门
Department engineeringDepartment = new Department("Engineering Department");
Department designDepartment = new Department("Design Department");
Department headDepartment = new Department("Head Department");
// 添加人员到部门
engineeringDepartment.add(employee1);
designDepartment.add(employee2);
headDepartment.add(employee3);
// 添加子部门到上级部门
headDepartment.add(engineeringDepartment);
headDepartment.add(designDepartment);
// 显示整个组织结构的详情
headDepartment.showDetails();
// 输出
// Department: Head Department
// Employee: Emily Davis, Position: Manager
// Department: Engineering Department
// Employee: John Doe, Position: Developer
// Department: Design Department
// Employee: Jane Smith, Position: Designer
}
}
通过组合模式,提供统一的接口,简化对层次结构的处理,使得使用方代码更加简洁与灵活。
装饰器模式
主要作用是通过创建包装类来实现功能的增强,而不是修改原始类。
通过创建一个装饰器类,该类实现了与原始对象相同的接口,并持有一个原始对象的引用。通过将原始对象传递给装饰器
代理模式中,代理类附加的是跟原始类无关的功能,而在装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。
最典型的装饰器实现就是 Java 中的I/O类库,示例代码如下:
import java.io.*;
public class IOExample {
public static void main(String[] args) throws IOException {
File file = new File("test.txt");
FileInputStream fis = new FileInputStream(file); //读取文件流
BufferedInputStream bis = new BufferedInputStream(fis); //fis被BufferedlnputStream 装饰,提供了缓存的功能
DataInputStream dis = new DataInputStream(bis); //被 DatalnputStream 装饰,提供了按数据类型读取的功能
while (dis.available() > 0) {
System.out.println(dis.readLine());
}
dis.close();
}
}
文件流的代码比较复杂,这里就不展示了,我们简单看个多装饰器叠加的代码实现:
// 组件
interface Component {
void operation();
}
// 具体构件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
// 装饰器
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器 A
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("ConcreteDecoratorA added behavior");
}
}
// 具体装饰器 B
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedState();
}
private void addedState() {
System.out.println("ConcreteDecoratorB added state");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decoratorA = new ConcreteDecoratorA(component);
Component decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB.operation();
// Output:
// ConcreteComponent operation
// ConcreteDecoratorA added behavior
// ConcreteDecoratorB added state
}
}
外观模式
也叫门面模式,提供了一个简化的接口,用于访问复杂系统中的一组接口。将复杂系统的功能封装起来,让客户端可以更方便地使用系统功能而不需要了解其内部复杂结构。
举个例子就清晰了。例如 A系统有 a、b、c三个接口,B需要分别调用 a、b、c三个接口,这样比较麻烦,所以A将 a、b、c封装成一个接口给B调用,对B来说使用起来就方便了,这就是外观模式
class CPU { //CPU
public void start() {
System.out.println("CPU 启动");
}
public void shutdown() {
System.out.println("CPU 关闭");
}
}
class Memory { //内存
public void start() {
System.out.println("内存启动");
}
public void shutdown() {
System.out.println("内存关闭");
}
}
class HardDrive { //硬盘
public void start() {
System.out.println("硬盘启动");
}
public void shutdown() {
System.out.println("硬盘关闭");
}
}
class ComputerFacade { //外观类,封装了计算机系统的一组复杂接口,包括 CPU、内存和硬盘的启动和关闭
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public ComputerFacade() {
cpu = new CPU();
memory = new Memory();
hardDrive = new HardDrive();
}
public void start() { //启动计算机
System.out.println("计算机启动开始");
cpu.start();
memory.start();
hardDrive.start();
System.out.println("计算机启动完成");
}
public void shutdown() { //关闭计算机
System.out.println("计算机关闭开始");
cpu.shutdown();
memory.shutdown();
hardDrive.shutdown();
System.out.println("计算机关闭完成");
}
}
ComputerFacade computerFacade = new ComputerFacade();
computerFacade.start();
// 输出:
// 计算机启动开始
// CPU 启动
// 内存启动
// 硬盘启动
// 计算机启动完成
computerFacade.shutdown();
// 输出:
// 计算机关闭开始
// CPU 关闭
// 内存关闭
// 硬盘关闭
// 计算机关闭完成
享元模式
享元模式本质就是对象池,判断是否存在,若存在则取,不存在则添加。
Integer的缓存(-128~127)采用了享元模式。
具体原理可看 Java基础 29.什么是Java的Integer缓冲池?
示例:
假设我们要绘制棋盘上的棋子,棋子的种类有很多,但是每种棋子的形状和颜色是固定的。我们可以使用享元模式来共享相同种类的棋子对象,从而减少对象的创建数量。
// 棋子接口
interface ChessPiece {
void setColor(String color);
void display(int x, int y);
}
// 具体棋子类
class ConcreteChessPiece implements ChessPiece {
private String color;
public ConcreteChessPiece(String color) {
this.color = color;
}
@Override
public void setColor(String color) {
this.color = color;
}
@Override
public void display(int x, int y) {
System.out.println("Chess Piece color: " + color + ", position: (" + x + "," + y + ")");
}
}
// 享元工厂类
class ChessPieceFactory {
private Map<String, ChessPiece> chessPieces;
public ChessPieceFactory() {
this.chessPieces = new HashMap<>(); //用集合存chessPieces
}
public ChessPiece getChessPiece(String color) { //返回单例
ChessPiece chessPiece = chessPieces.get(color);
if (chessPiece == null) {
chessPiece = new ConcreteChessPiece(color);
chessPieces.put(color, chessPiece);
}
return chessPiece;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
ChessPieceFactory chessPieceFactory = new ChessPieceFactory();
ChessPiece blackPiece1 = chessPieceFactory.getChessPiece("black");
ChessPiece blackPiece2 = chessPieceFactory.getChessPiece("black");
ChessPiece whitePiece1 = chessPieceFactory.getChessPiece("white");
ChessPiece whitePiece2 = chessPieceFactory.getChessPiece("white");
blackPiece1.display(1, 2); // 输出:Chess Piece color: black, position: (1,2)
blackPiece2.display(3, 4); // 输出:Chess Piece color: black, position: (3,4)
whitePiece1.display(5, 6); // 输出:Chess Piece color: white, position: (5,6)
whitePiece2.display(7, 8); // 输出:Chess Piece color: white, position: (7,8)
}
}
代理模式
作用:在不改变原始类的情况下,为其提供一个代理,以控制对这个对象的访问
(代理模式和装饰器模式很像,其主要不同是,前者是控制原对象的访问,后者是为原对象增强已有类的功能)
核心是创建一个代理类,该类实现了与原始对象相同的接口,并持有一个原始对象的引用。在代理类的方法中,我们可以添加额外的功能,然后将请求转发给原始对象进行处理。
// 图片接口
interface Image {
void display();
}
// 具体图片类
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}
private void loadImageFromDisk() {
System.out.println("Loading " + filename + " from disk.");
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
// 代理图片类
class ProxyImage implements Image {
private RealImage realImage; //引入具体图片类
private String filename; //实现了对原始图片对象的控制,而不需要修改原始图片类的代码
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
beforeDisplay();
realImage.display();
}
private void beforeDisplay() {
System.out.println("Before displaying " + filename + ", do some pre-processing.");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Image image1 = new ProxyImage("image1.jpg");
Image image2 = new ProxyImage("image2.jpg");
image1.display(); // 输出:Before displaying image1.jpg, do some pre-processing.
// Loading image1.jpg from disk.
// Displaying image1.jpg
image2.display(); // 输出:Before displaying image2.jpg, do some pre-processing.
// Loading image2.jpg from disk.
// Displaying image2.jpg
}
}