设计模式-结构型模式(详解)

发布于:2025-05-26 ⋅ 阅读:(46) ⋅ 点赞:(0)

适配器模式

将一个类的接口转换成客户端期望的另一个接口,解决接口不兼容问题。

适配器模式由四部分组成:

  1. 客户端:即需要使用目标接口的类

  2. 目标接口

  3. 需要适配的类,也就是已经存在好的功能,但客户端通过目标接口没办法使用这个类

  4. 适配器,会实现目标接口,然后耦合需要适配的类,调用类的功能

分为类适配器和对象适配器

// 目标接口
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 即可,这就是典型的抽象与实现解耦。

代码实例

实现形状颜色解耦

  1. 实现化(Implementor):定义颜色接口

    public interface Color {
        void applyColor();
    }
    
  2. 具体实现化(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");
        }
    }
    
  3. 抽象化(Abstraction):定义形状接口

    public abstract class Shape {
        protected Color color;
        public Shape(Color color) {
            this.color = color;
        }
        public abstract void draw();
    }
    
  4. 细化抽象化(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();
        }
    }
    
  5. 客户端代码

    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
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到