首页 > 编程语言 >java设计模式,英雄联盟的例子学习结构型模式

java设计模式,英雄联盟的例子学习结构型模式

时间:2024-10-28 16:17:20浏览次数:8  
标签:java String void 接口 public new 设计模式 class 结构型

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

结构型模式主要关注类或对象的组合,帮助确保如果一个系统的结构发生变化,系统的其他部分不会受到影响。

适配器模式

主要组成部分

  1. 目标接口(Target):客户端所期待的接口。

  2. 源类(Adaptee):需要适配的类,具有与目标接口不兼容的方法。

  3. 适配器(Adapter):实现目标接口,并持有一个源类的实例,负责将源类的调用转换为目标接口的调用。

使用场景

  • 当你想使用一些现有的类,但它们的接口不符合你的需求时。

  • 系统需要与一些不兼容的接口交互。

  • 当需要将多个不相关的类组合成一个统一的接口时。

在我们玩游戏去外服玩的时候,就需要用到一个叫做加速器的东西,不用的话就会很卡,我们用适配器模式来编写一遍

// Game 接口
interface Game {
    void start();
}

// SteamGame 类
class SteamGame implements Game {
    @Override
    public void start() {
        System.out.println("Starting game on Steam.");
    }
}

// VPNAccelerator 类
class VPNAccelerator {
    public void connect() {
        System.out.println("Connecting to VPN...");
    }

    public void accelerate() {
        System.out.println("Accelerating game speed.");
    }
}

// Adapter 类
class VPNAcceleratorAdapter implements Game {
    private VPNAccelerator vpnAccelerator;

    public VPNAcceleratorAdapter(VPNAccelerator vpnAccelerator) {
        this.vpnAccelerator = vpnAccelerator;
    }

    @Override
    public void start() {
        vpnAccelerator.connect();
        vpnAccelerator.accelerate();
        System.out.println("Game started with accelerator.");
    }
}

// 测试
public class AdapterPatternDemo {
    public static void main(String[] args) {
        Game steamGame = new SteamGame();
        steamGame.start();

        VPNAccelerator vpnAccelerator = new VPNAccelerator();
        Game vpnAcceleratorAdapter = new VPNAcceleratorAdapter(vpnAccelerator);
        vpnAcceleratorAdapter.start();
    }
}

这样子做同样是开启游戏的start(),但是我们使用了加速器在中间加速

桥接模式

主要组成部分

  1. 抽象类(Abstraction):定义高层操作的接口。

  2. 扩展抽象类(RefinedAbstraction):对抽象类的具体实现,可能会增加一些新的功能。

  3. 实现接口(Implementor):定义实现类的接口,通常会包含一些基础的方法。

  4. 具体实现类(ConcreteImplementor):实现实现接口的具体类。

使用场景

  • 当你希望在抽象和实现之间进行解耦,以便能够独立地改变它们。

  • 当你希望可以在运行时选择实现,或者需要组合多个实现。

当英雄联盟里各个英雄的皮肤的炫彩特效就可以使用桥接模式设计

// 抽象特效接口
interface Effect {
    void apply();
}

// 具体的特效实现:蓝色炫彩
class BlueEffect implements Effect {
    @Override
    public void apply() {
        System.out.println("应用蓝色炫彩特效");
    }
}

// 具体的特效实现:紫色炫彩
class PurpleEffect implements Effect {
    @Override
    public void apply() {
        System.out.println("应用紫色炫彩特效");
    }
}

// 抽象皮肤类
abstract class Skin {
    protected Effect effect;

    public Skin(Effect effect) {
        this.effect = effect;
    }

    public abstract void applySkin();
}

// 具体的皮肤实现:剑圣泳池派对
class PoolPartyYasuo extends Skin {
    public PoolPartyYasuo(Effect effect) {
        super(effect);
    }

    @Override
    public void applySkin() {
        System.out.println("应用剑圣泳池派对皮肤");
        effect.apply();
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Skin bluePoolPartyYasuo = new PoolPartyYasuo(new BlueEffect());
        bluePoolPartyYasuo.applySkin(); // 应用剑圣泳池派对皮肤和蓝色炫彩特效

        Skin purplePoolPartyYasuo = new PoolPartyYasuo(new PurpleEffect());
        purplePoolPartyYasuo.applySkin(); // 应用剑圣泳池派对皮肤和紫色炫彩特效
    }
}

在例子中泳池派对皮肤,不同的炫彩就不需要创建不同类来表达,避免了类爆炸的问题。

组合模式

主要组成部分

  1. 组件(Component)

    • 声明了叶子节点和组合节点的共同接口,通常是一个抽象类或接口。

  2. 叶子(Leaf)

    • 具体的子类,表示组合中的叶子节点。叶子节点没有子节点,因此通常实现了组件接口中的所有方法。

  3. 组合(Composite)

    • 继承自组件,定义了有子节点的对象的行为。组合可以包含叶子节点或其他组合,提供操作方法来管理其子节点(如添加、删除等)。

