设计模式(中)结构型篇

2、结构型模式

用于描述如何将类或对象按某种布局组成更大的结构,GOF(四人组)书中提供了代理、适配器、桥接、装饰、外观、享元、组合等7种结构型模式。

  1. 适配器模式 (Adapter Pattern): 将一个类的接口转换成另一个类所期望的接口。类似于电源适配器,使得不同插头能够连接到同一个插座。
  2. 桥接模式 (Bridge Pattern): 将抽象部分和实现部分分离,以便可以独立地变化。类似于不同颜色和形状的画笔可以组合使用。
  3. 组合模式 (Composite Pattern): 将对象组合成树状结构,以表示“整体-部分”的层次关系。就像一个文件系统中的目录和文件关系。
  4. 装饰器模式 (Decorator Pattern): 动态地给对象添加额外的职责,而不影响其结构。就像给一个蛋糕添加不同口味的糖霜。
  5. 外观模式 (Facade Pattern): 提供一个简化的接口,用于访问复杂系统中的一组接口。类似于操作系统提供的用户界面。
  6. 享元模式 (Flyweight Pattern): 共享对象来减少内存使用,特别是当对象有大量重复时。类似于共享池中的游泳池蓝色瓷砖。
  7. 代理模式 (Proxy Pattern): 控制对其他对象的访问,可以提供额外的处理逻辑。类似于助理代表某人参加会议。

2.1、适配器模式

适配器模式(Adapter Pattern)是一种结构性设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。它允许不兼容的接口协同工作,通过创建一个适配器来完成接口之间的转换。适配器模式在软件开发中常用于旧代码和新代码之间的兼容性问题,或者在集成不同组件或第三方库时使用。

适配器模式是一种结构型设计模式,它允许不兼容接口的类协同工作。适配器作为两个不兼容接口之间的桥梁,允许它们进行交互。这种模式允许你将一个类的接口转换成客户端所期望的另一个接口。

示例:电源适配器

想象你有一台笔记本电脑(客户端),但你需要连接它到墙上的电源插座。问题在于电源插座的接口与笔记本电脑的充电插头不兼容。这时你需要一个适配器,它可以将电源插座的接口转换成笔记本电脑充电插头所期望的接口。

// 目标接口,笔记本电脑的充电插头
interface LaptopCharger {
    void charge();
}

// 需要适配的类,电源插座
class PowerSocket {
    void supplyPower() {
        System.out.println("供电中...");
    }
}

// 适配器类,将电源插座适配成笔记本电脑充电插头
class SocketAdapter implements LaptopCharger {
    private PowerSocket socket;

    public SocketAdapter(PowerSocket socket) {
        this.socket = socket;
    }

    @Override
    public void charge() {
        socket.supplyPower();
        System.out.println("正在充电");
    }
}

public class AdapterPatternExample {
    public static void main(String[] args) {
        PowerSocket powerSocket = new PowerSocket();
        LaptopCharger charger = new SocketAdapter(powerSocket);

        charger.charge(); // 连接适配器进行充电
    }
}

优化垃圾代码

假设有一个项目中,有两个不同的日志记录类,分别是OldLoggerNewLogger,它们的接口不同,但我们想要在项目中统一使用Logger接口来进行日志记录。

// 垃圾代码:旧的日志记录类
class OldLogger {
    void logMessage(String message) {
        System.out.println("旧日志:" + message);
    }
}

// 垃圾代码:新的日志记录类
class NewLogger {
    void log(String logText) {
        System.out.println("新日志:" + logText);
    }
}

// 垃圾代码:项目中要统一使用的日志记录接口
interface Logger {
    void log(String message);
}

这时,我们可以使用适配器模式来优化这段垃圾代码,使得OldLoggerNewLogger都能适配成统一的Logger接口。

// 优化后的代码:适配器模式
class OldLoggerAdapter implements Logger {
    private OldLogger oldLogger;

    public OldLoggerAdapter(OldLogger oldLogger) {
        this.oldLogger = oldLogger;
    }

