Java 设计模式(结构型)

文章目录

  • 代理模式
  • 门面模式
  • 外观模式
  • 桥接模式
  • 适配器模式
  • 享元模式
  • 装饰器模式
  • 组合模式

代理模式

代理模式是一种结构型设计模式,允许在访问对象时提供一种代理以控制对该对象的访问。代理模式通常在客户端和实际对象之间引入了一个代理对象,客户端通过代理对象间接访问实际对象,从而可以在访问过程中添加额外的功能,例如权限验证、缓存、延迟加载等。

  • 结构

    1. 抽象主题(Subject):定义了代理对象和真实对象之间的共同接口,客户端通过该接口访问真实对象。
    2. 真实主题(Real Subject):实现了抽象主题接口,定义了真实对象的具体功能。
    3. 代理(Proxy):实现了抽象主题接口,并保存了一个引用指向真实主题对象,客户端通过代理对象间接访问真实对象。代理对象可以在客户端访问真实对象前后执行一些额外的操作。
  • 场景

    1. 远程代理:当客户端需要访问远程对象时,可以使用远程代理来隐藏网络通信的细节。
    2. 虚拟代理:当创建一个对象的开销很大时,可以使用虚拟代理来延迟对象的实例化。
    3. 安全代理:当需要对客户端访问真实对象的权限进行控制时,可以使用安全代理来进行权限验证。
    4. 缓存代理:当需要缓存真实对象的结果以提高性能时,可以使用缓存代理来实现结果的缓存。
  • 优点

    1. 实现了客户端和真实对象的解耦:代理模式将客户端和真实对象解耦,客户端无需直接访问真实对象,从而降低了客户端和真实对象之间的依赖关系。
    2. 提高了系统的灵活性:通过引入代理对象,可以在访问真实对象前后添加额外的功能,从而提高了系统的灵活性。
    3. 增强了安全性:代理对象可以在客户端访问真实对象前进行权限验证,从而增强了系统的安全性。
  • 缺点

    1. 增加了系统复杂度:引入代理对象会增加系统的复杂度,因为需要额外的类来实现代理功能。
    2. 可能降低性能:代理模式可能会增加请求的处理时间,因为代理对象需要在访问真实对象前后执行额外的操作,例如权限验证、缓存等。
  • 示例

// 主题接口
interface Subject {
    void request();
}

// 真实主题类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实主题类处理请求");
    }
}

// 代理类
class Proxy implements Subject {
    private RealSubject realSubject;
    
    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }
    
    @Override
    public void request() {
        // 在访问真实主题前执行一些额外的操作
        System.out.println("代理类处理请求之前的操作");
        
        // 调用真实主题对象的方法
        realSubject.request();
        
        // 在访问真实主题后执行一些额外的操作
        System.out.println("代理类处理请求之后的操作");
    }
}

public class ProxyPatternExample {
    public static void main(String[] args) {
        // 创建真实主题对象
        RealSubject realSubject = new RealSubject();
        
        // 创建代理对象,并将真实主题对象传递给代理对象
        Proxy proxy = new Proxy(realSubject);
        
        // 通过代理对象调用真实主题的方法
        proxy.request();
    }
}
  • 输出结果
代理类处理请求之前的操作
真实主题类处理请求
代理类处理请求之后的操作

门面模式

门面模式是一种结构型设计模式,旨在为复杂子系统提供一个简单的接口,以便与外部系统进行交互。门面模式通过将复杂系统的多个接口封装在一个单一的接口中,简化了客户端与系统之间的交互。

  • 结构

    1. 门面(Facade):提供了一个简单的接口,用于与外部系统进行交互。门面将复杂系统的多个接口封装在一个单一的接口中。
    2. 子系统(Subsystem):包含了系统中的多个组件和接口,负责实现系统的具体功能。
  • 场景

    1. 为外部系统提供统一接口:当需要为复杂系统提供一个简单的接口,以便与外部系统进行交互时,可以使用门面模式。
    2. 简化复杂系统的使用:当复杂系统有多个接口和组件,并且需要简化客户端的使用时,可以使用门面模式。
    3. 隐藏系统实现细节:当需要隐藏系统的实现细节,并提供一个简单的接口给客户端时,可以使用门面模式。
  • 优点

    1. 简化接口:门面模式提供了一个简单的接口,隐藏了复杂系统的实现细节,使得客户端更容易使用系统。
    2. 降低耦合度:门面模式将客户端与系统之间的依赖关系解耦,使得系统的变化不会影响到客户端。
    3. 提高可维护性:通过将复杂系统封装在一个门面接口中,可以更容易地理解和维护系统。
  • 缺点

    1. 可能导致门面对象臃肿:如果系统过于复杂,门面对象可能会变得臃肿,导致违反单一责任原则。
    2. 不符合开闭原则:当系统的功能发生变化时,可能需要修改门面对象的实现,这可能会违反开闭原则。
  • 示例

