百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

解锁Java设计模式:代码的优雅魔法

wptr33 2025-02-21 15:39 32 浏览

Java 设计模式是什么?

在 Java 编程的世界里,设计模式就像是隐藏在代码背后的神秘力量,默默塑造着程序的架构和行为。那么,Java 设计模式究竟是什么呢?

简单来说,设计模式是软件开发过程中针对常见问题总结出的可复用解决方案。它就像一套经过精心打磨的模板,帮助开发者更高效、更优雅地解决各种编程难题。在 Java 开发中,这些模式的应用无处不在,它们是代码质量和可维护性的重要保障。

设计模式的重要性不言而喻。它不仅能提高代码的可读性和可维护性,让其他开发者更容易理解你的代码逻辑,还能促进代码的复用,减少重复劳动,提高开发效率。同时,设计模式有助于构建灵活、可扩展的系统架构,使程序能够更好地应对需求的变化和业务的发展。

在接下来的内容中,我将带大家深入了解几种 Java 中常用的设计模式,揭开它们神秘的面纱,看看它们是如何在实际开发中发挥强大作用的。

常用设计模式大揭秘

单例模式:独一无二的存在

单例模式,确保一个类在系统中只有一个实例,并提供一个全局访问点来访问这个实例。就好比皇帝,整个国家只有一个,所有人都可以通过特定的方式(如圣旨、朝见等)来 “访问” 皇帝。

在 Java 中,单例模式有多种实现方式,常见的有饿汉式和懒汉式。

饿汉式实现比较简单,在类加载时就完成实例化,确保了线程安全。示例代码如下:

public class Singleton {

// 私有静态常量,在类加载时就实例化

private static final Singleton INSTANCE = new Singleton();

// 私有构造函数,防止外部实例化

private Singleton() {}

// 提供全局访问点

public static Singleton getInstance() {

return INSTANCE;

}

}

懒汉式则是在第一次使用时才进行实例化,实现了延迟加载,但在多线程环境下需要注意线程安全问题。示例代码如下:

