Java进阶--设计模式
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样,项目中合理地运用设计模式可以完美地解决很多问题。
设计模式多种多样,这里参考设计模式简介,本篇主要介绍最常用的几种。
设计模式的分类
分类 | 核心目标 | 典型模式 |
---|---|---|
创建型模式 | 对象创建过程的抽象与优化 | 工厂模式、抽象工厂、单例、建造者、原型 |
结构型模式 | 对象与类的组织方式(组合结构优化) | 适配器、代理、装饰者、桥接、组合、外观、享元 |
行为型模式 | 对象间的交互与职责分配(通信流程优化) | 策略、观察者、责任链、模板方法、命令、状态、迭代器、中介者、备忘录、访问者 |
常用的设计模式
单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,其核心目标是确保一个类仅有一个实例,并提供该实例的全局访问点。
特点:1.单例类只有一个实例对象。
2.单例对象必须由单例类创建。
3.对外提供一个访问该单例的全局访问点
核心实现方式
1. 饿汉式
-
特点:类加载时立即创建实例,线程安全但可能造成资源浪费。
public class EagerSingleton {// 类加载时初始化实例private static final EagerSingleton instance = new EagerSingleton();// 私有构造方法,防止外部实例化private EagerSingleton() {}public static EagerSingleton getInstance() {return instance;} }
2. 懒汉式
-
特点:延迟实例化,首次调用时创建对象,需处理多线程安全问题。
基础版(非线程安全):
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
同步方法版(线程安全但性能低):
public class SynchronizedSingleton {private static SynchronizedSingleton instance;private SynchronizedSingleton() {}public static synchronized SynchronizedSingleton getInstance() {if (instance == null) {instance = new SynchronizedSingleton();}return instance;}
}
3. 双重检查锁(Double-Checked Locking)
-
特点:延迟加载 + 线程安全 + 高性能,适用于多线程环境。
public class DCLSingleton {// 使用 volatile 防止指令重排序private static volatile DCLSingleton instance;private DCLSingleton() {}public static DCLSingleton getInstance() {if (instance == null) { // 第一次检查synchronized (DCLSingleton.class) { // 同步块if (instance == null) { // 第二次检查instance = new DCLSingleton();}}}return instance;}
}
举例:Runtime类
public class Runtime {private static Runtime currentRuntime = new Runtime();public static Runtime getRuntime() {return currentRuntime;}private Runtime() {}
}
jdk中提供的类,标准的单例模式应用。
工厂模式
工厂模式(Factory Pattern)是一种创建型设计模式,其核心思想是将对象的创建逻辑与使用逻辑分离,通过统一的工厂接口或类来实例化对象,从而降低代码耦合度并提升扩展性。
一、简单工厂模式
定义
-
核心:通过一个工厂类,根据传入的参数决定创建哪种具体产品对象。
-
适用场景:产品种类较少且创建逻辑简单。
// 1. 定义产品接口
interface Car {void drive();
}// 2. 具体产品实现
class SedanCar implements Car {@Overridepublic void drive() {System.out.println("驾驶轿车");}
}class SUVCar implements Car {@Overridepublic void drive() {System.out.println("驾驶SUV");}
}// 3. 简单工厂类
class CarFactory {public static Car createCar(String type) {switch (type.toLowerCase()) {case "sedan":return new SedanCar();case "suv":return new SUVCar();default:throw new IllegalArgumentException("未知的汽车类型");}}
}// 4. 使用示例
public class Client {public static void main(String[] args) {Car sedan = CarFactory.createCar("sedan");sedan.drive(); // 输出:驾驶轿车Car suv = CarFactory.createCar("suv");suv.drive(); // 输出:驾驶SUV}
}
二、工厂方法模式
定义
-
核心:定义抽象工厂接口,由子类决定具体实例化哪个类。每个产品对应一个工厂。
public interface Car {void run();
}public class Aodi implements Car {@Overridepublic void run() {System.out.println("奥迪汽车行驶");}
}public class Bmw implements Car {@Overridepublic void run() {System.out.println("宝马汽车行驶");}
}public interface CarFactory {Car createCar();
}public class AodiFactory implements CarFactory{@Overridepublic Car createCar() {return new Aodi();}
}public class BmwFactory implements CarFactory{@Overridepublic Car createCar() {return new Bmw();}
}public class Test {public static void main(String[] args) {CarFactory aodicarFactory = new AodiFactory();Car aodi = aodicarFactory.createCar();aodi.run();CarFactory bmwcarFactory = new BmwFactory();Car bmw = bmwcarFactory.createCar();bmw.run();}
}
一个产品对应一个工厂,奥迪车对应奥迪工厂,宝马车对应宝马工厂。
三、抽象工厂模式
定义
-
核心:提供一个接口,用于创建相关或依赖对象的家族,而无需指定具体类。
public interface AbstractFactory {Car getCar();Phone getPhone();
}public interface Car {void run();
}public interface Phone {void call();
}public class AodiFactory implements AbstractFactory{@Overridepublic Car getCar() {return new AodiCar();}@Overridepublic Phone getPhone() {return new AodiPhone();}
}public class AodiCar implements Car{@Overridepublic void run() {System.out.println("奥迪汽车行驶");}
}public class AodiPhone implements Phone{@Overridepublic void call() {System.out.println("奥迪手机打电话");}
}public class BmwFactory implements AbstractFactory{@Overridepublic Car getCar() {return new BmwCar();}@Overridepublic Phone getPhone() {return new BmwPhone();}
}public class BmwCar implements Car{@Overridepublic void run() {System.out.println("宝马汽车行驶");}
}public class BmwPhone implements Phone {@Overridepublic void call() {System.out.println("宝马手机打电话");}
}public class Test {public static void main(String[] args) {AbstractFactory aodiFactory = new AodiFactory();Car aodiCar = aodiFactory.getCar();Phone aodiphone = aodiFactory.getPhone();aodiCar.run();aodiphone.call();AbstractFactory bmwFactory = new BmwFactory();Car bmwCar = bmwFactory.getCar();Phone bmwPhone = bmwFactory.getPhone();bmwCar.run();bmwPhone.call();}
}
一个接口对应一个家族或者一个系列的东西,在这个案例中,就是奥迪工厂对应奥迪手机,奥迪车等等,宝马抽象工厂对应宝马手机和宝马车。
三种工厂模式对比
模式 | 核心区别 | 适用场景 |
---|---|---|
简单工厂 | 一个工厂类创建所有产品 | 产品类型少,逻辑简单 |
工厂方法 | 每个产品对应一个工厂类 | 需要灵活扩展产品类型 |
抽象工厂 | 创建多个相关产品组成的家族 | 需要保证产品族的兼容性 |
原型模式
原型模式(Prototype Pattern)是一种创建型设计模式,其核心思想是通过复制现有对象来创建新对象,而不是通过 new
关键字重新构造。
复制对象及其所有引用类型字段,创建完全独立的新对象。
class Address implements Cloneable {String city;public Address(String city) {this.city = city;}@Overridepublic Address clone() throws CloneNotSupportedException {return (Address) super.clone();}
}class User implements Cloneable {String name;Address address;public User(String name, Address address) {this.name = name;this.address = address;}// 深拷贝:递归复制引用类型字段@Overridepublic User clone() throws CloneNotSupportedException {User cloned = (User) super.clone();cloned.address = this.address.clone(); // 克隆Address对象return cloned;}
}// 使用示例
User user1 = new User("Alice", new Address("Beijing"));
User user2 = user1.clone();user2.address.city = "Shanghai";
System.out.println(user1.address.city); // 输出:Beijing(原对象未受影响)
优点 | 缺点 |
---|---|
提升性能,避免重复初始化 | 深拷贝实现复杂(需递归克隆所有引用对象) |
动态配置对象属性 | 需注意循环引用问题 |
绕过构造函数限制 | 对不支持克隆的类需额外处理(如实现接口) |
小结:原型模式通过复制现有对象高效创建新实例,尤其适用于对象初始化成本高或需要动态配置的场景。在实现时需注意 深拷贝与浅拷贝 的区别,避免因引用共享导致的数据不一致。合理使用原型模式,可以显著优化性能并简化复杂对象的创建逻辑。
代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,其核心思想是通过代理对象控制对原始对象的访问,在不修改原始类的前提下增强功能或限制访问。
代理模式的三种角色
-
抽象主题
定义真实主题和代理主题的公共接口 -
真实主题
实现业务逻辑的核心类 -
代理类
持有真实主题的引用,控制对真实主题的访问,并附加额外功能。
静态代理
手动编写代理类,代理类与真实类实现同一接口。
// 1. 抽象主题接口
interface UserService {void saveUser(String username);
}// 2. 真实主题
class UserServiceImpl implements UserService {public void saveUser(String username) {System.out.println("保存用户: " + username);}
}// 3. 静态代理类
class UserServiceProxy implements UserService {private UserService target;public UserServiceProxy(UserService target) {this.target = target;}public void saveUser(String username) {//通知System.out.println("[日志] 开始保存用户...");target.saveUser(username);//通知System.out.println("[日志] 用户保存完成");}
}// 使用示例
public class Client {public static void main(String[] args) {UserService realService = new UserServiceImpl();UserService proxy = new UserServiceProxy(realService);proxy.saveUser("Alice");}
}
一个代理类可以对某一类的目标提供代理
满足开闭原则(添加一类目标时,可以扩展添加一个新的代理类),
代码是写死的,不灵活
动态代理
jdk代理
创建一个代理对象生成器,实现InvocationHandler,重写invoke方法,这个方法会被代理对象动态调用,代理对象在运行时,被动态创建,可以代理任意的目标对象,提高灵活性。
注意被代理的目标对象,必须实现一个接口,在生成代理对象时,需要通过接口来获取目标对象信息。
底层实现原理利用反射机制。
/*抽象操作定义 卖东西*/
public interface Sell {void sell();
}//目标类
public class CarFactoryImpl implements Sell {@Overridepublic void sell() {System.out.println("汽车厂卖汽车");}}/*动态代理类代理类不需要实现与目标类相同的接口,这样就可以代理任意的目标类但是是有要求的,目标类必需实现接口,此种方式是动态代理的实现方式之一: jdk代理 是一种纯反射机制实现(动态获取目标类接口方法)*/
public class DynamicProxy implements InvocationHandler {Object object;//真实对象,接收任何的目标类对象public DynamicProxy(Object object) {this.object = object;}/*在代理类中调用目标类中的具体方法,动态的将代理动态对象,目标类中要调用的方法,及方法中的参数传递过来Method method 就是动态获取的真正要执行的方法*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("之前开启事务");method.invoke(object);System.out.println("之后提交事务");return proxy;}public Object getProxy(){return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);}
}public class Test {public static void main(String[] args) {CarFactoryImpl vip = new CarFactoryImpl();DynamicProxy dtproxy = new DynamicProxy(vip);//自己创建的代理类对象//这才是真正的创建动态代理对象 获取目标类所实现的接口Sell carfactory = (Sell)dtproxy.getProxy();carfactory.sell();//使用代理对象调用接口中的方法,获取当前调用的方法,最终调用invoke方法}
}
cglib代理
CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
public class CarFactoryImpl {public void sell() {System.out.println("汽车厂卖汽车");}}import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/** 动态代理类*/
public class CGLibProxy implements MethodInterceptor {private Enhancer enhancer = new Enhancer();public Object getProxy(Class<?> clazz){ enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } /** 拦截所有目标类方法的调用 * 参数: * obj 目标实例对象 * method 目标方法的反射对象 * args 方法的参数 * proxy 代理类的实例 */public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {//代理类调用父类的方法 System.out.println("开始事务"); Object result = proxy.invokeSuper(obj, args); System.out.println("关闭事务"); return result; }
}
public class Test {public static void main(String[] args) {CGLibProxy proxy = new CGLibProxy();CarFactoryImpl carFactory = (CarFactoryImpl) proxy.getProxy(CarFactoryImpl.class);carFactory.sell();}
}
要求目标类不能是final修饰,方法也不能是final修饰的,和static修饰的.
在spring框架中两种代理生成机制都实现了:
可以根据目标是否实现接口自动选择生成代理对象的方式,
默认采用cglib代理方式生成.
代理模式通过间接访问目标对象,实现了功能增强和访问控制,是解耦系统模块、提升灵活性的重要手段。选择代理类型时需注意:
-
静态代理:简单场景,代理类少。
-
JDK 动态代理:基于接口,适合代理多个方法。
-
CGLIB 代理:代理无接口的类,需注意性能与限制。
适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,用于将不兼容的接口转换为客户端期望的接口,使得原本无法协同工作的类能够一起协作。其核心思想是通过一个“中间层”(适配器)解决接口不匹配问题,类似于现实中的电源转接头。
适配器模式的核心角色
角色 | 说明 |
---|---|
目标接口(Target) | 客户端期望的接口(如 XmlParser ),定义客户端调用的标准方法。 |
适配者(Adaptee) | 已存在的、需要被适配的接口或类(如 JsonParser ),提供实际功能但接口不兼容。 |
适配器(Adapter) | 实现目标接口,并持有适配者的引用,通过转换逻辑调用适配者的方法。 |
-
特点:通过组合持有适配者对象,更灵活且符合合成复用原则。
-
适用场景:适配者与目标接口差异较大,或需适配多个适配者。
// 目标接口
public interface XmlParser {void parseXml(String xml);
}// 适配者类
public class JsonParser {public void parseJson(String json) {System.out.println("解析JSON: " + json);}
}// 对象适配器(持有适配者引用,实现目标接口)
public class JsonToXmlAdapter implements XmlParser {private JsonParser jsonParser;public JsonToXmlAdapter(JsonParser jsonParser) {this.jsonParser = jsonParser;}public void parseXml(String xml) {String json = convertXmlToJson(xml); // 转换逻辑jsonParser.parseJson(json); // 调用适配者方法}private String convertXmlToJson(String xml) {//伪代码return "Json"+xml,}
}
测试
public class Test {public static void main(String[] args) {JsonParser jsonParser = new JsonParser();XmlParser xmlParser = new JsonToXmlAdapter(jsonParser);xmlParser.parseXml("<order id='123'/>"); // 输出:解析JSON: { ... }}
}
其他案例:
Java I/O 中的适配器:将字节流转换为字符流。
Spring MVC 的 HandlerAdapter:统一处理不同类型的控制器(如基于注解的 @Controller
和旧的 Controller
接口)。
小结:
适配器模式通过接口转换解决不兼容问题,是集成遗留代码或第三方库的利器。
使用建议:
-
优先选择对象适配器,更灵活且符合组合复用原则。
-
避免滥用适配器,若接口不匹配问题可通过重构解决,则无需引入适配器。
-
在框架设计、系统集成、多格式兼容等场景中,适配器模式能显著提升代码复用性和扩展性。
模版方法模式
模板方法模式(Template Method Pattern)是一种行为型设计模式,其核心思想是定义一个算法的骨架,将某些步骤延迟到子类实现,使得子类可以在不改变算法结构的情况下重新定义某些步骤的具体实现。
角色 | 说明 |
---|---|
抽象类(Abstract Class) | 定义算法的骨架(模板方法),包含具体步骤和抽象方法。 |
具体子类(Concrete Class) | 实现抽象类中的抽象方法,完成特定步骤的具体逻辑。 |
模板方法(Template Method) | 抽象类中定义的算法流程,通常为 final 方法,防止子类重写算法结构。 |
钩子方法(Hook Method) | 抽象类中可选的方法,子类可选择性覆盖,用于影响算法流程。 |
举个例子:客户去银行办事,一般要经过以下 4 个流程:取号、排队、办理具体业务、对银行工作人员进行评分等。但是去办理的业务可能不同,可以延迟到子类中实现。
public abstract class AbstractBank {//办理业务方法 -- 模板方法public void handle(){this.offerNumber();this.lineup();this.business();this.score();}//抽号public void offerNumber(){System.out.println("抽号");}//排队public void lineup(){System.out.println("排队");}//办理具体业务--抽象方法,由具体子类实现public abstract void business();//评分public void score(){System.out.println("评分");}
}/*转账业务类*/
public class TransferBusiness extends AbstractBank{//转账public void business() {System.out.println("我要转账");}}/*存钱业务*/
public class StoreBusiness extends AbstractBank{//办理的具体业务public void business() {System.out.println("我要存钱");}
}
public class Test {public static void main(String[] args) {StoreBusiness storeBusiness = new StoreBusiness();storeBusiness.handle();System.out.println("===================================");TransferBusiness transferBusiness = new TransferBusiness();transferBusiness.handle();}
}
其他案例:
Servlet 的生命周期
Servlet 的 service()
方法是一个模板方法,处理 HTTP 请求的通用流程,子类(如 HttpServlet
)通过重写 doGet()
、doPost()
实现具体逻辑。
小洁:
模板方法模式通过固定算法骨架和灵活步骤实现,在保证代码复用性的同时支持扩展。其关键点在于:
-
定义模板方法:使用
final
修饰确保算法结构不被破坏。 -
抽象步骤方法:子类必须实现差异逻辑。
-
钩子方法:提供可选扩展点,控制算法流程。
适用场景:
-
多个子类有共同行为,但部分步骤不同。
-
需要控制子类扩展方式,避免破坏核心流程。
注意事项:
-
避免过度使用继承,若算法步骤频繁变化,可考虑策略模式。
-
合理使用钩子方法,保持代码简洁。
策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,其核心思想是定义一系列算法,封装每个算法,并使它们可以互相替换。策略模式让算法的变化独立于使用它的客户端,通过动态切换算法实现灵活扩展,同时遵循开闭原则(对扩展开放,对修改关闭)。
角色 | 说明 |
---|---|
策略接口(Strategy) | 定义算法的公共接口(如 PaymentStrategy ),所有具体策略必须实现该接口。 |
具体策略类(Concrete Strategy) | 实现策略接口,提供具体的算法实现(如支付宝支付、微信支付)。 |
上下文类(Context) | 持有策略对象,并委托具体策略执行算法(如订单支付处理类)。 |
案例:电商支付策略
//定义策略接口
interface PaymentStrategy {void pay(double amount);
}//实现具体策略类// 支付宝支付
class AlipayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("支付宝支付: " + amount + "元");}
}// 微信支付
class WechatPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("微信支付: " + amount + "元");}
}//委托策略执行// 银行卡支付
class BankCardStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("银行卡支付: " + amount + "元");}
}class OrderPayment {private PaymentStrategy paymentStrategy;// 设置支付策略public void setPaymentStrategy(PaymentStrategy paymentStrategy) {this.paymentStrategy = paymentStrategy;}// 执行支付public void executePayment(double amount) {if (paymentStrategy == null) {throw new IllegalStateException("未设置支付策略");}paymentStrategy.pay(amount);}
}//调用
public class Client {public static void main(String[] args) {OrderPayment order = new OrderPayment();// 使用支付宝支付order.setPaymentStrategy(new AlipayStrategy());order.executePayment(100.0); // 输出:支付宝支付: 100.0元// 切换为微信支付order.setPaymentStrategy(new WechatPayStrategy());order.executePayment(200.0); // 输出:微信支付: 200.0元}
}
总结
策略模式通过封装算法和动态切换策略,有效提升了系统的灵活性和可维护性。其核心优势在于:
-
解耦算法与业务逻辑:客户端仅依赖抽象策略接口。
-
简化单元测试:每个策略可独立测试。
-
符合开闭原则:新增策略无需修改现有代码。
适用场景:
-
系统需要多种算法变体,且需动态切换。
-
存在复杂条件分支,需消除大量
if-else
或switch
语句。 -
算法需要独立复用,或需隔离算法实现细节。
注意事项:
-
合理控制策略类数量,避免类膨胀。
-
优先使用组合而非继承,保持代码灵活性。
观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,用于建立对象间的一对多依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖它的对象(观察者)会自动收到通知并更新。
角色 | 说明 |
---|---|
被观察者(Subject) | 维护观察者列表,提供注册、注销和通知方法(如 addObserver() , notifyObservers() )。 |
观察者(Observer) | 定义更新接口(如 update() ),接收被观察者的状态变化通知。 |
具体被观察者(Concrete Subject) | 实现业务逻辑,状态变更时触发通知(如订单状态变化)。 |
具体观察者(Concrete Observer) | 实现 update() 方法,定义收到通知后的具体响应逻辑(如发送邮件、更新UI)。 |
案例:微信公众号发文,用户订阅
//抽象观察者
public interface Observer {void update(String message);}//抽象主题
public interface Subject {//增加订阅者public void attach(Observer observer);//删除订阅者public void detach(Observer observer);//通知订阅者更新消息public void notify(String message);
}//真实主体
public class SubscriptionSubject implements Subject {//储存订阅公众号的微信用户--观察者private List<Observer> weixinUserlist = new ArrayList();//增加订阅者@Overridepublic void attach(Observer observer) {weixinUserlist.add(observer);}//删除订阅者@Overridepublic void detach(Observer observer) {weixinUserlist.remove(observer);}//通知订阅者更新消息@Overridepublic void notify(String message) {for (Observer observer : weixinUserlist) {observer.update(message);}}
}//真实观察者
public class WeixinUser implements Observer{// 微信用户名private String name;public WeixinUser(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + "-" + message);}}
调用
public class Test {public static void main(String[] args) {SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();//创建微信用户WeixinUser user1=new WeixinUser("张三");WeixinUser user2=new WeixinUser("李四");WeixinUser user3=new WeixinUser("王麻子");//订阅公众号mSubscriptionSubject.attach(user1);mSubscriptionSubject.attach(user2);mSubscriptionSubject.attach(user3);//公众号更新发出消息给订阅的微信用户mSubscriptionSubject.notify("文章更新了");}}
总结
观察者模式通过事件通知机制实现对象间的动态联动,是解耦复杂系统的有效工具。其核心价值在于:
-
松耦合设计:主题与观察者独立演化。
-
灵活扩展:动态增删观察者,无需修改主题。
-
事件驱动:支持实时响应和异步处理。
适用场景:
-
需要实现一对多的消息通知。
-
期望降低对象间的直接依赖。
-
需构建灵活、可扩展的事件处理系统。
注意事项:
-
控制通知频率,避免性能瓶颈。
-
合理处理异常,防止单个观察者失败影响整体流程。
-
结合具体需求选择同步或异步通知方式。
推荐小说:大话设计模式