// 子系统 - 音频播放器
class AudioPlayer {
    public void play() {
        System.out.println("播放音频");
    }
    
    public void pause() {
        System.out.println("暂停音频");
    }
    
    public void stop() {
        System.out.println("停止音频");
    }
}

// 子系统 - 视频播放器
class VideoPlayer {
    public void play() {
        System.out.println("播放视频");
    }
    
    public void pause() {
        System.out.println("暂停视频");
    }
    
    public void stop() {
        System.out.println("停止视频");
    }
}

// 门面类 - 多媒体播放器门面
class MediaPlayerFacade {
    private AudioPlayer audioPlayer;
    private VideoPlayer videoPlayer;
    
    public MediaPlayerFacade() {
        audioPlayer = new AudioPlayer();
        videoPlayer = new VideoPlayer();
    }
    
    // 播放音频和视频
    public void play() {
        audioPlayer.play();
        videoPlayer.play();
    }
    
    // 暂停音频和视频
    public void pause() {
        audioPlayer.pause();
        videoPlayer.pause();
    }
    
    // 停止音频和视频
    public void stop() {
        audioPlayer.stop();
        videoPlayer.stop();
    }
}

public class FacadePatternExample {
    public static void main(String[] args) {
        // 使用门面模式简化客户端与复杂系统之间的交互
        MediaPlayerFacade mediaPlayer = new MediaPlayerFacade();
        mediaPlayer.play();
        mediaPlayer.pause();
        mediaPlayer.stop();
    }
}

外观模式

外观模式是一种结构型设计模式,旨在提供一个简单的接口,隐藏复杂的系统内部结构,使得客户端可以更容易地使用系统。外观模式将系统的多个子系统封装在一个统一的接口中,为客户端提供了一个更高层次的接口,从而简化了客户端与系统之间的交互。

  • 结构

    1. 外观(Facade):提供了一个统一的接口,用于访问子系统中的一组接口。
    2. 子系统(Subsystem):包含一组相关的类或接口,实现了系统的各种功能。
  • 场景

    1. 简化复杂系统:当系统中存在多个复杂的子系统,并且需要为客户端提供一个简单的接口来访问这些子系统时,可以使用外观模式。
    2. 隐藏实现细节:当希望隐藏系统的实现细节,使得客户端不需要了解系统的内部结构时,可以使用外观模式。
    3. 提供统一接口:当希望为客户端提供一个统一的接口,封装系统的多个子系统,并隐藏其复杂性时,可以使用外观模式。
  • 优点

    1. 简化接口:外观模式通过提供一个简单的接口,隐藏了系统的复杂性,使得客户端更容易地使用系统。
    2. 解耦:外观模式将客户端与子系统之间的依赖关系解耦,客户端只需要与外观对象交互,而不需要直接与子系统交互。
    3. 提高了灵活性:由于外观对象封装了子系统的实现细节,因此可以灵活地调整子系统的实现,而不影响客户端代码。
  • 缺点

    1. 违反单一职责原则:外观对象负责封装多个子系统的功能,可能会导致外观对象变得庞大和复杂,违反了单一职责原则。
    2. 不符合开闭原则:如果需要新增或修改系统的功能,可能需要修改外观对象,这违反了开闭原则。
  • 示例

// 子系统A
class SubsystemA {
    public void operationA() {
        System.out.println("子系统A的操作A");
    }
}

// 子系统B
class SubsystemB {
    public void operationB() {
        System.out.println("子系统B的操作B");
    }
}