public class Singleton {

// 私有静态变量,初始化为null

private static Singleton instance;

// 私有构造函数,防止外部实例化

private Singleton() {}

// 提供全局访问点,线程不安全版本

public static Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

为了解决懒汉式的线程安全问题,可以使用双重检查锁定(Double-Checked Locking)或静态内部类的方式。

单例模式的应用场景非常广泛,比如数据库连接池、线程池、日志对象等。在这些场景中,使用单例模式可以避免资源的重复创建和浪费,提高系统的性能和稳定性。但它也有一些缺点,例如单例类的职责过重,可能会违背单一职责原则;在多线程环境下实现复杂,需要考虑线程安全问题等。

工厂模式:对象的智能工厂

工厂模式,定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。简单来说,就像一个工厂,你告诉它你需要什么产品,它就会生产出相应的产品给你,而你不需要知道产品是如何生产出来的。

工厂模式主要有三种类型:简单工厂、工厂方法和抽象工厂。

简单工厂模式是工厂模式的基础,它通过一个工厂类根据传入的参数来返回不同的实例化对象。例如,我们有一个生产水果的简单工厂:

// 水果接口

interface Fruit {

void eat();

}

// 苹果类

class Apple implements Fruit {

@Override

public void eat() {

System.out.println("吃苹果");

}

}

// 香蕉类

class Banana implements Fruit {

@Override

public void eat() {

System.out.println("吃香蕉");

}

}

// 简单工厂类

class FruitFactory {

public static Fruit createFruit(String type) {

if ("apple".equals(type)) {

return new Apple();

} else if ("banana".equals(type)) {

return new Banana();

}

return null;

}

}

工厂方法模式将对象的创建过程封装在工厂接口中,具体的创建步骤由具体的工厂类实现。每个具体工厂类只负责创建特定的产品对象。例如,我们有一个生产披萨的工厂方法模式:

// 披萨接口

interface Pizza {

void prepare();

void bake();

void cut();

void box();

}

// 芝士披萨类

class CheesePizza implements Pizza {

@Override

public void prepare() {

System.out.println("准备芝士披萨的材料");

}

@Override

public void bake() {

System.out.println("烘烤芝士披萨");

}

@Override

public void cut() {

System.out.println("切割芝士披萨");

}

@Override

public void box() {

System.out.println("包装芝士披萨");

}

}

// 意大利辣香肠披萨类

class PepperoniPizza implements Pizza {

@Override

public void prepare() {

System.out.println("准备意大利辣香肠披萨的材料");

}

@Override

public void bake() {

System.out.println("烘烤意大利辣香肠披萨");

}

@Override

public void cut() {

System.out.println("切割意大利辣香肠披萨");

}

@Override

public void box() {

System.out.println("包装意大利辣香肠披萨");

}

}

// 披萨工厂接口

interface PizzaFactory {

Pizza createPizza();

}

// 芝士披萨工厂类

class CheesePizzaFactory implements PizzaFactory {

@Override

public Pizza createPizza() {

return new CheesePizza();

}

}

// 意大利辣香肠披萨工厂类

class PepperoniPizzaFactory implements PizzaFactory {

@Override

public Pizza createPizza() {

return new PepperoniPizza();

}

}

抽象工厂模式则是工厂方法模式的扩展,它通过抽象工厂类来定义一组相关或依赖的工厂接口,具体的工厂类实现这些接口,并根据不同的需求来生产不同的产品。例如,我们有一个生产电脑和手机的抽象工厂:

// 电脑接口

interface Computer {

void use();

}

// 戴尔电脑类

class DellComputer implements Computer {

@Override

public void use() {

System.out.println("使用戴尔电脑");

}

}

// 联想电脑类

class LenovoComputer implements Computer {

@Override

public void use() {

System.out.println("使用联想电脑");

}

}

// 手机接口

interface Phone {

void call();

}

// 苹果手机类

class ApplePhone implements Phone {

@Override

public void call() {

System.out.println("使用苹果手机打电话");

}

}

// 华为手机类

class HuaweiPhone implements Phone {

@Override

public void call() {

System.out.println("使用华为手机打电话");

}

}

// 抽象电脑工厂接口

interface ComputerFactory {

Computer createComputer();

}

// 抽象手机工厂接口

interface PhoneFactory {

Phone createPhone();

}

// 戴尔工厂类,实现生产戴尔电脑和苹果手机

class DellFactory implements ComputerFactory, PhoneFactory {

@Override

public Computer createComputer() {

return new DellComputer();

}

@Override

public Phone createPhone() {

return new ApplePhone();

}

}

// 联想工厂类,实现生产联想电脑和华为手机

class LenovoFactory implements ComputerFactory, PhoneFactory {

@Override

public Computer createComputer() {

return new LenovoComputer();

}

@Override

public Phone createPhone() {

return new HuaweiPhone();

}

}

工厂模式在实际开发中应用广泛,特别是在对象的创建和管理方面。它将对象的创建和使用分离,提高了代码的可维护性和可扩展性。当需要新增一种产品时,只需要新增一个具体的产品类和对应的工厂类,而无需修改客户端代码。

代理模式:幕后的代理人

代理模式,为其他对象提供一种代理以控制对这个对象的访问。就像明星有经纪人,经纪人作为明星的代理,控制着外界对明星的访问,比如安排工作、筛选活动等,而明星可以专注于自己的演艺事业。

代理模式分为静态代理和动态代理。

静态代理在编译时就已经确定代理类和被代理类的关系,代理类和被代理类都实现相同的接口。例如,我们有一个文件上传的接口,使用静态代理在上传文件前后记录日志:

// 文件上传接口

interface FileUploader {

void upload(String file);

}

// 真实上传类

class RealFileUploader implements FileUploader {

@Override

public void upload(String file) {

// 实际的文件上传逻辑

System.out.println("上传文件: " + file);

}

}

// 代理类

class ProxyFileUploader implements FileUploader {

private FileUploader realFileUploader;

public ProxyFileUploader(FileUploader realFileUploader) {

this.realFileUploader = realFileUploader;

}

@Override

public void upload(String file) {

// 额外的处理,比如记录日志

System.out.println("上传前: " + file);

// 调用真实主题的上传方法

realFileUploader.upload(file);

// 额外的处理,比如记录日志

System.out.println("上传后: " + file);

}

}

动态代理则是在运行时根据我们的指示动态生成代理类,更加灵活,可以代理任意的接口类型。动态代理主要使用java.lang.reflect.Proxy类和
java.lang.reflect.InvocationHandler接口来实现。例如,还是以上述文件上传接口为例,使用动态代理实现日志记录:

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

// 文件上传接口

interface FileUploader {

void upload(String file);

}

// 真实上传类

class RealFileUploader implements FileUploader {

@Override

public void upload(String file) {

// 实际的文件上传逻辑

System.out.println("上传文件: " + file);

}

}

// 动态代理处理器类

class DynamicProxyHandler implements InvocationHandler {

private Object realObject;

public DynamicProxyHandler(Object realObject) {

this.realObject = realObject;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// 额外的处理,比如记录日志

System.out.println("调用前: " + method.getName());

// 调用真实主题的对应方法

Object result = method.invoke(realObject, args);

// 额外的处理,比如记录日志

System.out.println("调用后: " + method.getName());

return result;

}

}

// 使用动态代理

class Main {

public static void main(String[] args) {

// 创建真实主题

FileUploader realFileUploader = new RealFileUploader();

// 创建动态代理的处理器

InvocationHandler handler = new DynamicProxyHandler(realFileUploader);

// 创建代理类

FileUploader proxyFileUploader = (FileUploader) Proxy.newProxyInstance(

FileUploader.class.getClassLoader(),

new Class[]{FileUploader.class},

handler

);

// 通过动态代理进行文件上传

proxyFileUploader.upload("example.txt");

}

}

代理模式在实际应用中常用于远程调用、权限控制、日志记录、事务管理等场景。通过代理模式,可以在不修改目标对象的基础上,对其功能进行扩展和增强,同时也提高了代码的可维护性和可扩展性。

观察者模式:消息的传播网络

观察者模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知并自动更新。就像你关注了一个公众号,当公众号发布新文章时,所有关注它的用户都会收到通知。

在观察者模式中,主要有两个角色:主题(Subject)和观察者(Observer)。主题负责管理观察者,并在状态发生变化时通知观察者;观察者则实现一个更新接口,用于接收主题的通知并进行相应的处理。

例如,我们有一个简单的股票行情监测系统,使用观察者模式实现当股票价格发生变化时通知关注该股票的观察者:

import java.util.ArrayList;

import java.util.List;

// 观察者接口

interface Observer {

void update(String stockName, double newPrice);

}

// 主题接口

interface Subject {

void registerObserver(Observer observer);

void removeObserver(Observer observer);

void notifyObservers(String stockName, double newPrice);

}

// 具体主题类

class Stock implements Subject {

private List observers = new ArrayList<>();

private String stockName;

private double price;

public Stock(String stockName, double price) {

this.stockName = stockName;

this.price = price;

}

@Override

public void registerObserver(Observer observer) {

observers.add(observer);

}

@Override

public void removeObserver(Observer observer) {

observers.remove(observer);

}

@Override

public void notifyObservers(String stockName, double newPrice) {

for (Observer observer : observers) {

observer.update(stockName, newPrice);

}

}

// 更新股票价格,并通知观察者

public void setPrice(double newPrice) {

this.price = newPrice;

notifyObservers(stockName, newPrice);

}

}

// 具体观察者类

class Investor implements Observer {

private String name;

public Investor(String name) {

this.name = name;

}

@Override

public void update(String stockName, double newPrice) {

System.out.println(name + " 关注的 " + stockName + " 股票价格更新为: " + newPrice);

}

}

观察者模式在实际开发中应用广泛,特别是在消息通知、事件驱动系统、图形界面开发等场景。它实现了对象之间的解耦,使得主题和观察者可以独立地变化和扩展,提高了系统的灵活性和可维护性。

策略模式:灵活的算法选择

策略模式,定义了一系列算法,并将每种算法封装起来,使得它们可以相互替换。策略模式让算法的变化不会影响到使用算法的客户。就像出行时,你可以选择不同的交通方式(如步行、骑自行车、坐公交车、打车等),每种交通方式就是一种策略,你可以根据自己的需求和情况灵活选择。

在策略模式中,主要有三个角色:上下文(Context)、策略接口(Strategy)和具体策略类(ConcreteStrategy)。上下文持有一个策略对象的引用,并可以通过调用策略对象的方法来执行策略;策略接口定义了算法的接口,上下文使用这个接口来调用具体的算法;具体策略类实现了策略接口,定义了具体的算法或行为。

例如,我们有一个电商系统,用户可以选择不同的支付方式来完成订单支付,使用策略模式实现支付方式的灵活切换:

// 支付策略接口

interface PaymentStrategy {

void pay(double amount);

}

// 信用卡支付策略类

class CreditCardPayment implements PaymentStrategy {

private String cardNumber;

public CreditCardPayment(String cardNumber) {

this.cardNumber = cardNumber;

}

@Override

public void pay(double amount) {

System.out.println("使用信用卡支付 " + amount + " 元,卡号: " + cardNumber);

}

}

// 支付宝支付策略类

class AlipayPayment implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用支付宝支付 " + amount + " 元");

}

}