使用场景

  1. 文件系统

    • 在文件系统中,文件和文件夹可以使用组合模式表示。文件夹可以包含其他文件夹或文件,客户端可以通过统一的接口处理文件和文件夹。

  2. 图形绘制

    • 在图形编辑器中,形状(如线条、圆形、矩形等)可以组合成复杂的图形。组合模式允许用户以统一的方式处理单个形状和形状组合。

  3. 树形结构

    • 当数据具有树形结构(如组织架构、目录结构等)时,可以使用组合模式来管理部分和整体的关系。

  4. 菜单系统

    • 在用户界面设计中,菜单可以包含子菜单和菜单项。使用组合模式,可以轻松地构建复杂的菜单结构,同时提供一致的操作接口。

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

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

// 叶子类:文件
class File implements FileSystemComponent {
    private String name;

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

    @Override
    public void display() {
        System.out.println("文件: " + name);
    }
}

// 组合类:文件夹
class Folder implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> components = new ArrayList<>();

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

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

    public void remove(FileSystemComponent component) {
        components.remove(component);
    }

    @Override
    public void display() {
        System.out.println("文件夹: " + name);
        for (FileSystemComponent component : components) {
            component.display();
        }
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        // 创建文件
        File file1 = new File("文档1.txt");
        File file2 = new File("图片1.jpg");

        // 创建文件夹
        Folder folder1 = new Folder("文件夹1");
        folder1.add(file1);
        folder1.add(file2);

        // 创建一个更大的文件夹
        Folder rootFolder = new Folder("根文件夹");
        rootFolder.add(folder1);

        // 显示文件系统结构
        rootFolder.display();
    }
}

组合模式就很适合那种需要套娃的数据结构模式。

装饰模式

主要组成部分

  1. 抽象组件(Component)

    • 定义一个接口或抽象类,用于声明对象的行为。

  2. 具体组件(Concrete Component)

    • 实现了抽象组件接口的具体类,表示被装饰的对象。

  3. 装饰类(Decorator)

    • 继承自抽象组件,持有一个指向抽象组件的引用。装饰类可以在调用基本行为之前或之后添加额外的功能。

  4. 具体装饰类(Concrete Decorators)

    • 具体的装饰类,扩展了装饰类,添加具体的功能。

使用场景

  1. 动态增加功能

    • 当需要在运行时为对象动态地添加新功能时,装饰模式非常适用。例如,图形编辑器中可以为形状添加不同的边框或颜色。

  2. 避免类爆炸

    • 当功能的组合可能导致类的数量迅速增加时,可以使用装饰模式。它通过将功能分离到装饰类中,减少了子类的数量。

  3. 增强功能

    • 可以通过装饰模式来增强已有对象的功能,而不需要修改其代码。例如,给一个已有的文本框添加滚动条。

// 抽象组件接口
interface Attack {
    String getDescription();
    double getDamage();
}

// 具体组件:基础攻击
class BasicAttack implements Attack {
    @Override
    public String getDescription() {
        return "基础攻击";
    }

    @Override
    public double getDamage() {
        return 50.0; // 基础攻击伤害
    }
}

// 装饰类
abstract class AttackDecorator implements Attack {
    protected Attack attack;

    public AttackDecorator(Attack attack) {
        this.attack = attack;
    }

    @Override
    public String getDescription() {
        return attack.getDescription();
    }

    @Override
    public double getDamage() {
        return attack.getDamage();
    }
}

// 具体装饰类:长剑
class LongSwordDecorator extends AttackDecorator {
    public LongSwordDecorator(Attack attack) {
        super(attack);
    }

    @Override
    public String getDescription() {
        return attack.getDescription() + ", 加长剑";
    }

    @Override
    public double getDamage() {
        return attack.getDamage() + 10.0; // 长剑增加10点伤害
    }
}

// 具体装饰类:破败
class BladeOfTheRuinedKingDecorator extends AttackDecorator {
    public BladeOfTheRuinedKingDecorator(Attack attack) {
        super(attack);
    }

    @Override
    public String getDescription() {
        return attack.getDescription() + ", 加破败";
    }

    @Override
    public double getDamage() {
        return attack.getDamage() + 20.0; // 破败增加20点伤害
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Attack basicAttack = new BasicAttack();
        System.out.println(basicAttack.getDescription() + " 伤害: " + basicAttack.getDamage());

        // 添加长剑
        Attack longSwordAttack = new LongSwordDecorator(basicAttack);
        System.out.println(longSwordAttack.getDescription() + " 伤害: " + longSwordAttack.getDamage());

        // 添加破败
        Attack finalAttack = new BladeOfTheRuinedKingDecorator(longSwordAttack);
        System.out.println(finalAttack.getDescription() + " 伤害: " + finalAttack.getDamage());
    }
}