    @Override
    public void log(String message) {
        oldLogger.logMessage(message);
    }
}

class NewLoggerAdapter implements Logger {
    private NewLogger newLogger;

    public NewLoggerAdapter(NewLogger newLogger) {
        this.newLogger = newLogger;
    }

    @Override
    public void log(String message) {
        newLogger.log(message);
    }
}

public class LoggerAdapterPatternExample {
    public static void main(String[] args) {
        Logger oldLogger = new OldLoggerAdapter(new OldLogger());
        Logger newLogger = new NewLoggerAdapter(new NewLogger());

        oldLogger.log("This is an old log.");
        newLogger.log("This is a new log.");
    }
}

在上面的代码中,通过适配器模式,我们将OldLoggerNewLogger适配成了统一的Logger接口,从而实现了在项目中的统一使用。这就是适配器模式的作用。

总结

适配器模式可以是类适配器对象适配器接口适配器 上面的例子是接口适配器

无非就是写了一个Adapter适配器类,然后把对应的需要适配的接口定义为 适配器类的成员变量,然后这个成员变量的值的来源就是区别是什么适配器

核心是用在同一接口实现里用目标对象去调用目标自己的方法实现。

  • 可以用构造器注入实现对象适配器
  • 可以用继承对应的实现类来实现类适配器
  • 可以在适配器中自己new,实现接口适配器

无非就是把原本不适配的对象用构造器注入进适配器类中,然后这个适配器类实现被适配的接口,重写方法,方法中用之前不适配的对象去调用方法即可

2.2、桥接模式

当涉及到将抽象部分与实现部分分离,使它们可以独立地变化时,桥接模式就发挥作用。它允许抽象和实现之间的耦合度降低,从而更容易地扩展和修改系统的各个部分。桥接模式通过创建抽象部分和实现部分的分离层次结构,使它们可以独立地变化,但仍然能够协同工作。

具体实现

以下是一个简单的Java代码示例,展示了桥接模式的实现。我们以 "形状"(Shape)为抽象部分,将其分为 "圆形"(Circle)和 "正方形"(Square),而实现部分则是 "颜色"(Color),分为 "红色"(Red)和 "蓝色"(Blue)。通过桥接模式,我们可以独立地扩展形状和颜色,并且它们之间的变化互不影响。

// 实现部分 - 颜色接口
interface Color {
    String fill();
}

// 具体实现部分 - 红色
class Red implements Color {
    public String fill() {
        return "红色";
    }
}

// 具体实现部分 - 蓝色
class Blue implements Color {
    public String fill() {
        return "蓝色";
    }
}

// 抽象部分 - 形状接口
abstract class Shape {
    protected Color color;

    public Shape(Color color) {
        this.color = color;
    }

    abstract String draw();
}

// 具体抽象部分 - 圆形
class Circle extends Shape {
    public Circle(Color color) {
        super(color);
    }

    public String draw() {
        return color.fill() + "圆形";
    }
}

// 具体抽象部分 - 正方形
class Square extends Shape {
    public Square(Color color) {
        super(color);
    }

    public String draw() {
        return color.fill() + "正方形";
    }
}

public class BridgePatternDemo {
    public static void main(String[] args) {
        Color redColor = new Red();
        Color blueColor = new Blue();

        Shape redCircle = new Circle(redColor);
        Shape blueSquare = new Square(blueColor);

        System.out.println(redCircle.draw());  // 输出:红色圆形
        System.out.println(blueSquare.draw()); // 输出:蓝色正方形
    }
}

在上述代码中,我们使用了桥接模式将 "形状" 和 "颜色" 分离。形状通过持有颜色的引用来实现颜色的变化,而形状本身与颜色实现解耦。这使得我们可以轻松地添加新的形状和颜色,而不必修改已有的代码。

总结:桥接模式通过将抽象部分与实现部分分离,实现了它们的独立变化,从而提高了系统的可扩展性和灵活性。这种模式特别适用于那些可能有多种维度变化的情况,例如上述示例中的形状和颜色。