// 子系统C
class SubsystemC {
    public void operationC() {
        System.out.println("子系统C的操作C");
    }
}

// 外观类
class Facade {
    private SubsystemA subsystemA;
    private SubsystemB subsystemB;
    private SubsystemC subsystemC;

    public Facade() {
        this.subsystemA = new SubsystemA();
        this.subsystemB = new SubsystemB();
        this.subsystemC = new SubsystemC();
    }

    // 提供一个统一的接口,供客户端调用
    public void operation() {
        System.out.println("外观类调用子系统A、B、C的操作:");
        subsystemA.operationA();
        subsystemB.operationB();
        subsystemC.operationC();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.operation();
    }
}
  • 输出结果
外观类调用子系统A、B、C的操作:
子系统A的操作A
子系统B的操作B
子系统C的操作C

桥接模式

桥接模式(Bridge Pattern)是一种结构型设计模式,用于将抽象部分与其实现部分分离,使它们可以独立地变化。桥接模式通过将抽象和实现分离,使得它们可以独立地变化,从而提高了系统的灵活性和可扩展性。

  • 结构

    1. 抽象部分(Abstraction):定义了抽象类或接口,并包含一个实现部分对象的引用。
    2. 扩展抽象部分(Refined Abstraction):对抽象部分进行扩展,通常包含一些额外的方法或属性。
    3. 实现部分接口(Implementor):定义了实现部分的接口,通常包含了实现抽象部分所定义的方法。
    4. 具体实现部分(Concrete Implementor):实现了实现部分接口,具体实现了实现部分的方法。
  • 场景

    1. 数据库驱动设计:在不同的数据库之间切换时,可以使用桥接模式将抽象部分和实现部分分离,以便灵活地切换。
    2. 图形界面设计:在图形界面库中,可以使用桥接模式将不同操作系统平台(如Windows、Linux、Mac)的图形界面控件和操作系统分离,提高了跨平台的灵活性。
    3. 手机操作系统设计:在手机操作系统中,可以使用桥接模式将手机应用和手机硬件分离,以便在不同手机硬件平台上灵活地切换。
    4. 消息发送系统:在消息发送系统中,可以使用桥接模式将不同的消息发送方式(如短信、邮件、微信)和消息内容分离,以便灵活地选择发送方式和内容。
  • 优点

    1. 解耦合:将抽象部分与其实现部分分离,使得它们可以独立地变化,降低了系统的耦合度。
    2. 灵活性:抽象部分和实现部分可以独立地扩展和变化,系统更加灵活。
    3. 可扩展性:可以方便地添加新的抽象部分和实现部分,符合开闭原则。
    4. 复用性:抽象部分和实现部分可以复用,提高了代码的复用性。
  • 缺点

    1. 增加系统复杂度:引入了抽象部分和实现部分之间的桥接,增加了系统的复杂度。
    2. 需正确识别抽象与实现:需要正确识别抽象部分和实现部分,否则会增加系统的混乱程度。
  • 示例

// 实现部分接口:消息发送方式
interface MessageSender {
    void sendMessage(String message);
}

// 具体实现部分:短信发送
class SmsSender implements MessageSender {
    @Override
    public void sendMessage(String message) {
        System.out.println("通过短信发送消息:" + message);
    }
}

// 具体实现部分:邮件发送
class EmailSender implements MessageSender {
    @Override
    public void sendMessage(String message) {
        System.out.println("通过邮件发送消息:" + message);
    }
}

// 抽象部分:消息
abstract class Message {
    // 持有实现部分的引用
    protected MessageSender sender;

    // 构造方法,传入实现部分的对象
    public Message(MessageSender sender) {
        this.sender = sender;
    }

    // 发送消息的抽象方法
    public abstract void send();
}

// 扩展抽象部分:普通消息
class NormalMessage extends Message {
    // 构造方法,调用父类构造方法
    public NormalMessage(MessageSender sender) {
        super(sender);
    }

    // 实现发送消息的方法
    @Override
    public void send() {
        // 调用实现部分的方法
        sender.sendMessage("普通消息");
    }
}

// 扩展抽象部分:加急消息
class UrgentMessage extends Message {
    // 构造方法,调用父类构造方法
    public UrgentMessage(MessageSender sender) {
        super(sender);
    }