使用另外一个对象来改变整个对象的行为。

外观模式

主要组成部分

  1. 外观类(Facade)

    • 提供一个统一的接口,调用复杂子系统的功能。外观类通常包含对多个子系统的引用。

  2. 子系统类(Subsystem Classes)

    • 具体的功能类,负责实现系统的某些功能。子系统通常不会直接与客户端交互。

使用场景

  1. 简化复杂系统

    • 当系统包含多个类和功能时,使用外观模式可以简化客户端的调用,隐藏复杂性。

  2. 分离接口和实现

    • 当希望将接口与实现分离,使得客户端与子系统的解耦。

  3. 提高可维护性

    • 当系统的某些部分发生变化时,外观模式可以减少对客户端的影响,从而提高系统的可维护性。

// 子系统类
class CPU {
    public void freeze() {
        System.out.println("CPU 冻结");
    }

    public void jump(long position) {
        System.out.println("CPU 跳转到 " + position);
    }

    public void execute() {
        System.out.println("CPU 执行");
    }
}

// 子系统类
class Memory {
    public void load(long position, byte[] data) {
        System.out.println("内存加载数据到 " + position);
    }
}

// 子系统类
class HardDrive {
    public byte[] read(long lba, int size) {
        System.out.println("从硬盘读取数据,LBA: " + lba + ", 大小: " + size);
        return new byte[size];
    }
}

// 外观类
class Computer {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;

    public Computer() {
        cpu = new CPU();
        memory = new Memory();
        hardDrive = new HardDrive();
    }

    public void start() {
        cpu.freeze();
        memory.load(0, hardDrive.read(0, 1024));
        cpu.jump(0);
        cpu.execute();
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.start(); // 启动计算机
    }
}

我们只需要暴露出一个start()方法就可以调用那些用户不需要知道的复杂操作

享元模式

主要组成部分

  1. 享元接口(Flyweight)

    • 定义了可以共享的对象的接口,通常包括一些方法来访问内部状态。

  2. 具体享元(Concrete Flyweight)

    • 实现了享元接口,存储共享状态(内部状态)并可以接受外部状态(非共享状态)作为参数。

  3. 享元工厂(Flyweight Factory)

    • 负责管理享元对象的创建和共享。工厂类确保返回的享元对象是共享的。

使用场景

  1. 需要大量相似对象

    • 在应用中需要创建大量相似的对象,但这些对象的状态大部分是相同的,可以使用享元模式来共享相同的部分。

  2. 节省内存

    • 当对象数量较多时,使用享元模式可以显著减少内存占用,特别是在需要频繁创建和销毁对象的场合。

  3. 提高性能

    • 通过共享相同的对象,可以减少对象创建的时间和内存分配的开销,从而提高性能。

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

// 享元接口
interface MinionType {
    void display(int x, int y);
}

// 具体享元类:小兵类型
class Minion implements MinionType {
    private String type; // 小兵类型(如 melee, ranged)
    private String skin; // 皮肤(如普通小兵、超级小兵)

    public Minion(String type, String skin) {
        this.type = type;
        this.skin = skin;
    }

    @Override
    public void display(int x, int y) {
        System.out.println("小兵类型: " + type + " | 皮肤: " + skin + " | 坐标: (" + x + ", " + y + ")");
    }
}

// 享元工厂
class MinionFactory {
    private Map<String, MinionType> minionTypes = new HashMap<>();

    public MinionType getMinionType(String type, String skin) {
        String key = type + "-" + skin;
        MinionType minionType = minionTypes.get(key);
        if (minionType == null) {
            minionType = new Minion(type, skin);
            minionTypes.put(key, minionType);
        }
        return minionType;
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        MinionFactory minionFactory = new MinionFactory();

        // 创建不同类型的小兵,使用享元模式共享小兵的类型
        MinionType meleeMinion = minionFactory.getMinionType("近战", "普通");
        meleeMinion.display(10, 20);

        MinionType rangedMinion = minionFactory.getMinionType("远程", "普通");
        rangedMinion.display(15, 25);

        // 重复获取相同类型的小兵,应该返回同一个实例
        MinionType anotherMeleeMinion = minionFactory.getMinionType("近战", "普通");
        anotherMeleeMinion.display(30, 40);

        System.out.println(meleeMinion == anotherMeleeMinion); // 输出: true,说明共享了同一个对象
    }
}

这样子,小兵就不用重复创建和销毁了,可以节约系统资源的损耗

代理模式