总结

桥接模式就是将俩个部分的产品,一个产品系列通过另外一个产品系列的抽象类来构造器注入到抽象类的属性中,并提供一个抽象方法待实现,实现的抽象类中的方法内容实际上是注入的对象调用自己的方法

然后这俩个产品系列就关联在一起了,这就是桥接

(和适配器的区别是,这里是俩个不同的产品系列,而适配器是适配一个产品系列的不一样的特征)

再举例:多个视频文件和多个操作系统

//视频文件
public interface VideoFile {
    void decode(String fileName);
}

//avi文件
public class AVIFile implements VideoFile {
    public void decode(String fileName) {
        System.out.println("avi视频文件:"+ fileName);
    }
}

//rmvb文件
public class REVBBFile implements VideoFile {

    public void decode(String fileName) {
        System.out.println("rmvb文件:" + fileName);
    }
}

//操作系统版本
public abstract class OperatingSystemVersion {

    protected VideoFile videoFile;

    public OperatingSystemVersion(VideoFile videoFile) {
        this.videoFile = videoFile;
    }

    public abstract void play(String fileName);
}

//Windows版本
public class Windows extends OperatingSystem {

    public Windows(VideoFile videoFile) {
        super(videoFile);
    }

    public void play(String fileName) {
        videoFile.decode(fileName);
    }
}

//mac版本
public class Mac extends OperatingSystemVersion {

    public Mac(VideoFile videoFile) {
        super(videoFile);
    }

    public void play(String fileName) {
		videoFile.decode(fileName);
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        OperatingSystem os = new Windows(new AVIFile());
        os.play("战狼3");
    }
}

使用场景:

  • 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
  • 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
  • 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。

好处:

  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。

    如:如果现在还有一种视频文件类型wmv,我们只需要再定义一个类实现VideoFile接口即可,其他类不需要发生变化。

  • 实现细节对客户透明

2.3、组合模式

组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树状结构来表示“整体-部分”关系,使得客户端对单个对象和组合对象的使用具有一致性。这种模式可以用于创建层次结构,使得客户端可以一致地处理单个对象和组合对象。

具体实现

以下是使用Java代码实现组合模式的示例,以文件系统为生活例子。

import java.util.ArrayList;
import java.util.List;

// 组件接口
interface FileSystemComponent {
    void displayInfo();
}

// 叶子节点
class File implements FileSystemComponent {
    private String name;

    public File(String name) {
        this.name = name;
    }

    @Override
    public void displayInfo() {
        System.out.println("File: " + name);
    }
}

// 组合节点
class Directory implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> components;

    public Directory(String name) {
        this.name = name;
        components = new ArrayList<>();
    }

    public void addComponent(FileSystemComponent component) {
        components.add(component);
    }

    @Override
    public void displayInfo() {
        System.out.println("Directory: " + name);
        for (FileSystemComponent component : components) {
            component.displayInfo();
        }
    }
}

public class CompositePatternExample {
    public static void main(String[] args) {
        // 创建文件和目录
        File file1 = new File("file1.txt");
        File file2 = new File("file2.txt");
        File file3 = new File("file3.txt");

        Directory subDirectory = new Directory("Subdirectory");
        subDirectory.addComponent(file2);
        subDirectory.addComponent(file3);

        Directory rootDirectory = new Directory("Root");
        rootDirectory.addComponent(file1);
        rootDirectory.addComponent(subDirectory);

        // 显示信息
        rootDirectory.displayInfo();
    }
}

在这个示例中,我们创建了一个文件系统的模拟结构。FileSystemComponent 接口是组合中的统一接口,File 类表示叶子节点,Directory 类表示组合节点。通过组合叶子节点和组合节点,我们形成了一个树状结构。在 main 方法中,我们创建了文件和目录,并将它们组合成了一个文件系统的结构,最后调用 displayInfo 方法展示整个文件系统的结构。