    // 实现发送消息的方法
    @Override
    public void send() {
        // 调用实现部分的方法
        sender.sendMessage("加急消息");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        // 创建短信发送对象
        MessageSender smsSender = new SmsSender();
        // 创建邮件发送对象
        MessageSender emailSender = new EmailSender();

        // 创建普通消息对象,传入短信发送对象
        Message normalMessage1 = new NormalMessage(smsSender);
        // 发送普通消息
        normalMessage1.send();

        // 创建加急消息对象,传入邮件发送对象
        Message urgentMessage1 = new UrgentMessage(emailSender);
        // 发送加急消息
        urgentMessage1.send();

        // 创建普通消息对象,传入邮件发送对象
        Message normalMessage2 = new NormalMessage(emailSender);
        // 发送普通消息
        normalMessage2.send();

        // 创建加急消息对象,传入短信发送对象
        Message urgentMessage2 = new UrgentMessage(smsSender);
        // 发送加急消息
        urgentMessage2.send();
    }
}
  • 输出结果
通过短信发送消息:普通消息
通过邮件发送消息:加急消息
通过邮件发送消息:普通消息
通过短信发送消息:加急消息

适配器模式

适配器模式是一种结构设计模式,用于使接口不兼容的类可以一起工作。它允许将现有类的接口转换为所需的接口,从而使得不兼容的类可以进行协同工作。

  • 结构

    1. 目标接口(Target):定义客户端使用的接口。
    2. 适配器(Adapter):实现目标接口,并持有被适配的对象,将被适配对象的接口转换为目标接口。
    3. 被适配者(Adaptee):定义了需要被适配的接口。
  • 场景

    1. 第三方库的接口不兼容:当需要使用某个第三方库,但其接口与系统中已有的接口不兼容时,可以使用适配器模式将其接口转换为系统需要的接口。
    2. 新旧系统的整合:当需要将新系统与旧系统进行整合时,由于接口不兼容,可以使用适配器模式进行转换。
    3. 在不破坏现有接口的情况下添加功能:当需要在不破坏现有接口的情况下添加功能时,可以使用适配器模式。
  • 优点

    1. 兼容性:可以让原本不兼容的接口能够一起工作,使得客户端无需修改现有代码。
    2. 复用性:可以复用现有的类,而无需修改其代码。
    3. 解耦性:客户端与被适配者之间解耦,适配器作为中间层,降低了客户端与被适配者之间的依赖关系。
  • 缺点

    1. 过多的适配器:如果系统中适配器的数量过多,可能会增加系统的复杂性。
    2. 增加了代码的阅读难度:适配器模式可能会增加代码的阅读难度,因为需要理解适配器与被适配者之间的关系。
  • 示例

// 目标接口
interface Target {
    void request();
}

// 具体目标类
class ConcreteTarget implements Target {
    @Override
    public void request() {
        System.out.println("具体目标类的方法被调用");
    }
}

// 适配器类
class Adapter implements Target {
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

// 被适配者类
class Adaptee {
    public void specificRequest() {
        System.out.println("被适配者类的方法被调用");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 使用具体目标类
        Target target = new ConcreteTarget();
        target.request(); // 输出:具体目标类的方法被调用

        // 使用适配器类
        Adaptee adaptee = new Adaptee();
        Target adapter = new Adapter(adaptee);
        adapter.request(); // 输出:被适配者类的方法被调用
    }
}
  • 输出结果
具体目标类的方法被调用
被适配者类的方法被调用

享元模式

享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享尽可能多的对象来最小化内存使用或计算开销。该模式适用于大量相似对象的场景,以减少内存占用和提高性能。

  • 结构