// 微信支付策略类

class WechatPayPayment implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用微信支付 " + amount + " 元");

}

}

// 购物车类,上下文

class ShoppingCart {

private PaymentStrategy paymentStrategy;

public void setPaymentStrategy(PaymentStrategy paymentStrategy) {

this.paymentStrategy = paymentStrategy;

}

public void checkout(double amount) {

if (paymentStrategy == null) {

throw new IllegalStateException("未设置支付策略");

}

paymentStrategy.pay(amount);

}

}

策略模式在实际开发中常用于不同算法的切换、行为的动态改变等场景。它将算法的实现和使用分离,使得代码更加灵活和可维护。当需要新增一种算法时,只需要新增一个具体策略类,而无需修改上下文和其他已有的策略类,符合开闭原则。

设计模式的选择与应用

在实际的 Java 开发中,选择合适的设计模式是一项关键技能。不同的设计模式适用于不同的场景,就像工具箱里的各种工具,每个都有其独特的用途。

选择设计模式时,首先要深入理解项目的需求和目标。如果项目中需要创建对象,且创建过程较为复杂,或者需要根据不同条件创建不同类型的对象,那么工厂模式可能是个不错的选择;要是想实现对象之间的解耦,当一个对象的状态变化时能自动通知其他依赖它的对象,观察者模式则更为合适。