组合模式的优点在于它能够使客户端代码对于单个对象和组合对象的处理保持一致性,从而简化了代码。在这个示例中,客户端可以像对待单个文件一样对待整个目录结构,而不需要考虑递归遍历等复杂操作。这种模式特别适用于处理树状结构的场景,如图形界面中的UI元素、组织架构等

总结

组合模式就是基于一个抽象的接口上,实现一个叶子节点和一个组合节点,组合节点中包含叶子节,叶子节点以集合的元素形式作为组合节点的属性

主要的内容是组合节点中叶子节点集合属性

// 组合节点
class Directory implements FileSystemComponent {
    private String name;
    // 组合模式的主要核心,套娃
    private List<FileSystemComponent> components;

    public Directory(String name) {
        this.name = name;
        components = new ArrayList<>();
    }

    public void addComponent(FileSystemComponent component) {
        components.add(component);
    }

    @Override
    public void displayInfo() {
        System.out.println("Directory: " + name);
        for (FileSystemComponent component : components) {
            component.displayInfo();
        }
    }
}

2.4、装饰器模式

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地向对象添加行为,而不影响其类。装饰模式通过创建一个装饰类,该类包含一个指向被装饰对象的引用,从而在不改变原始对象的情况下,为其添加新的功能。

具体实现

我们将以消息发送器为例,通过装饰者模式来实现对消息进行加密和压缩的功能。

  1. 定义被装饰对象接口:Message
// 被装饰对象接口
interface Message {
    String getContent();
}
  1. 创建具体的消息类:TextMessage
// 具体的消息类(被装饰对象)
class TextMessage implements Message {
    private String content;

    public TextMessage(String content) {
        this.content = content;
    }

    @Override
    public String getContent() {
        return content;
    }
}
  1. 定义装饰者抽象类:MessageDecorator
// 装饰者抽象类
abstract class MessageDecorator implements Message {
    protected Message decoratedMessage; // 持有被装饰对象的引用

    public MessageDecorator(Message decoratedMessage) {
        this.decoratedMessage = decoratedMessage;
    }

    @Override
    public String getContent() {
        return decoratedMessage.getContent();
    }
}
  1. 创建具体的装饰者类:EncryptionDecoratorCompressionDecorator
// 加密装饰者
class EncryptionDecorator extends MessageDecorator {
    public EncryptionDecorator(Message decoratedMessage) {
        super(decoratedMessage);
    }

    @Override
    public String getContent() {
        String encryptedContent = super.getContent();
        // 在这里添加加密逻辑,这里用简化的假设
        return "Encrypted: " + encryptedContent;
    }
}

// 压缩装饰者
class CompressionDecorator extends MessageDecorator {
    public CompressionDecorator(Message decoratedMessage) {
        super(decoratedMessage);
    }

    @Override
    public String getContent() {
        String compressedContent = super.getContent();
        // 在这里添加压缩逻辑,这里用简化的假设
        return "Compressed: " + compressedContent;
    }
}
  1. 测试装饰模式。
public class DecoratorPatternExample {
    public static void main(String[] args) {
        // 创建基础消息
        Message textMessage = new TextMessage("Hello, world!");

        // 添加加密功能
        Message encryptedMessage = new EncryptionDecorator(textMessage);
        System.out.println("Encrypted Message: " + encryptedMessage.getContent());

        // 添加加密和压缩功能
        Message compressedEncryptedMessage = new CompressionDecorator(encryptedMessage);
        System.out.println("Compressed and Encrypted Message: " + compressedEncryptedMessage.getContent());
    }
}

在这个例子中,我们定义了一个消息接口(Message),并创建了一个具体的消息类(TextMessage)。然后,我们定义了装饰者抽象类(MessageDecorator)来扩展消息功能。

接着,我们实现了具体的装饰者类,分别是加密装饰者(EncryptionDecorator)和压缩装饰者(CompressionDecorator)。这些装饰者类都扩展了MessageDecorator,并在getContent方法中添加了加密和压缩的逻辑。