    1. Flyweight(享元接口):
      • 定义了享元对象的接口,通过这个接口可以接受并作用于外部状态。
      • 通常包括一个或多个方法,用于接受外部状态并执行相应的操作。
    2. ConcreteFlyweight(具体享元类):
      • 实现了享元接口,并且包含了内部状态。
      • 具体享元对象通常是可共享的,并且在系统中可能会被多次使用。
    3. UnsharedConcreteFlyweight(非共享的具体享元类,可选):
      • 有时候并不是所有的享元对象都可以被共享,因此可以定义非共享的具体享元类。
      • 这些对象的状态不会被共享,每个对象都是独立的。
    4. FlyweightFactory(享元工厂):
      • 负责创建和管理享元对象。
      • 在请求享元对象之前,通常会首先检查工厂中是否已经存在对应的享元对象,如果存在则直接返回,否则创建一个新的享元对象并将其放入工厂中。
    5. Client(客户端):
    • 使用享元对象的客户端。
    • 客户端通常负责创建或获取享元工厂,并通过工厂获取具体的享元对象。
  • 场景

    1. 文本编辑器:在文本编辑器中,字符和字体可以被视为享元对象。大量相同字符或字体可以共享相同的状态,从而减少内存占用。
    2. 游戏开发:在游戏开发中,例如棋盘游戏中的棋子,可以使用享元模式来共享相同的状态,以减少对象数量并提高性能。
    3. 连接池:数据库连接池、线程池等资源池都可以使用享元模式来管理和复用资源对象,以减少资源的创建和销毁开销。
    4. 图形系统:在图形系统中,像素点、颜色等可以被视为享元对象,大量相同的像素点或颜色可以共享相同的状态,以减少内存占用。
  • 优点

    1. 减少内存占用:通过共享相同的状态来减少内存使用,特别是当存在大量相似对象时。
    2. 提高性能:减少了对象的数量,从而减少了内存访问和计算开销,提高了系统的性能。
    3. 灵活性:享元模式允许在运行时添加或移除共享对象,从而使系统更加灵活。
  • 缺点

    1. 复杂性增加:实现享元模式可能需要引入额外的复杂性,特别是在管理共享状态和外部状态之间的关系时。
    2. 可能影响线程安全性:如果共享的对象在多个线程之间共享并且会修改状态,那么可能需要考虑线程安全性问题。
  • 示例

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

// 享元接口
interface CharFlyweight {
    void display(String font);
}

// 具体享元类:字符对象
class Char implements CharFlyweight {
    private char character;

    public Char(char character) {
        this.character = character;
    }

    @Override
    public void display(String font) {
        System.out.println("Character: " + character + ", Font: " + font);
    }
}

// 享元工厂类
class CharFactory {
    private Map<Character, CharFlyweight> charMap = new HashMap<>();

    public CharFlyweight getChar(char character) {
        if (!charMap.containsKey(character)) {
            charMap.put(character, new Char(character));
        }
        return charMap.get(character);
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        CharFactory charFactory = new CharFactory();

        CharFlyweight charA = charFactory.getChar('A');
        charA.display("Arial");

        CharFlyweight charB = charFactory.getChar('B');
        charB.display("Times New Roman");

        // 再次获取相同的字符对象
        CharFlyweight charA2 = charFactory.getChar('A');
        charA2.display("Calibri");
    }
}
  • 输出结果
Character: A, Font: Arial
Character: B, Font: Times New Roman
Character: A, Font: Calibri

装饰器模式

装饰器模式是一种结构设计模式,它允许你动态地将责任附加到对象上。装饰器模式提供了一种灵活的方式来扩展对象的功能,而无需通过子类化来实现。

  • 结构

    1. 组件(Component):定义了被装饰对象的接口,可以是抽象类或接口。
    2. 具体组件(ConcreteComponent):实现了组件接口,是被装饰的对象。
    3. 装饰器(Decorator):继承了组件接口,并持有一个指向组件对象的引用。通常是抽象类,它的子类可以扩展组件的功能。
    4. 具体装饰器(ConcreteDecorator):实现了装饰器接口,具体地添加额外的功能。
  • 场景

    1. 动态地添加功能:当需要动态地添加或移除对象的功能时,装饰器模式是一个很好的选择。例如,Java的IO流就使用了装饰器模式。
    2. 继承关系不合适:当类的继承关系不合适或不可行时,可以使用装饰器模式。例如,如果需要扩展一个类的功能,但又不希望创建其子类的情况下。
    3. 避免类爆炸:当使用继承会导致类的数量急剧增加时,装饰器模式可以避免类爆炸问题。
  • 优点