同时,也要考虑系统的可扩展性和维护性。例如,使用开闭原则,尽量选择对扩展开放、对修改关闭的设计模式,这样在系统需要添加新功能时,能通过扩展代码来实现,而不是大规模地修改现有代码,从而降低维护成本和风险。

此外,团队成员对设计模式的熟悉程度也会影响设计模式的选择。如果团队成员对某种设计模式有丰富的经验和深入的理解,那么在项目中使用该模式可能会更加顺利,开发效率也会更高。

在应用设计模式时,要避免过度设计。不是所有的项目都需要使用复杂的设计模式,对于一些简单的场景,直接编写简洁明了的代码可能是更好的选择。设计模式是为了解决实际问题,提高代码质量和开发效率,而不是为了使用模式而使用模式。

希望大家能够在实际项目中不断实践和总结,将这些设计模式运用得得心应手,编写出更加优雅、高效、可维护的 Java 代码。

相关推荐

MySQL进阶五之自动读写分离mysql-proxy

自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...

Postgres vs MySQL_vs2022连接mysql数据库

...

3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?

引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...

一文由浅入深带你完全掌握MySQL的锁机制原理与应用

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...

验证Mysql中联合索引的最左匹配原则

后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...

MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)

目录1.索引基础...

你会看 MySQL 的执行计划(EXPLAIN)吗?

SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...

MySQL 从入门到精通(四)之索引结构

索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...

mysql总结——面试中最常问到的知识点

mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...

mysql总结——面试中最常问到的知识点(2)

首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...

MySQL基础全知全解!超详细无废话!轻松上手~

本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...

深入剖析 MySQL 中的锁机制原理_mysql 锁详解

在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...

Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析

引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...

MySQL基础篇:DQL数据查询操作_mysql 查

一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...

MySql:索引的基本使用_mysql索引的使用和原理

一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...