main方法中,我们测试了装饰模式的效果。我们首先创建一个基础的消息,然后分别使用加密和压缩装饰者来装饰这个消息。每次装饰都会增加新的功能,而不需要改变原始消息类的代码。

总结来说,装饰模式可以动态地为对象添加功能,使得功能的扩展更加灵活,同时避免了继承的问题。这在需要根据需求不断变化的场景中特别有用,如在上述例子中根据不同的需求来选择加密、压缩或二者都要的功能。

总结

适用场景:一个主体需要添加其他可选的操作的时。比如一份蛋炒饭需要加鸡蛋或者加火腿或者都加的时候

主要步骤:

  1. 定义蛋炒饭的接口,定义蛋炒饭接口的实现类
  2. 用抽象类实现蛋炒饭的接口,并且把蛋炒饭接口作为成员变量添加,并且用构造器注入
  3. 定义抽象类的具体实现类,构造器引用父类的构造器

2.5、外观模式

外观模式(Facade Pattern)是一种结构型设计模式,旨在为复杂的子系统提供一个简化的接口,以便客户端能够更容易地与该子系统交互。外观模式通过创建一个高层接口,隐藏了底层子系统的复杂性,使得客户端只需与外观对象进行交互,而不需要直接与子系统的各个组件打交道。

具体实现

我们以一个家庭影院系统为例,展示如何使用外观模式。假设家庭影院系统包括投影仪、音响和DVD播放器,而客户端希望通过一个简单的接口来控制整个家庭影院系统。

首先,我们定义子系统中的各个组件类:

  1. DVDPlayer.java - DVD播放器类
public class DVDPlayer {
    public void on() {
        System.out.println("DVD播放器已开启");
    }

    public void play(String movie) {
        System.out.println("正在播放电影:" + movie);
    }

    public void off() {
        System.out.println("DVD播放器已关闭");
    }
}
  1. Projector.java - 投影仪类
public class Projector {
    public void on() {
        System.out.println("投影仪已开启");
    }

    public void setInput(DVDPlayer dvdPlayer) {
        System.out.println("已将投影仪的输入设为DVD播放器");
    }

    public void off() {
        System.out.println("投影仪已关闭");
    }
}
  1. SoundSystem.java - 音响类
public class SoundSystem {
    public void on() {
        System.out.println("音响已开启");
    }

    public void setVolume(int volume) {
        System.out.println("音量已设置为:" + volume);
    }

    public void off() {
        System.out.println("音响已关闭");
    }
}

接下来,我们创建一个外观类来封装子系统的复杂性,并提供一个简单的接口供客户端使用:

  1. HomeTheaterFacade.java - 家庭影院外观类
public class HomeTheaterFacade {
    private DVDPlayer dvdPlayer;
    private Projector projector;
    private SoundSystem soundSystem;

    public HomeTheaterFacade() {
        dvdPlayer = new DVDPlayer();
        projector = new Projector();
        soundSystem = new SoundSystem();
    }

    public void watchMovie(String movie) {
        System.out.println("准备观影...");
        projector.on();
        projector.setInput(dvdPlayer);
        soundSystem.on();
        soundSystem.setVolume(10);
        dvdPlayer.on();
        dvdPlayer.play(movie);
    }

    public void endMovie() {
        System.out.println("结束观影...");
        dvdPlayer.off();
        soundSystem.off();
        projector.off();
    }
}

最后,客户端可以通过使用外观类来控制家庭影院系统,而无需了解每个子系统组件的详细操作:

  1. Client.java - 客户端类
public class Client {
    public static void main(String[] args) {
        HomeTheaterFacade homeTheater = new HomeTheaterFacade();

        homeTheater.watchMovie("Inception");
        System.out.println("------");
        homeTheater.endMovie();
    }
}