    1. 灵活性:可以动态地向对象添加新的功能,而无需修改其原始代码。
    2. 遵循开闭原则:允许新增功能而不影响现有代码,符合开闭原则。
    3. 避免类爆炸:相比使用继承来扩展功能,装饰器模式避免了类的数量急剧增加。
  • 缺点

    1. 增加复杂性:引入了许多小对象和装饰器之间的相互关系,可能会增加代码复杂性。
    2. 滥用可能性:过度使用装饰器模式可能导致代码变得难以理解和维护。
  • 示例

// 抽象组件接口(Component Interface)
interface Component {
    void operation();
}

// 具体组件类(Concrete Component Class)
class ConcreteComponent implements Component {
    public void operation() {
        System.out.println("Performing operation in the concrete component.");
    }
}

// 抽象装饰器类(Decorator Abstract Class)
abstract class Decorator implements Component {
    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    public void operation() {
        component.operation();
    }
}

// 具体装饰器类(Concrete Decorator Class)
class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    public void operation() {
        super.operation();
        additionalOperationA();
    }

    private void additionalOperationA() {
        System.out.println("Performing additional operation A.");
    }
}

class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    public void operation() {
        super.operation();
        additionalOperationB();
    }

    private void additionalOperationB() {
        System.out.println("Performing additional operation B.");
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        // 创建具体组件对象
        Component component = new ConcreteComponent();

        // 对具体组件对象进行装饰
        Component decoratedComponent = new ConcreteDecoratorA(new ConcreteDecoratorB(component));

        // 执行操作
        decoratedComponent.operation();
    }
}
  • 输出结果
Performing operation in the concrete component.
Performing additional operation B.
Performing additional operation A.

组合模式

组合模式是一种结构型设计模式,它允许将对象组合成树形结构以表示“部分-整体”的层次结构。通过组合模式,客户端可以统一对待单个对象和组合对象,从而简化了客户端的代码。

  • 结构

    1. 组件(Component):定义了组合中所有对象的通用接口,可以是抽象类或接口,它声明了一些用于管理子节点的方法,如增加子节点、删除子节点、获取子节点等。
    2. 叶子节点(Leaf):表示组合中的叶子节点对象,它没有子节点。
    3. 复合节点(Composite):表示组合中的复合节点对象,它可以包含其他子节点,通常会实现组件接口中的方法来管理子节点。
  • 场景

    1. 表示“部分-整体”层次结构:当需要表示一个对象的部分-整体层次结构时,可以使用组合模式。
    2. 处理树形结构数据:当需要处理树形结构的数据时,可以使用组合模式来统一处理叶子节点和复合节点。
    3. 构建菜单、文件系统等:组合模式常用于构建菜单、文件系统等具有树形结构的应用程序。
  • 优点

    1. 简化客户端代码:客户端可以统一对待单个对象和组合对象,从而简化了客户端的代码。
    2. 灵活性:组合模式可以灵活地构建树形结构,允许动态地增加或删除节点。
    3. 可扩展性:组合模式使得客户端可以方便地增加新的组件类型,而无需修改现有代码。
  • 缺点

    1. 限制类型:组合模式限制了组合中所有对象的类型必须相同,这可能会限制组合模式的使用场景。
    2. 增加了复杂性:组合模式引入了多个新的类,可能会增加系统的复杂性。
  • 示例

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

// 组件接口
interface Component {
    void operation();
}

// 叶子节点
class Leaf implements Component {
    private String name;
    
    public Leaf(String name) {
        this.name = name;
    }
    
    @Override
    public void operation() {
        System.out.println("Leaf " + name + " operation");
    }
}

// 复合节点
class Composite implements Component {
    private String name;
    private List<Component> children = new ArrayList<>();
    
    public Composite(String name) {
        this.name = name;
    }
    
    public void add(Component component) {
        children.add(component);
    }
    
    public void remove(Component component) {
        children.remove(component);
    }
    
    @Override
    public void operation() {
        System.out.println("Composite " + name + " operation");
        for (Component component : children) {
            component.operation();
        }
    }
}

