Java设计模式:深入解析与应用示例

2023-11-08

引言

设计模式是一种在特定上下文中反复出现的可重用解决方案,用于处理软件设计中常见的问题。掌握设计模式不仅可以帮助我们编写出更优雅、更易于理解和维护的代码,而且也是Java面试中的常考知识点。在本文中,我们将探讨几种常见的设计模式,包括它们的定义、使用场景和Java实现。

一、单例模式

单例模式确保一个类只有一个实例,并提供全局访问点。这种设计模式属于创建型模式,它涉及一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

应用场景:需要频繁实例化然后销毁的对象,数据库连接、线程池等长时间存在于系统中的对象。

示例代码:

public class Singleton {
    // 使用volatile关键字防止指令重排序
    private static volatile Singleton instance;

    private Singleton() {}

    // 提供全局访问点
    public static Singleton getInstance() {
        // 第一次检查
        if (instance == null) {
            // 加锁
            synchronized (Singleton.class) {
                // 第二次检查
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

我们创建了一个 Singleton 类。Singleton 类有一个 getInstance() 方法,它可以返回 Singleton 类的一个实例。在这个类中,我们有一个私有构造函数,它可以防止其他类实例化这个类。而 getInstance() 方法可以为其他类提供了一种方式来获取这个类的单一实例。

二、工厂模式

工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

应用场景:在编码时不能预见需要创建哪种类的实例,系统需要提供一个接口,让系统与其各种具体实现类之间解耦。

示例代码:

public interface Shape {
    void draw();
}

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}

public class ShapeFactory {
    // 使用getShape方法获取形状类型的对象
    public Shape getShape(String shapeType) {
        if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        }
        // other shape types...
        return null;
    }
}

我们首先创建了一个接口 Shape 和实现了 Shape 接口的实体类。然后我们创建了一个工厂类 ShapeFactoryShapeFactory 类有一个方法 getShape ,根据输入的类型,返回一个实体类的实例。在这个例子中,我们的工厂类 ShapeFactory 是如何根据我们提供的信息来返回不同类的实例的。

三、抽象工厂模式

抽象工厂模式是一种创建型设计模式,它提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。在抽象工厂模式中,抽象工厂定义了产品是什么,为创建一系列相关或相互依赖的对象提供一个接口,而无需指定它们具体的类。

应用场景:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
示例代码:

public interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

public class WinFactory implements GUIFactory {
    // 返回WinButton类的实例
    public Button createButton() {
        return new WinButton();
    }

    // 返回WinCheckbox类的实例
    public Checkbox createCheckbox() {
        return new WinCheckbox();
    }
}

和工厂模式类似,但是这次我们添加了一个新的层次——工厂创造器/生成器类 FactoryProducerAbstractFactory 类是所有工厂类的超类,FactoryProducer 可以根据传入的信息返回一个特定的工厂。

四、建造者模式

建造者模式是一种创建型设计模式,它可以将一个复杂对象的建造过程抽象出来(抽象为指挥者和建造者),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的复杂对象。具体来说,将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

应用场景:需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个部分。

示例代码:

public class Pizza {
    private String dough = "";
    private String sauce = "";
    private String topping = "";

    // setters...
}

public class PizzaBuilder {
    private Pizza pizza;

    public PizzaBuilder() {
        pizza = new Pizza();
    }

    public PizzaBuilder setDough(String dough) {
        pizza.setDough(dough);
        return this;
    }

    public PizzaBuilder setSauce(String sauce) {
        pizza.setSauce(sauce);
        return this;
    }

    public PizzaBuilder setTopping(String topping) {
        pizza.setTopping(topping);
        return this;
    }

    // 最终构建复杂的Pizza对象并返回
    public Pizza build() {
        return pizza;
    }
}

PackingItem 接口表示食物和食物包装。然后我们有了实体类实现了这些接口,BurgerColdDrink 实现了 Item 接口,WrapperBottle 实现了 Packing 接口。Meal 类是一个组合类,包含了 Item 对象。MealBuilder 是实际的构建器,负责创建 Meal 对象。

五、原型模式

原型模式是创建型设计模式,通过复制一个已存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。

应用场景:创建对象成本较大(如初始化需要消耗很多时间,占用太多CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
示例代码:

public class Prototype implements Cloneable {
    // 使用 clone() 方法来创建新的实例
    public Prototype clone() {
        try {
            return

 (Prototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

在原型模式中,我们通过复制一个已存在的对象来创建新的对象。我们创建了一个抽象类 Shape 和几个扩展了 Shape 类的实体类。ShapeCache 类是一个缓存类,将shape对象存储在一个 Hashtable 中,并在请求的时候返回它们的克隆。

六、适配器模式

适配器模式是一种结构型设计模式,它通过把一个类的接口变换成客户端所期待的另一种接口,可以帮助我们解决不兼容的问题。这个模式主要应用于希望复用那些与新系统不兼容的旧组件的场景,使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。

应用场景:已经存在的类,它的方法和我们的需求不一样,也就是接口不相同,或者我们创建了一个可复用的类,该类可能和现有的类库不是很兼容,需要在双方都不易修改的情况下使用适配器模式。

示例代码:

// 目标接口,或称为标准接口
public interface MediaPlayer {
    void play(String audioType, String fileName);
}

// 适配器类,实现MediaPlayer接口
public class MediaAdapter implements MediaPlayer {
    // 适配器中包含了一个需要适配的对象
    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer = new Mp4Player();
        }
    }

    // 调用适配器中的方法
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer.playVlc(fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer.playMp4(fileName);
        }
    }
}

我们有一个 MediaPlayer 接口和一个实现了 MediaPlayer 接口的实体类。然后我们创建了另一个接口 AdvancedMediaPlayer 和实现了 AdvancedMediaPlayer 接口的实体类。然后我们创建了一个适配器类 MediaAdapter,它使用 AdvancedMediaPlayer 对象来播放所需要的格式。

七、装饰器模式

装饰器模式是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它在不需要使用继承的情况下为对象动态添加新的功能。

应用场景:在不想增加很多子类的情况下扩展类,需要动态增加及撤销对象的功能。

示例代码:

// 定义接口Shape
public interface Shape {
    void draw();
}

// 定义装饰器DecoratorShape
public class DecoratorShape implements Shape {
    protected Shape decoratedShape;

    public DecoratorShape(Shape decoratedShape) {
        this.decoratedShape = decoratedShape;
    }

    // 在装饰器中调用原始对象的方法,并添加新的功能
    public void draw() {
        decoratedShape.draw();
        System.out.println("Additional decoration function.");
    }
}

我们有一个 Shape 接口和实现了 Shape 接口的实体类。然后我们创建了一个抽象的装饰器类 ShapeDecorator,并扩展了 Shape 接口,此装饰器类在被装饰类上进行了一层包装,以增加新的功能。

八、观察者模式

观察者模式是一种行为型设计模式,定义对象间的一种一对多的依赖关系,使得当一个对象的状态发生改变时,其相关依赖对象皆会得到通知并被自动更新。观察者模式属于行为型模式。

应用场景:当一个对象的改变需要同时改变其他对象,并且它不知道具体有多少对象需要改变,就可以考虑使用观察者模式。
示例代码:

// 定义Subject,持有观察者的列表,并提供attach和notifyAllObservers方法
public class Subject {
    private List<Observer> observers = new ArrayList<Observer>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void notifyAllObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

// 定义Observer,声明更新自己的抽象方法
public abstract class Observer {
    protected Subject subject;
    public abstract void update();
}

我们创建了 Subject 类、Observer 抽象类和扩展了 Observer 类的实体类。Subject 对象改变状态时,所有依赖于它的对象都会得到通知,并自动更新。

九、策略模式

策略模式是一种行为型设计模式,它定义了一系列的算法,并将每一个算法封装起来,而且使它们可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

应用场景:一个系统有很多类的算法或者业务逻辑,可以把这些算法或者业务逻辑封装在同一个接口的不同的实现类中,减少使用多重转移语句(if…else if…else)。

示例代码:

// 定义策略接口,声明算法方法
public interface Strategy {
    public int doOperation(int num1, int num2);
}

// 定义具体策略类
public class OperationAdd implements Strategy {


    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

// 环境类,持有一个策略类的引用
public class StrategyContext {
    private Strategy strategy;

    public StrategyContext(Strategy strategy) {
        this.strategy = strategy;
    }

    // 使用策略的方法
    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

我们定义了一个策略接口 Strategy 和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。Context 对象使用了一些策略对象,这个策略对象改变了 Context 对象的执行算法。

十、命令模式

命令模式是一种行为型设计模式,它通过在对象之间引入级别,使得对象之间的依赖关系变得更加简单,并且降低了组合和调用的复杂性。这种模式涉及到五个组件:Client、Invoker、Command、ConcreteCommand、Receiver。

应用场景:当需要为请求调用者与请求接收者解耦时,命令模式使得调用者与接收者不直接交互,并且调用者无需知道接收者的接口。命令模式可以用于有多个触发者或接收者,需要向多个对象发出请求。

示例代码:

// 命令接口,声明执行方法
public interface Order {
    void execute();
}

// 具体命令类,实现Order接口的execute方法,调用接收者的方法
public class StockRequest implements Order {
    private Stock stock;

    public StockRequest(Stock stock) {
        this.stock = stock;
    }

    public void execute() {
        stock.buy();
    }
}

// 调用者类,接收命令并执行
public class Broker {
    private List<Order> orderList = new ArrayList<Order>();

    public void takeOrder(Order order) {
        orderList.add(order);
    }

    public void placeOrders() {
        for (Order order : orderList) {
            order.execute();
        }
        orderList.clear();
    }
}

我们创建了一个请求类 Stock,一个命令接口 Order 和实现了 Order 接口的实体命令类。命令实现类持有一个对请求的引用,并执行这个请求。Broker 对象使用命令对象和队列来执行请求。

结语

在这篇博客中,我们研究了10种常用的Java设计模式,希望这些知识能帮助你在日常开发或面试中更好地解决问题。记住,设计模式只是工具。在实际项目中,我们应该根据项目需求和具体情况选择合适的设计模式,而不是强行套用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java设计模式:深入解析与应用示例 的相关文章

  • Java 中等效的并行扩展

    我在 Net 开发中使用并行扩展有一些经验 但我正在考虑在 Java 中做一些工作 这些工作将受益于易于使用的并行库 JVM 是否提供任何与并行扩展类似的工具 您应该熟悉java util concurrent http java sun
  • 如何为最终用户方便地启动Java GUI程序

    用户想要从以下位置启动 Java GUI 应用程序Windows 以及一些额外的 JVM 参数 例如 javaw Djava util logging config file logging properties jar MyGUI jar
  • Java new Date() 打印

    刚刚学习 Java 我知道这可能听起来很愚蠢 但我不得不问 System out print new Date 我知道参数中的任何内容都会转换为字符串 最终值是 new Date 返回对 Date 对象的引用 那么它是如何打印这个的呢 Mo
  • 如何在 Play java 中创建数据库线程池并使用该池进行数据库查询

    我目前正在使用 play java 并使用默认线程池进行数据库查询 但了解使用数据库线程池进行数据库查询可以使我的系统更加高效 目前我的代码是 import play libs Akka import scala concurrent Ex
  • 在画布上绘图

    我正在编写一个 Android 应用程序 它可以在视图的 onDraw 事件上直接绘制到画布上 我正在绘制一些涉及单独绘制每个像素的东西 为此我使用类似的东西 for int x 0 x lt xMax x for int y 0 y lt
  • Java - 将节点添加到列表的末尾?

    这是我所拥有的 public class Node Object data Node next Node Object data Node next this data data this next next public Object g
  • 使用 Android 发送 HTTP Post 请求

    我一直在尝试从 SO 和其他网站上的大量示例中学习 但我无法弄清楚为什么我编写的示例不起作用 我正在构建一个小型概念验证应用程序 它可以识别语音并将其 文本 作为 POST 请求发送到 node js 服务器 我已确认语音识别有效 并且服务
  • Final字段的线程安全

    假设我有一个 JavaBeanUser这是从另一个线程更新的 如下所示 public class A private final User user public A User user this user user public void
  • Spark 1.3.1 上的 Apache Phoenix(4.3.1 和 4.4.0-HBase-0.98)ClassNotFoundException

    我正在尝试通过 Spark 连接到 Phoenix 并且在通过 JDBC 驱动程序打开连接时不断收到以下异常 为简洁起见 下面是完整的堆栈跟踪 Caused by java lang ClassNotFoundException org a
  • 列出jshell中所有活动的方法

    是否有任何命令可以打印当前 jshell 会话中所有新创建的方法 类似的东西 list但仅适用于方法 您正在寻找命令 methods all 它会打印所有方法 包括启动 JShell 时添加的方法 以及失败 被覆盖或删除的方法 对于您声明的
  • Spring Data JPA 应用排序、分页以及 where 子句

    我目前正在使用 Spring JPA 并利用此处所述的排序和分页 如何通过Spring data JPA通过排序和可分页查询数据 https stackoverflow com questions 10527124 how to query
  • 如何为俚语和表情符号构建正则表达式 (regex)

    我需要构建一个正则表达式来匹配俚语 即 lol lmao imo 等 和表情符号 即 P 等 我按照以下示例进行操作http www coderanch com t 497238 java java Regular Expression D
  • JRE 系统库 [WebSphere v6.1 JRE](未绑定)

    将项目导入 Eclipse 后 我的构建路径中出现以下错误 JRE System Library WebSphere v6 1 JRE unbound 谁知道怎么修它 右键单击项目 特性 gt Java 构建路径 gt 图书馆 gt JRE
  • 加密 JBoss 配置中的敏感信息

    JBoss 中的标准数据源配置要求数据库用户的用户名和密码位于 xxx ds xml 文件中 如果我将数据源定义为 c3p0 mbean 我会遇到同样的问题 是否有标准方法来加密用户和密码 保存密钥的好地方是什么 这当然也与 tomcat
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • Java执行器服务线程池[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 如果我使用 Executor 框架在
  • Google App Engine 如何预编译 Java?

    App Engine 对应用程序的 Java 字节码使用 预编译 过程 以增强应用程序在 Java 运行时环境中的性能 预编译代码的功能与原始字节码相同 有没有详细的信息这是做什么的 我在一个中找到了这个谷歌群组消息 http groups
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 当我从 Netbeans 创建 Derby 数据库时,它存储在哪里?

    当我从 netbeans 创建 Derby 数据库时 它存储在哪里 如何将它与项目的其余部分合并到一个文件夹中 右键单击Databases gt JavaDB in the Service查看并选择Properties This will
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List

随机推荐

  • Vue报错Custom elements in iteration require 'v-bind:key' directives."错误解决

    错误代码
  • 造一台机器人需要哪些技能?

    转自 帐号已迁移 大家好 我是小王 是一名刚刚加入机器人队的大学生 有人说搞机器人特别简单 只要画画图 拧拧螺丝 敲敲代码就可以了 He tui 要这么容易 每年能有上百支机器人队伍 花十几万挤破了头都抢不到30万奖金吗 不过 团队有了我这
  • Osmosis 0.46详细使用说明

    原文地址 http wiki openstreetmap org wiki Osmosis Detailed Usage 0 46 全局选项 Short Option Long Option Description v verbose 需要
  • 图神经网络基础(part 1)

    文章目录 一 图的基本概念 二 简易图谱论 2 1 拉普拉斯矩阵 2 2 拉普拉斯二次型 2 3 拉普拉斯矩阵与图扩散 2 4 图论傅里叶变换 一 图的基本概念 对于接触过数据结构和算法的人来说 图并不是一个陌生的概念 一个图 Graph
  • 解释器-架构案例2021(三十一)

    软件架构设计与评估 某公司支持用户使用浏览器在线进行基于机器学习的智能应用开发活动 该平台核心应用场景是用户拖拉拽算法组件灵活定义机器学习流程 采用自助方式智能应用设计 实现与部署 并开发新算法加入平台 a 平台用户分为算法工程师 软件工程
  • markdown使用手册

    目录 记录 04 学会用SLA评估系统 功能快捷键 合理的创建标题 有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 创建一个表格 设定内容居中 居左 居右 SmartyPants 创建一
  • 前端上传组件Plupload使用指南 与swfupload一样强大

    http blog csdn net z69183787 article details 46698741 Plupload 是一款由著名的web编辑器 TinyMCE 团队开发的上传组件 简单易用且功能强大 我们完全可以使用Pluploa
  • 静态资源加载不到解决办法

    这是我总结的几种解决办法 可以自行尝试实在不行都加上 第一种在springmvc配置文件当中加入资源映射
  • 选课微信小程序开发 java

    1 使用技术 java springboot mysql hibernate 微信小程序 2 功能介绍 后台管理 学生管理 课程管理 类型管理 选课管理 操作中心 管理员中心 导入导出学生 微信小程序端 学生端 查看课程 选课 查看选课课程
  • 网络爬虫是怎么运行的

    2 2 1知识概述 网络爬虫究竟是怎么运行的 单个页面是如何运行的 1 指定一个url 2 使用技术发送get请求 3 获得服务端的响应 4 将二进制的数据 转化成HTML文档 网络爬虫一般会爬取很多很多很多的页面 for 1 指定一个ur
  • 如何在TheBrain中为Plex选择自定义图像并更改默认字体?

    TheBrain 点击下载 您的终极数字记忆和无限思维导图软件 我们从一个想法跳到另一个想法 构建越来越复杂的网络 直到新想法形成 TheBrain允许你以同样的方式组织你的信息 而不限制你预先确定的文件结构 事实上 你的数字大脑是没有限制
  • Unix/Linux编程:文件树遍历-----nftw()

    nftw 函数是对执行类似功能的老函数 ftw 的加强 由于提供了更多功能 对符号链接的处理也更易于把握 GNU C 语言函数库也提供了派生自 BSD 分支的 fts API fts open fts read fts children f
  • telnet配置及用法

    内容转载 http jingyan baidu com article ae97a646b22fb6bbfd461d19 html 搭建或配置网络环境时 经常会使用ping命令检查网络是否可达 有些时候Ping命令也不好使 比如因防火墙禁止
  • 中国半导体硅片行业未来发展趋势与销售规模预测报告2022版

    中国半导体硅片行业未来发展趋势与销售规模预测报告2022版 HS HS HS HS HS HS HS HS HS HS HS HS 修订日期 2021年11月 搜索鸿晟信合研究院查看官网更多内容 第一章 半导体硅片相关概述 1 1 半导体硅
  • C语言向上取整数简单写法

    C语言整数除法向上取整 define GET M N M 1 N 1 实例 一共100字节的数据 一次发送11字节 需要发送多少次 GET 100 16 7 次
  • 记录一道leetcode算法题的坑

    题目链接 这个题并不难 只是想让自己牢记一句老话 题目里面的每个条件都有用 int twoSum int nums int numsSize int target int returnSize int a 2 0 注意 注意 注意 retu
  • Mathematica 随机打乱列表顺序/列表随机重排列

    更新 找到相关函数了 没仔细看说明以为RandomSample只能随机取样来着 汗 RandomSample list 原内容 没必要看了 没有找到直接的相关函数 想到的方法是随机交换列表元素 例如以下程序为1 10的数字乱序 Permut
  • IP数据包长度问题总结

    首先要看TCP IP协议 涉及到四层 链路层 网络层 传输层 应用层 其中以太网 Ethernet 的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据 Data 在应用层 它们的关系是 数据帧 IP包 TCP
  • TypeError: write() argument must be str, not bytes报错原因及Python3写入二进制文件方法

    Python2随机写入二进制文件 with open python2 random bin w as f f write os urandom 10 但使用Python3会报错 TypeError must be str not bytes
  • Java设计模式:深入解析与应用示例

    文章目录 引言 一 单例模式 二 工厂模式 三 抽象工厂模式 四 建造者模式 五 原型模式 六 适配器模式 七 装饰器模式 八 观察者模式 九 策略模式 十 命令模式 结语 引言 设计模式是一种在特定上下文中反复出现的可重用解决方案 用于处