在这个例子中,外观模式允许客户端通过简单的接口 HomeTheaterFacade 来控制整个家庭影院系统,而不必直接与各个子系统组件交互。这隐藏了子系统的复杂性,提供了更方便的方式来管理和操作整个家庭影院

总结

就是正对多个对象的方法,统一提供一个对外的对象方法来集成调用。

看下面这个类即可明白

public class HomeTheaterFacade {
    private DVDPlayer dvdPlayer;
    private Projector projector;
    private SoundSystem soundSystem;

    public HomeTheaterFacade() {
        dvdPlayer = new DVDPlayer();
        projector = new Projector();
        soundSystem = new SoundSystem();
    }

    public void watchMovie(String movie) {
        System.out.println("准备观影...");
        projector.on();
        projector.setInput(dvdPlayer);
        soundSystem.on();
        soundSystem.setVolume(10);
        dvdPlayer.on();
        dvdPlayer.play(movie);
    }

    public void endMovie() {
        System.out.println("结束观影...");
        dvdPlayer.off();
        soundSystem.off();
        projector.off();
    }
}

2.6、享元模式

享元模式是一种结构型设计模式,旨在通过共享对象以减少内存使用和提高性能。该模式适用于存在大量重复对象的情况,通过共享这些对象的相同部分来减少内存占用。这种共享在对象之间创建了一种一对多的关系,其中一个对象(享元对象)保存了共享状态,而其他对象(非共享状态)可以引用这些共享状态。

具体实现

生活例子:享元模式在棋盘游戏中的应用

假设我们正在开发一个棋盘游戏,例如国际象棋。在这个游戏中,棋盘上的每个格子都有一些共同的属性,比如颜色。这些属性是所有格子都一样的,因此我们可以使用享元模式来共享这些属性,从而减少内存消耗。

代码实现与详细中文注释:

import java.util.HashMap;
import java.util.Map;

// 抽象享元类
abstract class ChessPiece {
    protected String color; // 共享的属性

    public ChessPiece(String color) {
        this.color = color;
    }

    public abstract void display(int x, int y);
}

// 具体享元类
class ConcreteChessPiece extends ChessPiece {
    public ConcreteChessPiece(String color) {
        super(color);
    }

    public void display(int x, int y) {
        System.out.println("在坐标(" + x + "," + y + ")处放置一个 " + color + " 的棋子");
    }
}

// 享元工厂类(单例)
class ChessPieceFactory {
    private Map<String, ChessPiece> chessPieces = new HashMap<>();// 容器式单例
    private static ChessPieceFactory instance = new ChessPieceFactory(); // 单例实例

    private ChessPieceFactory() {
        // 私有构造函数,确保只能在内部实例化
    }