public class CompositePatternExample {
    public static void main(String[] args) {
        Component leaf1 = new Leaf("Leaf1");
        Component leaf2 = new Leaf("Leaf2");
        Component leaf3 = new Leaf("Leaf3");
        
        Composite composite1 = new Composite("Composite1");
        composite1.add(leaf1);
        composite1.add(leaf2);
        
        Composite composite2 = new Composite("Composite2");
        composite2.add(leaf3);
        
        Composite root = new Composite("Root");
        root.add(composite1);
        root.add(composite2);
        
        root.operation();
    }
}
  • 输出结果
Composite Root operation
Composite Composite1 operation
Leaf Leaf1 operation
Leaf Leaf2 operation
Composite Composite2 operation
Leaf Leaf3 operation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/582106.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

语音识别的基本概念

语音识别的基本概念​​​​​​​ ​​​​​​​ 言语是一种复杂的现象。人们很少了解它是如何产生和感知的。天真的想法常常是语音是由单词构成的&#xff0c;而每个单词又由音素组成。不幸的是&#xff0c;现实却大不相同。语音是一个动态过程&#xff0c;没有明确区分的…

Spring AI聊天功能开发

一、引入依赖 继承父版本的springboot依赖&#xff0c;最好是比较新的依赖。 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.4</version><relativePat…

JS实现对用户名、密码进行正则表达式判断,按钮绑定多个事件,网页跳转

目标&#xff1a;使用JS实现对用户名和密码进行正则表达式判断&#xff0c;用户名和密码正确时&#xff0c;进行网页跳转。 用户名、密码的正则表达式检验 HTML代码&#xff1a; <button type"submit" id"login-btn" /*onclick"login();alidate…

Spring Boot | Spring Boot 实现 “Redis缓存管理“

目录 : Spring Boot 实现 "Redis缓存管理" :一、Spring Boot 支持的 "缓存组件" &#xff08; 如果 “没有” 明确指定使用自定义的 "cacheManager "或 "cacheResolver" &#xff0c;此时 SpringBoot会按照“预先定义的顺序” 启动一个…

免费SSL证书和付费SSL证书区别在哪

SSL证书免费和付费的区别有&#xff1a; 1.证书类型不同&#xff0c;免费SSL证书只有域名验证性型&#xff0c;付费SSL证书有域名验证型、企业验证型和组织验证型&#xff1b; 2.使用限制不同&#xff0c;免费SSL证书只能绑定单个域名、不支持通配符域名、多域名等&#xff0…

4.28java项目小结

这几天完成了用户修改资料模块的功能&#xff0c;实现了修改用户头像&#xff0c;昵称等信息&#xff0c;并且对数据库进行了操作&#xff0c;大致画了好友资料的页面的内容&#xff0c;这两天尽量完成表的创建&#xff0c;建立多对多的关系&#xff0c;实现好友的添加功能。

代码随想录:二叉树29-30

目录 701.二叉搜索树中的插入操作 题目 代码&#xff08;迭代法走一边&#xff09; 代码&#xff08;递归法走一边&#xff09; 450.删除二叉搜索树中的节点 题目 代码&#xff08;递归法走一边&#xff09; 701.二叉搜索树中的插入操作 题目 给定二叉搜索树&#xff…

centos7 openresty lua 自适应webp和缩放图片

目录 背景效果图准备安装cwebp等命令&#xff0c;转换文件格式安装ImageMagick&#xff0c;压缩文件下载Lua API 操控ImageMagick的依赖包 代码参考 背景 缩小图片体积&#xff0c;提升加载速度&#xff0c;节省流量。 效果图 参数格式 &#xff1a; ?image_processformat,…

在IDEA中使用.env文件导入系统配置的图文教程

JetBrains的IDEA是一款功能强大的集成开发环境&#xff0c;为开发人员提供了丰富的功能和工具。使用.env文件来管理配置信息在IDEA中非常简单。 旧版本默认支持&#xff0c;新版本idea需要安装插件才可以。 这里我们可以安装EnvFile插件&#xff0c;步骤如下&#xff1a; 在弹…

centos 7 安装 JDK 和Rockmq

1、版本说明 CentOS版本&#xff1a;使用 cat /etc/redhat-release 命令查看centos版本。 本次版本是&#xff1a;CentOS Linux release 7.9.2009 (Core) JDK版本是&#xff1a;jdk-8u401-linux-x64.tar.gz RockeqMQ版本&#xff1a;rocketmq-all-4.9.2-bin-release.zip …