主要组成部分

  1. 抽象主题(Subject)

    • 定义了真实主题和代理对象的共同接口。

  2. 真实主题(RealSubject)

    • 实现了抽象主题接口,代表实际的业务对象。

  3. 代理(Proxy)

    • 也实现了抽象主题接口,并持有一个真实主题的引用。代理对象可以在调用真实主题的方法时添加额外的行为(如权限检查、日志记录等)。

// 抽象主题
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("加载图像: " + filename);
    }

    @Override
    public void display() {
        System.out.println("显示图像: " + 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);
        }
        realImage.display();
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Image image1 = new ProxyImage("image1.jpg");
        Image image2 = new ProxyImage("image2.jpg");

        // 图像加载只在第一次调用时发生
        image1.display();
        image1.display(); // 不会重复加载

        image2.display();
    }
}

代码解析

  1. Image 接口:定义了显示图像的方法。

  2. RealImage 类:实现了 Image 接口,负责实际图像的加载和显示。

  3. ProxyImage 类:实现了 Image 接口,持有对 RealImage 的引用,控制图像的加载和显示。

  4. Main 类:客户端代码,使用代理对象来展示图像,真实图像在首次调用时才加载。

输出结果

当运行上述代码时,输出结果将是:

加载图像: image1.jpg
显示图像: image1.jpg
显示图像: image1.jpg
加载图像: image2.jpg
显示图像: image2.jpg

标签:java,String,void,接口,public,new,设计模式,class,结构型
From: https://blog.csdn.net/m0_61547204/article/details/143305837

相关文章

  • java 将Log4j2 的日志内容输出到udp上
    在Maven项目中pom.xml中添加Log4j2的依赖 <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.1</version></dependency><depen......
  • 2024年10月新版Java面试八股文大总结!
    线程有哪几种状态。(1)NEW线程至今尚未启动(2)RUNNABLE线程正在 Java虚拟机中执行(3)BLOCKED受阻塞并等待获得同步代码块的锁(4)WAITING无限期地等待另一个线程来执行某一特定操作(5)TIMED_WAITING在指定的时间内等待另一个线程来执行某一特定操作(6)TERMINATED线程已退出注......
  • 简单的Java二维码应用
    闲来无事做一个java的小应用玩玩,可以模拟微信小程序刷二维码。需要解决的主要问题:二维码获取二维码展示1.抓包微信小程序,获取所需信息微信电脑版可以登录小程序,通过Charles可以抓包。配置Charles,可以参考文章:charles使用教程,只要配置好证书就行运行Charles,打开微信小......
  • 【JAVA毕业设计】基于Vue和SpringBoot的校园美食分享平台
    本文项目编号T033,文末自助获取源码\color{red}{T033,文末自助获取源码}......
  • 【JAVA毕业设计】基于Vue和SpringBoot的购物商城网站
    本文项目编号T032,文末自助获取源码\color{red}{T032,文末自助获取源码}......
  • 大话Java系列-并发场景下HashMap的环形链表问题,jmap检查内存状态,jstack查看线程状态,线
    文章目录童话故事故事开始发现问题解决问题代码实现1.使用普通`HashMap`导致的环形链表问题2.使用`jmap-histo`检查内存状态3.使用`jstack`查看当前线程的状态4.分析结果`jmap-histo`输出示例`jstack`输出示例5.使用ConcurrentHashMap解决问题6.使用外部加锁保......
  • java毕业设计基于springboot的商城系统
    文章目录项目介绍技术介绍功能介绍核心代码数据库参考系统效果图项目介绍  在当今信息爆炸的大时代,由于信息管理系统能够更有效便捷的完成信息的管理,越来越多的人及机构都已经引入和发展以信息管理系统为基础的信息化管理模式,随之信息管理技术也在不断的发展和成......
  • 【Java】若以框架(ruoyi-master)——10.BaseController源码了解
    BaseController通用方法Web层的通用数据。所有接口继承此方法。里面有一些各个接口会用到的通用方法。比如分页、返回消息、设置或获取一些登录信息,某些操作的时候,将日志写入或返回。方法归类方法名称说明请求参数预处理initBinder()将前台传递过来的日期格式的字符......
  • Java EasyExcel 导出报内存溢出如何解决
    大家好,我是V哥。使用EasyExcel进行大数据量导出时容易导致内存溢出,特别是在导出百万级别的数据时。你有遇到过这种情况吗,以下是V哥整理的解决该问题的一些常见方法,分享给大家,欢迎一起讨论:EasyExcel大数据量导出常见方法1.分批写入EasyExcel支持分批写入数据,可以将数据分批......
  • java excel转pdf
    使用jacob实现Excel转PDF在使用jacob之前需要做一些准备,首先需要去下载jacob的压缩包jacob.zip,下载地址:https://github.com/freemansoft/jacob-project/releases/download/Root_B-1_21/jacob-1.21.zip解压之后,得到如下内容: 1、如果你是64位系统就用x64的dll,32位系统就用x......