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

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

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

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 代码。

相关推荐

威信Chronosonic XVX全新旗舰全球首发 设计特点彻底公开

第一眼看到WilsonAudio新推出的ChronosonicXVX音箱,相信大家都会直觉认为它是两年前超级旗舰WAMMMasterChronosonic的缩小版,不过这个推测并不完全正确。C...

C#高精度Timer和Delay以及时间测量

在PCHMI7.0后在工具箱里会多一个MsTimer,以及Delay和Microsecond两个类。...

python教程从基础到精通,第9课—日期与时间

Hello,小伙伴们,祝大家五.一玩得快乐!刚学习完了七大数据类型,今天咱们来学习日期与时间的表示方法。Python标准库中提供了时间和日期的支持:calendar:日历相关;time、datetim...

软件测试|教你轻松玩转Python日期时间

Python基础之日期时间处理...

Go语言中互斥锁与读写锁,你知多少?

简述Golang中的锁机制主要包含互斥锁和读写锁互斥锁互斥锁是传统并发程序对共享资源进行控制访问的主要手段。在Go中主要使用sync.Mutex的结构体表示。一个简单的示例:funcmutex()...

变形金刚动画大电影——经典台词赏析

YOURDAYSARENUMBEREDNOW,DECEPTI-CREEPS你们活不了多久了,霸天虎小子。-{铁皮说的话,体现了铁皮的嫉恶如仇,可是后来铁皮在飞船上遇袭身亡,可谓是出师未捷身先...

Python时间日期模块使用教程(python3日期)

1.时间日期处理概述在日常编程中,时间日期处理是非常常见的需求,比如:记录日志时间...

亚马逊介绍AWS“无服务器”云服务改进:数据库可线上扩充容量等

IT之家11月29日消息,在今天于美国拉斯维加斯展开的亚马逊“AWSre:Invent2023”活动中,亚马逊计算部门资深副总裁PeterDeSantis,介绍了旗下三款云端服务,IT...

2.日期格式 datetime(日期时间显示格式)

fromdatetimeimportdatetime1.获取当前日期和时间now=datetime.now()#2025-05-3110:56:01.4687822.格式化日期...

【科普】时间单位大盘点(时间单位都有哪些?)

时间单位,是7种基本单位之一,长度、时间、质量、物质的量、光照度、电流和(热力学)温度是七种基本单位。本词条中时间单位以时间从大到小列。今天我们来盘点下时间的单位换算...

基于PHP的Laravel框架,盘点Github高星Web管理后台,效率为王!

在Web开发工作中,选择一个高效、稳定的后台管理系统是提高开发效率的关键。虽然PHP在近些年中的热度有所减退,但其上手简单、开源、灵活且被广泛应用的特点,仍然使其在编程语言排行榜中保持前十的位置。这表...

如何使用PHP编写一个简单的留言板?

留言板是一个常见的Web应用程序,允许用户在网站上发布和查看留言。在本文中,我们将使用PHP编写一个简单的留言板,介绍构建过程中的关键步骤和技巧。一、准备工作在开始编写留言板之前,我们需要准备好以下工...

产品经理提需求时要考虑的 15 个隐性需求

虽然世界充满未知的变化,但是有一些大的方向还是可以把握的,本文跟大家谈谈产品经理提需求时要考虑的15个隐性需求,enjoy~俗话说,计划赶不上变化快,无论需求文档做得如何细致,考虑得如何周全,总会...

关于 PHP 启动 MongoDb 找不到指定模块问题

前言:最近有一个小demo,需要通过PHP将用户行为记录储存到MongoDB,再用Spark做协同过滤。由于以前处理跨语言交互是通过消息中间件,这次本地使用MongoDB却弄出了几个问...

PHP程序员老鸟面试经历(php程序员怎么样)

在任何时代找任何工作都有面试这么一说的。特别是高端技术类的工种对技术理论和技术实操能力要求很严格。大部分公司招收技术员工的要求也越来愈高。至于PHP程序员也是如此,我估计大多数PHP老鸟已经不在意所...