Vue 组件单元测试深度探索:细致解析与实战范例大全

Vue.js作为一款广受欢迎的前端框架&#xff0c;以其声明式的数据绑定、组件化开发和灵活的生态系统赢得了广大开发者的心。然而&#xff0c;随着项目规模的增长&#xff0c;确保组件的稳定性和可靠性变得愈发关键。单元测试作为软件质量的守护神&#xff0c;为Vue组件的开发过程…

人脸识别系统架构

目录 1. 系统架构 1.1 采集子系统 1.2 解析子系统 1.3 存储子系统 1.4 比对子系统 1.5 决策子系统 1.6 管理子系统 1.7 应用开放接口 2. 业务流程 2.1 人脸注册 2.2 人脸验证 2.2.1 作用 2.2.2 特点 2.2.3 应用场景 2.3 人脸辨识 2.3.1 作用 2.3.2 特点 2.3.3…

学习STM32第二十天

低功耗编程 一、修改主频 STM32F4xx系列主频为168MHz&#xff0c;当板载8MHz晶振时&#xff0c;系统时钟HCLK满足公式 H C L K H S E P L L N P L L M P L L P HCLK \frac{HSE \times PLLN}{PLLM \times PLLP} HCLKPLLMPLLPHSEPLLN​&#xff0c;在文件stm32f4xx.h中可修…

HTML 学习笔记

html 超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;是一种用于创建网页的标准标记语言。 1.HTML文档的后缀名 (1) .html (2) .htm 这里更推荐使用 ".html "&#xff0c;命名应该遵从含义清…

FPGA 以太网概念简单学习

1 MAC和PHY 从硬件的角度来说&#xff0c;以太网接口电路主要由 MAC &#xff08; Media Access Control &#xff09;控制器和物理层接口 PHY&#xff08;Physical Layer &#xff0c; PHY &#xff09;两大部分构成。 MAC 指媒体访问控制子层协议&#xff0c;它和 PHY 接…

SpringMVC进阶(自定义拦截器以及异常处理)

文章目录 1.自定义拦截器1.基本介绍1.说明2.自定义拦截器的三个方法3.流程图 2.快速入门1.Myinterceptor01.java2.FurnHandler.java3.springDispatcherServlet-servlet.xml配置拦截器4.单元测试 3.拦截特定路径1.拦截指定路径2.通配符配置路径 4.细节说明5.多个拦截器1.执行流程…

刷代码随想录有感(49):找树左下角的值

题干&#xff1a; 用层序遍历方便些&#xff0c;因为只需要把res不断替换成每一层第一个节点值即可&#xff0c;代码如下&#xff1a; class Solution { public:int findBottomLeftValue(TreeNode* root) {queue<TreeNode*>que;if(root ! NULL)que.push(root);int res …

逆向案例三十——webpack登录某游戏

网址&#xff1a;aHR0cHM6Ly93d3cuZ205OS5jb20v 步骤&#xff1a; 进行抓包分析&#xff0c;找到登录接口&#xff0c;发现密码有加密 跟栈分析&#xff0c;从第三个栈进入&#xff0c;打上断点&#xff0c;再次点击登录 明显找到password,它由o赋值&#xff0c;o由a.encode(…

【哈希】Leetcode 面试题 01.02. 判定是否互为字符重排

题目讲解 面试题 01.02. 判定是否互为字符重排 算法讲解 直观的想法&#xff1a;我们找到一个字符串的全排列&#xff0c;然后对比当前的排列是否等于另一个字符串。如果两个字符串如果互为排列&#xff0c;所以我们知道两个字符串对应的字符出现的个数相同&#xff0c;那么…

在config.json文件中配置出来new mars3d.graphic.PolylineCombine({大量线合并渲染类型的geojson图层

在config.json文件中配置出来new mars3d.graphic.PolylineCombine({大量线合并渲染类型的geojson图层 问题场景&#xff1a; 1.浏览官网示例的时候图层看到大量线数据合并渲染的示例 2.矢量数据较大量级的时候&#xff0c;这种时候怎么在config.json文件中尝试配置呢&#x…
最新文章