    public static ChessPieceFactory getInstance() {
        return instance;
    }

    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 FlyweightPatternExample {
    public static void main(String[] args) {
        ChessPieceFactory chessPieceFactory = ChessPieceFactory.getInstance();

        ChessPiece blackPiece1 = chessPieceFactory.getChessPiece("黑色");
        blackPiece1.display(1, 2);

        ChessPiece blackPiece2 = chessPieceFactory.getChessPiece("黑色");
        blackPiece2.display(3, 4);

        ChessPiece whitePiece = chessPieceFactory.getChessPiece("白色");
        whitePiece.display(2, 5);
    }
}

模式解释:

在上述示例中,我们创建了一个棋盘游戏的场景,使用享元模式来管理棋子的共享状态。抽象享元类 ChessPiece 定义了共享属性 color 并声明了一个 display 方法,具体享元类 ConcreteChessPiece 实现了具体的棋子对象。享元工厂类 ChessPieceFactory 负责管理已创建的棋子,如果相同颜色的棋子已经存在,则直接返回共享对象,否则创建一个新的棋子对象。

在示例中,我们可以看到两个黑色棋子共享了相同的颜色属性,而白色棋子则是另一个对象。这样,通过共享颜色属性,我们在内存中只创建了一个黑色棋子对象,节省了内存空间。

总结:

享元模式适用于需要大量相似对象的情况,通过共享共同的状态来减少内存占用。这有助于提高性能和效率。在实际应用中,可以用于图形编辑器、文本编辑器、棋盘游戏等场景。通过共享不变的状态,享元模式可以有效地优化系统资源的使用。

总结

享元模式就是相似对象共享,节约资源

实现不过就是一个单例工厂+一个容器map存储共享的对象。

2.7、代理模式

代理模式是一种结构型设计模式,它允许一个对象(代理)作为另一个对象的接口,以控制对该对象的访问。代理模式可以用于多种目的,例如:延迟加载,访问控制,封装,缓存等。在代理模式中,代理类持有对真实对象的引用,客户端通过代理来间接访问真实对象。

具体实现

下面以一个简单的生活例子来说明代理模式:假设你要购买一张演唱会的门票。门票代售点就是一个代理,他们会帮你购买门票,但实际上演唱会的主办方才是真正提供门票的地方。在这里,门票代售点就是代理,演唱会主办方是真实对象。

以下是使用Java实现代理模式的例子,我会提供详细的代码和注释:

// 1. 创建一个共同的接口,代表真实对象和代理对象的行为
interface TicketSeller {
    void sellTicket();
}

// 2. 创建真实对象类,实现共同的接口
class ConcertOrganizer implements TicketSeller {
    @Override
    public void sellTicket() {
        System.out.println("演唱会主办方:欢迎购买门票!");
    }
}

// 3. 创建代理类,同样实现共同的接口
class TicketProxy implements TicketSeller {
    private ConcertOrganizer organizer;

    public TicketProxy() {
        organizer = new ConcertOrganizer();
    }

    @Override
    public void sellTicket() {
        System.out.println("门票代售点:您正在购买门票...");
        organizer.sellTicket(); // 通过代理间接调用真实对象的方法
        System.out.println("门票代售点:谢谢购买,祝您享受演唱会!");
    }
}

// 4. 客户端代码
public class ProxyPatternExample {
    public static void main(String[] args) {
        // 使用代理来购买门票
        TicketSeller ticketProxy = new TicketProxy();
        ticketProxy.sellTicket();
    }
}

在上面的代码中,我们创建了一个共同的接口 TicketSeller,它定义了真实对象和代理对象的行为。然后,我们实现了真实对象 ConcertOrganizer 和代理类 TicketProxy,它们都实现了 TicketSeller 接口。代理类中持有了真实对象的引用,并在 sellTicket() 方法中通过代理间接调用了真实对象的方法。

在客户端代码中,我们使用代理来购买门票。当我们调用代理的 sellTicket() 方法时,代理会在购买门票前后添加一些额外的操作,例如显示购买信息和感谢信息。

通过这个例子,我们可以理解代理模式的含义:代理模式允许我们使用代理对象来控制对真实对象的访问,从而实现了额外的功能或控制。在实际应用中,代理模式可以用于许多场景,例如远程代理、虚拟代理、缓存代理等,以实现更复杂的功能。

总结

代理模式就是基于一个接口,这个接口有了一个实现类,我再实现这个接口为代理类,代理类中包含前面实现的实现类,然后用代理类中的方法去用实现类的对象去调用自己的方法

核心代码,代理类中,用实现类的对象调用这个对象实现了的方法,直接new,隐藏对象,不用构造器传入

// 3. 创建代理类,同样实现共同的接口
class TicketProxy implements TicketSeller {
    private ConcertOrganizer organizer;

    public TicketProxy() {
        organizer = new ConcertOrganizer();
    }

    @Override
    public void sellTicket() {
        System.out.println("门票代售点:您正在购买门票...");
        organizer.sellTicket(); // 通过代理间接调用真实对象的方法
        System.out.println("门票代售点:谢谢购买,祝您享受演唱会!");
    }
}


设计模式(中)结构型篇
http://101.126.22.188:9090//2023/10/31/1698765102655
作者
不是王总
发布于
2023年10月31日
更新于
2023年11月06日
许可协议