本文将详细介绍Java中常见的23种设计模式、应用场景、优缺点、代码示例。包括单例模式、工厂模式、建造者模式、原型模式、适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式、代理模式、职责链模式、状态模式、策略模式、模板方法模式、观察者模式、迭代器模式、访问者模式、中介者模式、备忘录模式、解释器模式和享元工厂模式。通过了解这些设计模式,可以帮助我们更好地理解和应用面向对象编程的思想,提高代码的可重用性、灵活性和可维护性。
###资源里面附带全部源码###
一、创建型模式(五种)
1、单例模式(Singleton Pattern)
模式介绍:
单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问该实例。在进行系统设计时,单例模式常用于需要共享状态或控制资源使用的场景。
应用场景:
数据库连接池、日志记录器和配置管理器等。
单例模式的优点:
唯一实例控制:
确保在整个应用程序中只有一个实例存在,避免了由于实例多次创建而导致的资源浪费和性能问题。
全局访问点:
提供一个全局访问点,可以在不同的模块或组件中轻松访问这个唯一的实例,简化了代码管理。
延迟初始化:
可以实现延迟加载,即在第一次使用时才创建实例,节省了资源,提高了程序启动速度。
线程安全:
可以通过适当的实现方式(如双重检查锁定、静态内部类等)确保线程安全,避免多线程环境下的实例重复创建问题。
控制资源使用:
适用于需要严格控制资源使用的场景,例如数据库连接、文件系统操作、网络通信等。
单例模式的缺点:
难以测试:
由于单例模式提供了一个全局实例,这使得在单元测试中很难进行模拟和隔离测试,从而增加了测试的复杂性。
隐藏的依赖性:
单例模式隐藏了类与类之间的依赖关系,使得代码更难理解和维护。使用全局访问点可能导致代码紧耦合,不利于模块化设计。
难以扩展:
单例模式限制了类的可扩展性,如果需要对单例类进行扩展或修改,可能需要重构大量代码。
生命周期控制复杂:
单例实例的生命周期与应用程序的生命周期绑定,如果不慎管理可能导致资源泄露或无法释放。
并发问题:
如果未能正确实现线程安全,单例模式在并发环境下会导致多个实例的创建,从而违背单例模式的初衷。
代码实现:
饿汉式(线程安全,但可能浪费资源)
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
懒汉式(线程不安全)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双重检查锁定(线程安全)
public class Singleton {
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;
}
}
静态内部类(线程安全,推荐)
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
2、工厂方法模式(pattern of factory method)
模式介绍:
工厂方法模式(pattern of factory method)是一种创建型设计模式,它定义了一个用于创建对象的接口,但将实际创建对象的工作延迟到子类中,这样可以在不改变整体结构的情况下,通过子类来选择具体实现。工厂方法模型常用于需要不同条件创建不同对象的场景。
应用场景:
当一个类不知道它所必须创建的对象的类时。
当一个类希望由它的子类来指定它所创建的对象时。
当类将创建对象的职责委托给多个帮助子类中的某一个,并且希望将哪一个帮助子类是代理者这一信息局部化时。
优点:
符合开闭原则: 新增产品时,只需新增相应的工厂类和产品类,而无需修改已有的代码。
降低耦合度: 客户端只需关心所需要的产品对应的工厂,无需关心具体的产品实现细节。
易于扩展: 可以方便地添加新的产品类和对应的工厂类。
缺点:
类的个数增加: 每增加一个产品,就需要增加一个具体产品类和一个对应的工厂类,使得系统中类的个数成倍增加。
增加了系统的复杂度: 增加了系统的抽象性和理解难度。
代码实现:
/**
* 车间接口
*/
public interface FactoryMethodWorkshop {
// 生产方法
void produce();
}
/**
* 工厂车间A
*/
public class FactoryMethodWorkshopA implements FactoryMethodWorkshop {
@Override
public void produce() {
System.out.println("车间A生产A产品");
}
}
/**
* 工厂车间B
*/
public class FactoryMethodWorkshopB implements FactoryMethodWorkshop{
@Override
public void produce() {
System.out.println("车间B生产B产品");
}
}
/**
* 工厂接口
*/
public interface FactoryMethod {
FactoryMethodWorkshop product();
}
/**
* 工厂A
*/
public class FactoryMethodA implements FactoryMethod {
@Override
public FactoryMethodWorkshop product() {
return new FactoryMethodWorkshopA();
}
}
/**
* 工厂B
*/
public class FactoryMethodB implements FactoryMethod{
@Override
public FactoryMethodWorkshop product() {
return new FactoryMethodWorkshopB();
}
}
/**
* 应用
*/
public class FactoryMethodClient {
public static void main(String[] args) {
FactoryMethod factoryA = new FactoryMethodA();
FactoryMethodWorkshop productA = factoryA.product();
productA.produce();
FactoryMethod factoryB = new FactoryMethodB();
FactoryMethodWorkshop productB = factoryB.product();
productB.produce();
}
}
3、抽象工厂模式(Abstract Factory Pattern)
模式介绍:
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供一个接口用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。通过使用抽象工厂模式,客户端可以更灵活地构建和交换产品族。
应用场景:
需要创建一系列相关或相互依赖的对象时,且系统不应依赖这些对象的具体类。
系统需要独立于产品的创建和组织,只需通过接口来操作。
产品族中存在多个产品等级,且要求系统消费时使用同一个产品族的产品。
优点:
分离接口和实现:客户端代码通过抽象工厂接口使用产品类,与具体实现解耦。
更方便的产品交换:由于产品的具体类被隐藏,替换产品族变得简单。
一致性:确保客户端使用的产品属于同一个产品族,避免了产品之间的不兼容性。
缺点:
新增产品族困难:每次增加新的产品族都需要修改抽象工厂接口及其所有子类,违反了开闭原则。
复杂度高:增加了系统的复杂度和理解难度。
代码实现:
/**
* 产品接口A
*/
public interface ProductA {
void use();
}
/**
* 产品接口B
*/
public interface ProductB {
void use();
}
/**
* 具体产品A1
*/
public class ConcreteProductA1 implements ProductA {
@Override
public void use() {
System.out.println("使用产品A1");
}
}
/**
* 具体产品A2
*/
public class ConcreteProductA2 implements ProductA {
@Override
public void use() {
System.out.println("使用产品A2");
}
}
/**
* 具体产品B1
*/
public class ConcreteProductB1 implements ProductB {
@Override
public void use() {
System.out.println("使用产品B1");
}
}
/**
* 具体产品B2
*/
public class ConcreteProductB2 implements ProductB {
@Override
public void use() {
System.out.println("使用产品B2");
}
}
/**
* 抽象工厂接口
*/
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
/**
* 具体工厂A
*/
public class ConcreteFactoryA implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
/**
* 具体工厂B
*/
public class ConcreteFactoryB implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
}
/**
* 应用
*/
public class AbstractFactoryClient {
private ProductA productA;
private ProductB productB;
public AbstractFactoryClient(AbstractFactory factory) {
productA = factory.createProductA();
productB = factory.createProductB();
}
public void run() {
productA.use();
productB.use();
}
public static void main(String[] args) {
AbstractFactory factoryA = new ConcreteFactoryA();
AbstractFactoryClient clientA = new AbstractFactoryClient(factoryA);
clientA.run();
AbstractFactory factoryB = new ConcreteFactoryB();
AbstractFactoryClient clientB = new AbstractFactoryClient(factoryB);
clientB.run();
}
}
4、原型模式(Prototype Pattern)
模式介绍:
原型模式(Prototype Pattern)是一种创建型设计模式,它允许对象在创建新实例时通过复制现有实例而不是通过实例化新对象来完成。这样做可以避免耗费大量的资源和时间来初始化对象。原型模式涉及一个被复制的原型对象,在这个模式中,要创建的对象通过请求原型对象来进行复制。
应用场景:
需要创建大量相似对象:当需要创建大量具有相似属性的对象时,使用原型模式可以避免重复的初始化工作,提高性能。
对象的创建开销较大:如果对象的创建开销很大,例如读取数据库或文件操作,使用原型模式可以节省时间。
动态配置对象:当需要动态配置对象时,可以先创建一个原型对象,然后通过复制来生成新的对象并根据需要进行修改。
优点:
减少对象初始化开销:避免了重复的初始化工作,提高了对象创建的效率。
简化对象的创建:无需关心对象的具体创建过程,只需通过复制原型对象来生成新对象。
动态配置对象:可以通过复制原型对象来动态配置新对象,增加灵活性。
缺点:
深拷贝问题:原型模式需要确保对象的属性都能被正确复制,特别是对于复杂对象来说,需要注意深拷贝和浅拷贝的问题。
类必须实现克隆接口:要使用原型模式,类必须实现克隆方法,这在某些情况下可能是不可行的。
代码实现:
/**
* 实现 Cloneable 接口的原型类
*/
@Data
public class Shape implements Cloneable {
private String type;
@Override
public Shape clone() {
try {
return (Shape) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
/**
* 具体原型类
*/
public class Rectangle extends Shape {
public Rectangle() {
setType("Rectangle原型类");
}
}
/**
* 具体原型类
*/
public class Circle extends Shape {
public Circle() {
setType("Circle原型类");
}
}
/**
* 应用
*/
public class PrototypeClient {
public static void main(String[] args) {
Shape rectangle = new Rectangle();
Shape circle = new Circle();
// 使用原型对象创建新对象
Shape clonedRectangle = rectangle.clone();
Shape clonedCircle = circle.clone();
System.out.println(clonedRectangle.getType());
System.out.println(clonedCircle.getType());
}
}
5、建造者模式(Builder Pattern)
模式介绍:
建造者模式(Builder Pattern)是一种创建型设计模式,它允许你创建一个复杂对象的表示,同时隐藏了创建细节。该模式将对象的构建过程和表示分离,使得相同的构建过程可以创建不同的表示。
应用场景:
创建复杂对象:当需要创建的对象具有复杂的内部结构或需要进行多个步骤的构建时,可以使用建造者模式。
避免构造器参数过多:如果类的构造函数参数过多,可以使用建造者模式构建不同的属性。
创建不同的表示:在不同的情况下,可以通过相同的构建过程创建不同的对象表示。
优点:
封装性好:客户端不需要知道产品内部的构建细节,只需关心建造者的接口和产品的组装过程。
灵活性好:可以根据需求选择构建不同的产品。
扩展性好:增加新的建造者无需修改原有代码,符合开闭原则。
缺点:
增加了对象的数量:引入了建造者模式后,会增加对象的数量,可能会增加系统的复杂度。
构建过程复杂:对于简单的对象,引入建造者模式可能会增加不必要的复杂性。
代码实现:
/**
* 产品类
*/
@Data
public class Product {
private String productA;
private String productB;
public void show() {
System.out.println("productA: " + productA);
System.out.println("productB: " + productB);
}
}
/**
* 抽象建造者
*/
public interface Builder {
void buildProductA();
void buildProductB();
Product getResult();
}
/**
* 具体建造者
*/
public class ConcreteBuilder implements Builder {
private Product product = new Product();
@Override
public void buildProductA() {
product.setProductA("产品A");
}
@Override
public void buildProductB() {
product.setProductB ("产品B");
}
@Override
public Product getResult() {
return product;
}
}
/**
* 指挥者
*/
public class Director {
public Product construct(Builder builder) {
builder.buildProductA();
builder.buildProductB();
return builder.getResult();
}
}
/**
* 应用
*/
public class BuilderClient {
public static void main(String[] args) {
Director director = new Director();
Builder builder = new ConcreteBuilder();
Product product = director.construct(builder);
product.show();
}
}
二、结构型模式(七种)
1、代理模式(Proxy Pattern)
模式介绍:
代理模式(Proxy Pattern)是一种结构型设计模式,它允许你在不改变客户端代码的情况下,向某个对象提供一个代理,以控制对该对象的访问。代理对象通常会在实际对象的方法调用前后添加一些附加逻辑,如权限控制、懒加载、日志记录等。
代理模式涉及以下几个角色:
接口(Subject):定义了代理类和真实类共同的接口。
真实类(RealSubject):实现了接口的具体类,它是真正执行操作的对象。
代理类(Proxy):也实现了接口,持有真实类的引用,并在调用真实类的方法前后进行一些额外的处理。
应用场景:
远程代理(Remote Proxy):为一个对象在不同地址空间提供局部代表。典型应用是 RMI(远程方法调用)。
虚拟代理(Virtual Proxy):根据需要创建开销大的对象。它在真正需要时才创建对象。
保护代理(Protection Proxy):控制对原始对象的访问。通常用于对象应该有不同的访问权限。
智能引用(Smart Reference):在访问对象时提供额外的功能,例如引用计数、缓存等。
优点:
控制对真实对象的访问,增加系统的灵活性。
可以对真实对象进行扩展,而不修改其代码。
提供了对目标对象额外的操作,如权限控制、延迟加载、日志记录等。
缺点:
增加了系统的复杂度,尤其是在使用保护代理时。
可能会影响请求的处理速度,因为增加了间接层。
代码实现:
/**
* 代理接口
*/
public interface Subject {
void request();
}
/**
* 实体类
*/
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("实体类的请求");
}
}
/**
* 代理类
*/
public class Proxy implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
private void preRequest() {
System.out.println("代理类: 前置处理");
}
private void postRequest() {
System.out.println("代理类: 后置处理");
}
}
/**
* 应用
*/
public class ProxyClient {
public static void main(String[] args) {
Subject proxy = new Proxy();
proxy.request();
}
}
拓展动态代理:
还可以使用动态代理来实现代理模式,它使用 java.lang.reflect.Proxy
类和 InvocationHandler
接口来动态地创建代理对象。动态代理可以在运行时动态地将代理逻辑添加到真实对象中,而不需要在编译时显式地创建代理类。
/**
* 动态代理
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object realSubject;
public ProxyInvocationHandler(Object realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("动态代理类: 前置处理");
Object result = method.invoke(realSubject, objects);
System.out.println("动态代理类: 后置处理");
return result;
}
}
/**
* 动态代理应用
*/
public class ProxyInvocationClient {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new ProxyInvocationHandler(realSubject)
);
proxy.request();
}
}
2、适配器模式(Adapter Pattern)
模式介绍:
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户希望的另外一个接口。适配器模式通常用于需要复用现有的类,但是接口与客户端的要求不完全匹配的情况。它包括两种形式:类适配器模式和对象适配器模式。
应用场景:
系统需要使用现有的类,而这些类的接口不符合需求。
不想修改现有的接口,而又需要复用该接口的情况。
多个类似功能的接口需要统一。
优点:
增加了类的透明性和复用性:将具体的实现封装在适配器中,对客户端来说是透明的,同时提高了被适配类的复用性。
灵活性好:通过适配器,可以在不改变原有代码的基础上增加新的适配器类,符合开闭原则。
缺点:
过多使用适配器,会让系统非常零乱。
增加系统的复杂性:因为增加了额外的适配器,可能会增加系统的理解难度
代码实现:
/**
* 适配接口
*/
public interface MediaPlayer {
void play(String mediaType, String fileName);
}
/**
* 被适配者
*/
public class AudioPlayer implements MediaPlayer{
@Override
public void play(String mediaType, String fileName) {
if (mediaType.equalsIgnoreCase("mp3")) {
System.out.println("播放MP3文件: " + fileName);
} else {
System.out.println("使用" + mediaType + "媒体播放");
}
}
}
/**
* 新适配接口
*/
public interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
/**
* Mp4播放器实现新的接口
*/
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
}
@Override
public void playMp4(String fileName) {
System.out.println("播放MP4文件: " + fileName);
}
}
/**
* Vlc播放器实现新的接口
*/
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("播放Vlc文件: " + fileName);
}
@Override
public void playMp4(String fileName) {
}
}
/**
* 适配器类,实现目标接口并持有被适配者的引用
*/
public class MediaAdapter implements MediaPlayer{
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String mediaType) {
if (mediaType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
} else if (mediaType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String mediaType, String fileName) {
if (mediaType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if (mediaType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
/**
* 应用
*/
public class AdapterClient {
public static void main(String[] args) {
MediaPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "音乐.mp3");
audioPlayer.play("mp4", "视频.mp4");
audioPlayer.play("vlc", "视频.vlc");
audioPlayer.play("avi", "视频.avi"); // Unsupported media type
}
}
3、装饰模式(Decorator Pattern)
模式介绍:
装饰模式是一种结构型设计模式,允许你通过将对象放入包含行为的特殊包装对象中来动态地扩展其功能。它提供了一种灵活的方式来添加功能,避免了创建大量子类的问题。
应用场景:
动态添加功能:当需要动态地给一个对象添加功能,而且这些功能可以组合的时候,装饰模式是一个很好的选择。例如,对输入流进行缓冲、加密或压缩操作。
避免子类爆炸:通过装饰模式,可以避免大量子类的创建,每个装饰器类只关注一个特定的功能,使得功能的组合更加灵活。
优点:
灵活性:可以动态地添加对象的功能,而不需要修改现有的代码。
避免子类爆炸:避免创建大量扩展类。
单一职责原则:每个装饰器只关注一个功能,符合单一职责原则。
缺点:
复杂性增加:因为装饰模式会增加许多小类和对象,可能会使设计更加复杂。
初学者理解困难:对于初学者来说,理解装饰模式可能需要一定的时间。
代码实现:
/**
* 车品牌接口
*/
public interface Car {
void assemble();
}
/**
* 基础品牌车
*/
public class BasicCar implements Car{
@Override
public void assemble() {
System.out.print("Basic Car.");
}
}
/**
* 装饰
*/
public class CarDecorator implements Car{
protected Car decoratedCar;
public CarDecorator(Car decoratedCar) {
this.decoratedCar = decoratedCar;
}
public void assemble() {
this.decoratedCar.assemble();
}
}
/**
* 普通品牌车
*/
public class GeneralCar extends CarDecorator{
public GeneralCar(Car decoratedCar) {
super(decoratedCar);
}
@Override
public void assemble() {
super.assemble();
System.out.print("添加一个普通品牌车");
}
}
/**
* 奢侈品牌车
*/
public class LuxuryCar extends CarDecorator{
public LuxuryCar(Car decoratedCar) {
super(decoratedCar);
}
@Override
public void assemble() {
super.assemble();
System.out.print("添加一个奢侈品牌车");
}
}
/**
* 应用
*/
public class DecoratorClient {
public static void main(String[] args) {
Car sportsCar = new GeneralCar(new BasicCar());
sportsCar.assemble();
System.out.println("");
Car sportsLuxuryCar = new GeneralCar(new LuxuryCar(new BasicCar()));
sportsLuxuryCar.assemble();
}
}
4、桥接模式(Bridge Pattern)
模式介绍:
桥接模式是一种结构型设计模式,它将抽象部分与其实现部分分离,使它们可以独立变化。桥接模式通过提供抽象类和实现类之间的桥接结构,可以更容易地进行系统扩展。
应用场景:
多维度变化:当一个类存在多个独立变化的维度时,使用桥接模式可以避免类爆炸式增长。例如,图形界面系统中,可以将不同操作系统(如Windows、Mac)与不同界面元素(如按钮、文本框)分离开来。
抽象和实现分离:希望在抽象和实现之间建立一个稳定的连接,但又不希望两者彼此紧密耦合。桥接模式可以使它们独立变化、互不影响。
优点:
分离抽象和实现:桥接模式通过将抽象部分与实现部分分离,使得它们可以独立变化。
更好的扩展能力:可以动态地组合和扩展抽象部分和实现部分,而不需要修改已有的代码。
隐藏实现细节:桥接模式将实现细节从客户端代码中隐藏,使得代码更加清晰。
缺点:
增加复杂度:桥接模式会增加系统的理解和设计难度,需要正确地识别抽象和实现的分离点。
可能导致类的数量增加:如果桥接模式被过度使用,可能会导致系统中类的数量急剧增加。
代码实现:
/**
* 实现部分接口
*/
public interface DrawingAPI {
void drawCircle(int x, int y, int radius);
}
/**
* 实现类1
*/
public class DrawingAPI1 implements DrawingAPI{
@Override
public void drawCircle(int x, int y, int radius) {
System.out.printf("API1.画圆 %d:%d radius %d\n", x, y, radius);
}
}
/**
* 实现类2
*/
public class DrawingAPI2 implements DrawingAPI{
@Override
public void drawCircle(int x, int y, int radius) {
System.out.printf("API2.画圆 %d:%d radius %d\n", x, y, radius);
}
}
/**
* 抽象绘画类
*/
abstract class Drawing {
protected DrawingAPI drawingAPI;
protected Drawing(DrawingAPI drawingAPI) {
this.drawingAPI = drawingAPI;
}
public abstract void draw(); // 抽象方法
}
/**
* 绘画实体类
*/
public class DrawingClass extends Drawing{
private int x, y, radius;
public DrawingClass(int x, int y, int radius, DrawingAPI drawingAPI) {
super(drawingAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
@Override
public void draw() {
drawingAPI.drawCircle(x, y, radius);
}
}
/**
* 应用
*/
public class BridgeClient {
public static void main(String[] args) {
Drawing drawing1 = new DrawingClass(100, 100, 10, new DrawingAPI1());
Drawing drawing2 = new DrawingClass(200, 200, 20, new DrawingAPI2());
drawing1.draw();
drawing2.draw();
}
}
5、外观模式(Facade Pattern)
模式介绍:
外观模式是一种结构型设计模式,提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,使得子系统更容易使用。
应用场景:
当你希望为复杂的子系统提供一个简单接口时。
当客户端与多个子系统之间存在很大依赖性时,引入外观模式可以将客户端与子系统解耦,从而提高灵活性和可维护性。
优点:
简化了客户端与子系统之间的交互,客户端只需要通过外观类访问子系统,不需要了解子系统的具体细节。
减少了客户端对子系统的依赖,降低了耦合度。
更好地划分了访问层次,符合迪米特法则(最少知识原则)。
缺点:
如果设计不当,增加新的子系统可能需要修改外观类或者客户端代码,违反开闭原则。
代码实现:
/**
* CPU处理
*/
public class CPU {
public void freeze() {
System.out.println("freeze");
}
public void jump(long position) { System.out.println("jump"+position); }
public void execute() {
System.out.println("execute");
}
}
/**
* 硬件处理
*/
public class HardDrive {
public byte[] read(long len, int size) {
System.out.println("len:"+len);
System.out.println("size:"+size);
return null;
}
}
/**
* 内存处理
*/
public class Memory {
public void load(long position, byte[] data) {
System.out.println("position:"+position);
System.out.println("data:"+data);
}
}
/**
* 外观类应用
*/
public class ComputerFacade {
private final CPU cpu;
private final Memory memory;
private final HardDrive hardDrive;
public ComputerFacade() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
public void start() {
cpu.freeze();
memory.load(1L, hardDrive.read(2L, 3));
cpu.jump(1L);
cpu.execute();
}
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.start();
}
}
6、组合模式(Composite Pattern)
模式介绍:
组合模式是一种结构型设计模式,它允许你将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
应用场景:
表示树形结构:当你需要表示对象的部分-整体层次结构时,例如文件系统中的文件和目录,图形界面中的 UI 组件,公司组织结构中的部门与员工等。
统一处理:希望客户端能够统一处理单个对象和组合对象时,可以使用组合模式。这样客户端不需要关心处理的是单个对象还是组合对象。
优点:
简化客户端代码:客户端可以一致地对待单个对象和组合对象,无需区分它们的具体类型。
灵活性:可以在运行时动态地增加或移除子对象,更容易扩展。
层次结构:可以更好地表示对象的层次结构,方便递归遍历和操作。
缺点:
限制类型:可能限制组合中的组件类型,因为组件必须实现统一的接口或继承自统一的类。
复杂性:可能会使设计更加抽象复杂,特别是在处理深层次的递归结构时。
代码实现:
/**
* 抽象组件接口
*/
public interface Department {
void printDepartmentName();
}
/**
* 具体部门
*/
public class ConcreteDepartment implements Department{
private String name;
public ConcreteDepartment(String name) {
this.name = name;
}
@Override
public void printDepartmentName() {
System.out.println("Department: " + name);
}
}
/**
* 组合部门
*/
public class CompositeDepartment implements Department{
private String name;
private List<Department> departments = new ArrayList<>();
public CompositeDepartment(String name) {
this.name = name;
}
public void addDepartment(Department department) {
departments.add(department);
}
public void removeDepartment(Department department) {
departments.remove(department);
}
@Override
public void printDepartmentName() {
System.out.println("Department: " + name);
for (Department department : departments) {
department.printDepartmentName();
}
}
}
/**
* 应用
*/
public class CompositeClient {
public static void main(String[] args) {
ConcreteDepartment salesDepartment = new ConcreteDepartment("A");
ConcreteDepartment marketingDepartment = new ConcreteDepartment("B");
CompositeDepartment headDepartment = new CompositeDepartment("C");
headDepartment.addDepartment(salesDepartment);
headDepartment.addDepartment(marketingDepartment);
ConcreteDepartment financeDepartment = new ConcreteDepartment("D");
headDepartment.addDepartment(financeDepartment);
headDepartment.printDepartmentName();
}
}
示例说明:
在上面的示例中,Department 是抽象组件类,定义了统一的接口 printDepartmentName()。ConcreteDepartment 是叶子节点类,表示具体的部门。CompositeDepartment 是容器节点类,表示组合的部门,可以包含其他部门(包括叶子部门和容器部门)。客户端通过操作抽象组件 Department 来构建和操作部门与员工的层次结构。
7、享元模式(Flyweight Pattern)
模式介绍:
享元模式是一种结构型设计模式,旨在通过共享对象来有效支持大量细粒度的对象。它通过将对象的状态分为内部状态(可共享)和外部状态(不可共享)来减少内存消耗和提高性能。内部状态存储在享元对象内部,而外部状态则由客户端代码管理和传递。
应用场景:
对象池:当程序中存在大量相似对象且不需要区分其具体身份时,可以使用享元模式。比如线程池、连接池等。
文本编辑器:对于每一个字符或者格式化文本片段,如果存在大量相同的文本片段,可以共享相同的内部状态,减少内存消耗。
游戏开发:在游戏中,大量的角色或者粒子对象可能具有相同的外观和行为,可以通过享元模式来节省资源。
字符串常量池:Java中的字符串常量池就是享元模式的一个实际应用,相同的字符串常量在内存中只存储一份,多个字符串变量可以共享同一个常量。
优点:
减少内存消耗:通过共享相同的对象实例,减少内存使用。
提高性能:减少了创建对象的时间,特别是在对象频繁被创建和销毁的场景下。
缺点:
可能引入额外的复杂性:需要对内部状态和外部状态进行区分和管理,可能增加系统的复杂度。
需要合理划分内部状态和外部状态:不正确的划分可能导致系统的逻辑混乱。
代码实现:
/**
* 享元接口
*/
public interface FlyweightInterface {
//绘画方法
void draw();
}
/**
* 享元实现类
*/
@Data
public class FlyweightClass implements FlyweightInterface{
private final String color; // 颜色
private int x, y, radius; // 坐标半径
public FlyweightClass(String color) {
this.color = color;
}
@Override
public void draw() {
System.out.println("享元实现类: 绘画 [Color : " + color
+ ", x : " + x + ", y :" + y + ", radius :" + radius);
}
}
/**
* 享元工厂
*/
public class FlyweightFactory {
private static final Map<String, FlyweightInterface> flyweightMap = new HashMap<>();
public static FlyweightInterface getCircle(String color) {
FlyweightClass flyweightClass = (FlyweightClass) flyweightMap.get(color);
if (flyweightClass == null) {
flyweightClass = new FlyweightClass(color);
flyweightMap.put(color, flyweightClass);
System.out.println("开始创作: " + color);
}
return flyweightClass;
}
}
/**
* 应用
*/
public class FlyweightClient {
private static final String[] colors = {"红", "绿", "蓝"};
public static void main(String[] args) {
for (int i = 0; i < 20; ++i) {
FlyweightClass circle = (FlyweightClass)FlyweightFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}
private static int getRandomX() {
return (int) (Math.random() * 100);
}
private static int getRandomY() {
return (int) (Math.random() * 100);
}
}
示例说明:
在这个示例中:FlyweightClass类作为具体享元类,颜色(内部状态)color 是享元的一部分,而 x、y、raidus则是外部状态。
FlyweightFactory 类作为享元工厂,负责管理和提供享元对象。享元对象在首次创建时存储在 flyweightMap 中,以便于后续共享使用。
FlyweightClient类作为客户端代码,演示如何使用享元模式来创建和绘制多个圆形对象,共享相同的颜色。
通过享元模式,可以看到多个具有相同颜色的圆形对象共享同一个 FlyweightClass 实例,从而减少了对象的创建和内存消耗。
三、行为型模式(十一种)
1、模版方法模式(Template Method Pattern)
模式介绍:
模版方法模式是一种行为型设计模式,定义了一个操作中的算法框架,将一些步骤延迟到子类中实现。模版方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
应用场景:
算法框架固定:当有一个算法有固定的框架但具体步骤可以有所不同时,可以使用模版方法模式。例如,开发一个网络库,其中连接、发送数据、关闭连接的顺序是固定的,但具体实现可能依赖于不同的网络协议。
代码复用:将公共操作抽象出来,避免代码重复。模版方法定义了算法的骨架,子类实现具体细节,提高了代码的复用性。
扩展性:通过模版方法模式,可以方便地扩展和修改算法的部分实现,而不需要修改整个算法的结构。
优点:
代码复用:将公共部分抽象到父类的模版方法中,避免重复代码。
灵活性:子类可以根据需要实现特定步骤,同时保持算法的整体结构不变。
扩展性:易于扩展新的算法步骤或者变体,通过子类扩展来实现。
缺点:
控制流程:模版方法模式在某种程度上限制了子类的灵活性,因为算法框架由父类控制,可能会导致类爆炸问题。
复杂性:过度使用模版方法可能会导致类的数量增多,增加代码复杂度。
代码示例:
/**
* 抽象类:制作咖啡的模版
*/
abstract class CoffeeTemplate {
// 模版方法,定义咖啡的制作流程
public final void makeCoffee() {
boilWater();
brewCoffeeGrinds();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
// 具体步骤的具体实现由子类提供
abstract void brewCoffeeGrinds();
abstract void addCondiments();
// 公共步骤,具体实现是相同的
void boilWater() {
System.out.println("开水");
}
void pourInCup() {
System.out.println("倒入杯中");
}
// 钩子方法,子类可以覆盖,控制算法的某些步骤
boolean customerWantsCondiments() {
return true;
}
}
/**
* 具体类:制作咖啡
*/
public class Coffee extends CoffeeTemplate{
@Override
void brewCoffeeGrinds() {
System.out.println("冲咖啡");
}
@Override
void addCondiments() {
System.out.println("加糖和牛奶");
}
// 覆盖钩子方法,可以不加糖和牛奶
@Override
boolean customerWantsCondiments() {
return false;
}
}
/**
* 应用
*/
public class TemplateMethodClient {
public static void main(String[] args) {
Coffee coffee = new Coffee();
coffee.makeCoffee();
}
}
示例说明:
在上面的例子中,CoffeeTemplate 是一个抽象类,定义了制作咖啡的模版方法 makeCoffee(),并包含了公共步骤和钩子方法。Coffee 类是具体的咖啡类型,实现了特定的 brewCoffeeGrinds() 和 addCondiments() 方法,以及钩子方法 customerWantsCondiments() 的覆盖。通过模版方法模式,实现了制作咖啡的通用框架,同时具体的制作细节由子类决定
2、策略模式(Strategy Pattern)
模式介绍:
策略模式是一种行为设计模式,它定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
应用场景:
当一个对象有多种行为,而需要动态选择一种行为时。
不同的策略可以实现不同的行为,客户端根据需要在运行时选择合适的策略。
当需要避免使用大量的条件判断语句来选择不同的行为时,可以使用策略模式来提高代码的可维护性。
优点:
策略模式提供了一种可以替换继承关系的方法,可以独立于客户端改变算法,易于扩展。
可以避免使用多重条件语句。
策略类之间可以自由切换,易于修改和测试。
缺点:
客户端必须了解所有的策略,并在适当的时候选择合适的策略,增加了客户端的复杂度。
策略模式会造成很多的策略类,增加了系统的对象数量。
代码示例:
/**
* 排序策略接口
*/
interface SortingStrategy {
void sort(int[] data);
}
/**
* 冒泡策略
*/
public class BubbleSortStrategy implements SortingStrategy{
@Override
public void sort(int[] data) {
// 冒泡排序算法实现
System.out.println("使用冒泡算法实现");
}
}
/**
* 快速排序实现
*/
public class QuickSortStrategy implements SortingStrategy{
@Override
public void sort(int[] data) {
// 快速排序算法实现
System.out.println("快速排序算法实现");
}
}
/**
* 环境类(Context)
*/
public class Sorter {
private SortingStrategy strategy;
public Sorter(SortingStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(SortingStrategy strategy) {
this.strategy = strategy;
}
public void performSort(int[] data) {
strategy.sort(data);
}
}
/**
* 应用
*/
public class StrategyClient {
public static void main(String[] args) {
int[] data = {5, 1, 3, 6, 4, 2};
Sorter sorter = new Sorter(new BubbleSortStrategy());
sorter.performSort(data); // 使用冒泡排序策略
sorter.setStrategy(new QuickSortStrategy());
sorter.performSort(data); // 使用快速排序策略
}
}
3、命令模式(Command Pattern)
模式介绍:
命令模式(Command Pattern)是一种行为设计模式,其主要目的是将请求封装成一个对象,从而允许使用不同的请求、队列或者日志来参数化其他对象。这种模式使得命令的请求者和实现者解耦。
应用场景:
需要将请求发送者和接收者解耦:命令模式将请求封装成对象,使得发送者和接收者彼此独立,发送者不需要知道接收者的具体操作。
需要在不同的时间指定请求、排队请求、记录请求:命令模式允许将操作请求封装成对象,从而使得可以参数化其他对象来执行不同的请求、将请求排队、记录日志等。
优点:
降低系统耦合度:命令模式将请求者与接收者解耦,请求者无需知道接收者的详细信息。
新增或修改命令更加灵活:可以新增命令类或修改命令参数,而不影响到其他类。
支持撤销和重做操作:通过保存历史命令可以实现撤销和重做操作。
缺点:
命令类可能会增加系统复杂度:每一个命令都需要设计一个具体命令类,可能会导致类的数量增加。
可能会导致系统产生大量的具体命令类:如果命令操作很多,可能会产生大量的具体命令类。
代码示例:
/**
* 命令接口
*/
public interface Command {
void execute();
}
/**
* 接收者类 - 灯
*/
public class Light {
public void turnOn() {
System.out.println("灯已打开");
}
public void turnOff() {
System.out.println("灯已关闭");
}
}
/**
* 具体命令类 - 关灯命令
*/
public class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOff();
}
}
/**
* 具体命令类 - 开灯命令
*/
public class LightOnCommand implements Command{
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}
/**
* 调用者类 - 遥控器
*/
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
/**
* 应用
*/
public class CommandClient {
public static void main(String[] args) {
// 创建灯对象
Light light = new Light();
// 创建具体命令对象
Command turnOnCommand = new LightOnCommand(light);
Command turnOffCommand = new LightOffCommand(light);
// 创建遥控器对象
RemoteControl remoteControl = new RemoteControl();
// 设置命令并执行
remoteControl.setCommand(turnOnCommand);
remoteControl.pressButton(); // 打开灯
remoteControl.setCommand(turnOffCommand);
remoteControl.pressButton(); // 关闭灯
}
}
示例说明:
Command 是命令接口,定义了执行命令的方法 execute。
LightOnCommand 和 LightOffCommand 是具体的命令类,分别用于开灯和关灯操作。
Light 是接收者类,定义了实际的操作方法。
RemoteControl 是调用者类,通过 setCommand 方法设置命令,通过 pressButton 方法执行命令。
4、职责链模式(Chain of Responsibility Pattern)
模式介绍:
职责链模式是一种行为设计模式,其中多个对象按顺序处理请求,直到其中一个对象能够处理请求为止。请求沿着链传递,直到有一个对象处理它为止。
应用场景:
职责链模式适用于以下场景:
请求的发送者不需要知道请求的处理细节,以及请求的处理者是谁。
多个对象可以处理同一个请求,但具体处理者在运行时确定。
系统需要动态地指定可以处理请求的对象集合。
具体的应用场景包括:
权限验证:例如,一个网页访问请求可以经过多个权限验证器,任何一个验证器通过即可访问。
日志记录:日志记录器可以形成一个职责链,每个记录器记录不同级别的日志。
请求的分发:例如,一个请求可以经过多个过滤器或处理器,每个处理器负责不同的处理逻辑。
优点:
降低耦合度:请求的发送者和接收者解耦,可以灵活地添加或删除处理器。
增强了灵活性:可以动态地改变链的组成和处理顺序。
简化了对象:每个处理器只关注自己的任务,不需要了解整个处理流程。
缺点:
请求可能未被处理:如果链未正确配置,请求可能无法被处理。
性能问题:长链可能会影响性能,因为每个请求都可能沿着链传递到链的末端。
代码示例:
/**
* 请求类
*/
public class Request {
private int level;
public Request(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
}
/**
* 抽象处理器类
*/
abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest(Request request);
}
/**
* 具体处理器类 - 级别1处理器
*/
public class ConcreteHandler1 extends Handler{
public void handleRequest(Request request) {
if (request.getLevel() <= 1) {
System.out.println("ConcreteHandler1 处理请求");
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
/**
* 具体处理器类 - 级别2处理器
*/
public class ConcreteHandler2 extends Handler {
public void handleRequest(Request request) {
if (request.getLevel() <= 2) {
System.out.println("ConcreteHandler2 处理请求");
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
/**
* 具体处理器类 - 级别3处理器
*/
public class ConcreteHandler3 extends Handler{
public void handleRequest(Request request) {
if (request.getLevel() <= 3) {
System.out.println("ConcreteHandler3 处理请求");
} else {
System.out.println("所有处理器均不能处理该请求");
}
}
}
/**
* 应用
*/
public class ChainClient {
public static void main(String[] args) {
// 创建处理器对象
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
// 设置处理链
handler1.setSuccessor(handler2);
handler2.setSuccessor(handler3);
// 创建不同级别的请求
Request request1 = new Request(2);
Request request2 = new Request(5);
// 处理请求
handler1.handleRequest(request1); // 输出 ConcreteHandler2 处理请求
handler1.handleRequest(request2); // 输出 所有处理器均不能处理该请求
}
}
示例说明:
Request 类表示请求,具有一个级别属性。
Handler 是抽象处理器类,定义了处理请求的方法和持有下一个处理器的引用。
ConcreteHandler1、ConcreteHandler2 和 ConcreteHandler3 是具体的处理器类,每个处理器根据自己的能力处理请求或传递到下一个处理器。
ChainOfResponsibilityPatternExample 是测试类,创建了一个简单的处理链,并测试不同级别的请求。
5、状态模式(State Pattern)
模式介绍:
状态模式(State Pattern)是一种行为型设计模式,用于实现对象状态的变化管理。它允许一个对象在其内部状态发生变化时改变其行为,使得对象看起来似乎修改了其类。
应用场景:
当一个对象的行为取决于其状态,并且需要在运行时根据状态改变其行为时。
当状态转换过程中需要维护一致性,确保状态之间的切换不会引起不良后果。
当对象的某些状态导致大量条件语句时,可以使用状态模式简化代码。
优点:
封装性良好:将与特定状态相关的行为局部化,并将不同状态的行为分离。
易于添加新状态:通过定义新的子类可以很容易地增加新的状态和转换。
减少条件语句:避免了大量的条件判断语句,提高了代码的可维护性。
缺点:
可能会导致类的数量增加,因为每个状态都需要一个对应的类。
如果状态转换很频繁,并且状态对象很小,可能会引起较多的对象创建开销。
代码示例:
public interface State {
void handle();
}
public class CloseState implements State{
@Override
public void handle() {
System.out.println("电梯关闭...");
// 具体关闭状态的操作
}
}
public class OpenState implements State {
@Override
public void handle() {
System.out.println("电梯开启...");
// 具体开启状态的操作
}
}
public class RunningState implements State {
@Override
public void handle() {
System.out.println("电梯运行...");
// 具体运行状态的操作
}
}
public class StoppingState implements State{
@Override
public void handle() {
System.out.println("电梯停止...");
// 具体停止状态的操作
}
}
/**
* 应用上下文类
*/
public class StateContext {
private State state;
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle();
}
}
public class StateClient {
public static void main(String[] args) {
StateContext context = new StateContext();
// 设置初始状态为关闭状态
context.setState(new CloseState());
// 请求电梯运行
context.request();
// 切换到开启状态
context.setState(new OpenState());
context.request();
// 切换到运行状态
context.setState(new RunningState());
context.request();
// 切换到停止状态
context.setState(new StoppingState());
context.request();
}
}
6、观察者模式(Observer Pattern)
模式介绍:
观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
应用场景:
当一个对象的改变需要同时改变其它对象,并且不知道具体有多少对象有待改变时,可以使用观察者模式。
当一个抽象模型有两个方面,其中一个依赖于另一个方面时,可使用观察者模式。例如,一个主题和其观察者之间的关系。
优点:
观察者模式实现了解耦。主题(或被观察者)只知道观察者列表,并不需要知道具体的观察者。观察者可以独立于主题存在和扩展。
支持广播通信。主题对象会自动将状态的变化通知给所有注册过的观察者,观察者只需要关心自己需要的信息即可。
符合开闭原则。可以在不修改主题或观察者的情况下,灵活地增加或删除观察者。
缺点:
如果一个观察者与主题之间有循环依赖,可能会导致循环调用,造成系统崩溃。
如果观察者过多或处理时间过长,会影响主题的性能。
代码示例:
/**
* 观察者接口
*/
public interface Observer {
void update(int state);
}
/**
* 具体观察者
*/
public class ConcreteObserver implements Observer {
private int observerState;
@Override
public void update(int state) {
observerState = state;
System.out.println("Observer State updated to: " + observerState);
}
}
/**
* 主题接口
*/
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
/**
* 具体主题
*/
public class ConcreteSubject implements Subject{
private List<Observer> observers = new ArrayList<>();
private int state; // 主题状态
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyObservers(); // 状态变化时通知所有观察者
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
/**
* 应用
*/
public class ObserverClient {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
// 创建两个具体观察者
ConcreteObserver observer1 = new ConcreteObserver();
ConcreteObserver observer2 = new ConcreteObserver();
// 注册观察者
subject.registerObserver(observer1);
subject.registerObserver(observer2);
// 主题状态改变,触发通知
subject.setState(10);
subject.setState(20);
// 移除一个观察者
subject.removeObserver(observer1);
// 主题状态再次改变,触发通知
subject.setState(30);
}
}
示例说明:
Subject 定义了主题的基本操作,包括注册、移除和通知观察者。
ConcreteSubject 是具体的主题实现,维护了一个观察者列表,并在状态改变时通知所有观察者。
Observer 定义了观察者的更新操作。
ConcreteObserver 是具体的观察者实现,当接收到更新时,输出新的状态。
在 main 方法中,创建了主题对象和观察者对象,并演示了状态改变时观察者的自动更新。
这个例子展示了观察者模式如何实现对象之间的松耦合,以及如何在状态改变时通知所有依赖于这个状态的对象。
7、中介者模式(Mediator Pattern)
模式介绍:
中介者模式是一种行为设计模式,它允许对象之间通过一个中介对象进行协作,而不直接相互引用,从而降低了它们之间的耦合度。在中介者模式中,对象之间的交互被封装在一个中介对象中,对象不再直接交互,而是通过中介者进行通信。
应用场景:
当一组对象之间的通信方式复杂,并且每个对象都需要了解其他对象的状态时,可以使用中介者模式简化对象之间的交互。
当多个对象之间形成的网状结构导致难以维护时,可以引入中介者来集中控制和管理对象之间的交互。
优点:
降低了对象之间的耦合度,使得对象易于独立复用。
将多个对象之间的交互集中管理,减少了代码的复杂性。
可以简化对象之间的通信,使得系统更易于维护和扩展。
缺点:
中介者模式会导致中介者本身变得复杂且难以维护,特别是当逻辑复杂、对象之间关系多变时。
如果设计不当,中介者本身可能成为系统的瓶颈,影响系统的性能。
代码示例:
/**
* 中介者接口
*/
public interface Mediator {
void sendMessage(String message, Colleague colleague);
}
/**
* 同事类抽象接口
*/
abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public abstract void sendMessage(String message);
public abstract void receiveMessage(String message);
}
/**
* 具体中介者
*/
public class ConcreteMediator implements Mediator {
private List<Colleague> colleagues = new ArrayList<>();
public void addColleague(Colleague colleague) {
colleagues.add(colleague);
}
@Override
public void sendMessage(String message, Colleague originator) {
// 中介者接收到消息后,转发给其他同事
for (Colleague colleague : colleagues) {
if (colleague != originator) {
colleague.receiveMessage(message);
}
}
}
}
/**
* 具体同事类
*/
public class ConcreteColleague1 extends Colleague{
public ConcreteColleague1(Mediator mediator) {
super(mediator);
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("ConcreteColleague1 received: " + message);
}
}
/**
* 具体同事类
*/
public class ConcreteColleague2 extends Colleague {
public ConcreteColleague2(Mediator mediator) {
super(mediator);
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("ConcreteColleague2 received: " + message);
}
}
/**
* 应用
*/
public class MediatorClient {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
mediator.addColleague(colleague1);
mediator.addColleague(colleague2);
colleague1.sendMessage("Hello from Colleague1");
colleague2.sendMessage("Hi from Colleague2");
}
}
示例讲解:
Mediator 定义了中介者的接口,包括发送消息的方法。
ConcreteMediator 是具体的中介者实现,维护了一个同事列表,并实现了消息的转发逻辑。
Colleague 是同事类的抽象,包含了一个中介者对象和发送、接收消息的抽象方法。
ConcreteColleague1 和 ConcreteColleague2 是具体的同事类实现,它们通过中介者来发送和接收消息。
在 main 方法中,创建了中介者对象和多个同事对象,并演示了它们通过中介者进行消息交互的过程。
8、迭代器模式(Iterator Pattern)
模式介绍:
迭代器模式是一种行为设计模式,它允许客户端通过统一的方式访问聚合对象中的各个元素,而不必暴露其内部表示。通过迭代器模式,可以在不知道聚合对象内部结构的情况下,顺序访问其中的元素。
应用场景:
当需要访问一个聚合对象的内容而无需暴露其内部表示时,可以使用迭代器模式。这样客户端可以通过迭代器依次访问聚合对象中的元素。
当需要对聚合对象有多种遍历方式时,可以通过不同的迭代器实现类来实现不同的遍历策略。
优点:
分离了聚合对象的遍历行为,使得聚合对象的内部变化不会影响到客户端的遍历操作。
提供了一种统一的接口,客户端可以使用统一的方式遍历不同的聚合对象。
缺点:
增加了类的个数,因为每个聚合对象都需要一个对应的迭代器。
在某些情况下,使用迭代器可能会导致性能上的损失,因为迭代器模式在遍历过程中会增加额外的对象创建和方法调用。
代码实现:
/**
* 迭代器接口
*/
public interface IteratorPatternIterator<T> {
boolean hasNext();
T next();
}
/**
* 聚合接口
*/
public interface Aggregate<T> {
IteratorPatternIterator<T> createIterator();
}
/**
* 具体迭代器实现
*/
public class ConcreteIterator<T> implements IteratorPatternIterator<T> {
private List<T> elements;
private int position;
public ConcreteIterator(List<T> elements) {
this.elements = elements;
this.position = 0;
}
@Override
public boolean hasNext() {
return position < elements.size();
}
@Override
public T next() {
return elements.get(position++);
}
}
/**
* 具体聚合实现
*/
public class ConcreteAggregate<T> implements Aggregate<T> {
private List<T> elements = new ArrayList<>();
public void add(T element) {
elements.add(element);
}
@Override
public IteratorPatternIterator<T> createIterator() {
return new ConcreteIterator<>(elements);
}
}
/**
* 实现
*/
public class IteratorClient {
public static void main(String[] args) {
ConcreteAggregate<String> aggregate = new ConcreteAggregate<>();
aggregate.add("Item 1");
aggregate.add("Item 2");
aggregate.add("Item 3");
IteratorPatternIterator<String> iterator = aggregate.createIterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println("Item: " + item);
}
}
}
示例说明:
IteratorPatternIterator 定义了迭代器的接口,包括 hasNext() 和 next() 方法。
Aggregate 定义了聚合对象的接口,其中包括 createIterator() 方法用于创建迭代器。
ConcreteIterator 是具体的迭代器实现,负责遍历 List 中的元素。
ConcreteAggregate 是具体的聚合对象实现,维护一个 List 来存储元素,并实现了 createIterator() 方法以生成对应的迭代器。
在 main 方法中,创建了一个具体的聚合对象 ConcreteAggregate,添加了一些元素,并使用迭代器依次访问这些元素并打印输出。
9、访问者模式(Visitor Pattern)
模式介绍:
访问者模式是一种行为设计模式,其主要目的是在不改变已有类的前提下,定义作用于这些类对象结构中的新功能。它将数据结构与数据操作分离,使得操作集合可以独立变化。
应用场景:
当一个数据结构包含许多不同类型的对象,而你希望对这些对象实施一些依赖其具体类型的操作时,可以使用访问者模式。例如,编译器中对抽象语法树(AST)的不同节点进行不同的操作。
当需要在不改变类的前提下,添加新的操作时,访问者模式可以帮助避免修改现有代码,而只需新增访问者类即可。
当对象结构中的元素类很少改变,但经常需要在此结构上定义新的操作时,访问者模式提供了一种灵活的方式。
优点:
符合单一职责原则:将相关行为封装在一起,使得代码易于理解和维护。
扩展性良好:可以通过增加新的访问者类,实现对已有代码的功能扩展,而无需修改现有代码。
分离数据结构和算法:可以在不修改原有数据结构的情况下,增加新的操作。
缺点:
增加新的元素类比较困难,需要修改所有的访问者类。
具体元素暴露,增加了对象结构的复杂度。
代码示例:
/**
* 元素接口
*/
public interface Element {
void accept(Visitor visitor);
}
/**
* 具体元素实现类A
*/
public class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
/**
* 具体元素实现类B
*/
public class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
/**
* 访问者接口
*/
public interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
/**
* 具体的访问者实现
*/
public class ConcreteVisitor implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("Visitor is visiting ConcreteElementA");
// 对ConcreteElementA的具体操作
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("Visitor is visiting ConcreteElementB");
// 对ConcreteElementB的具体操作
}
}
/**
* 对象结构类,可以包含多种不同的元素对象
*/
public class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void attach(Element element) {
elements.add(element);
}
public void detach(Element element) {
elements.remove(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
/**
* 应用
*/
public class VisitorClient {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new ConcreteElementA());
objectStructure.attach(new ConcreteElementB());
Visitor visitor = new ConcreteVisitor();
objectStructure.accept(visitor);
}
}
示例讲解:
Visitor
定义了访问者的接口,包括访问不同具体元素的方法。ConcreteVisitor
是具体的访问者实现,实现了对不同具体元素的访问操作。Element
定义了元素的接口,包括接受访问者访问的方法 accept()
。ConcreteElementA
和 ConcreteElementB
是具体的元素实现类,实现了 Element
接口的 accept()
方法,并在其中调用访问者的访问方法。ObjectStructure
定义了对象结构,可以包含多种不同的元素对象,并提供了接受访问者的方法 accept()
,用于遍历其中的元素并调用它们的 accept()
方法。
在 main
方法中,创建了一个具体的对象结构 ObjectStructure
,并向其中添加了 ConcreteElementA
和 ConcreteElementB
。然后创建了一个具体的访问者 ConcreteVisitor
,并调用对象结构的 accept()
方法,传入访问者,从而实现对元素的访问。
10、备忘录模式(Memeton Pattern)
模式介绍:
备忘录模式是一种行为设计模式,它允许在不破坏封装的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
应用场景:
需要保存和恢复对象的历史状态,例如撤销操作。
需要提供一个可回滚的操作,如编辑器中的撤销/重做功能。
需要实现检查点(checkpoint)和复原的功能,如游戏中的存档功能。
优点:
提供了一种可以恢复对象状态的机制,使得用户可以比较方便地回滚到之前的状态。
实现了状态的封装,使得对象状态信息对其他对象不可见,符合了封装的原则。
缺点:
如果需要保存的对象状态非常多,可能会消耗大量的内存。
在频繁创建备忘录对象时,可能会产生较大的开销。
代码示例:
/**
* 备忘录类,存储原始对象的状态
*/
public class Memento {
private String state;
public Memento(String stateToSave) {
state = stateToSave;
}
public String getSavedState() {
return state;
}
}
/**
* 原始对象,可以创建备忘录和从备忘录中恢复状态
*/
public class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
// 创建备忘录,保存当前状态
public Memento saveStateToMemento() {
return new Memento(state);
}
// 从备忘录中恢复状态
public void restoreStateFromMemento(Memento memento) {
state = memento.getSavedState();
}
}
/**
* 管理者类,负责保存和恢复备忘录
*/
public class Caretaker {
private Memento memento;
public void setMemento(Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
}
/**
* 应用
*/
public class MementoClient {
public static void main(String[] args) {
// 创建原始对象
Originator originator = new Originator();
originator.setState("State1");
// 创建备忘录,并保存当前状态
Memento memento = originator.saveStateToMemento();
// 使用管理者类保存备忘录
Caretaker caretaker = new Caretaker();
caretaker.setMemento(memento);
// 修改原始对象的状态
originator.setState("State2");
// 恢复原始对象的状态
originator.restoreStateFromMemento(caretaker.getMemento());
System.out.println("Current State: " + originator.getState());
}
}
示例讲解:
Memento
类用于存储原始对象的状态。Originator
类是拥有状态的原始对象,它能创建备忘录并从备忘录中恢复状态。Caretaker
类负责保存和管理备忘录。
在 main
方法中,首先创建 Originator
对象,并设置其状态为 "State1"。然后创建备忘录并保存当前状态。接着修改原始对象的状态为 "State2"。最后,使用 Caretaker
恢复原始对象的状态,并打印出当前状态。
11、解释器模式(Interpreter Pattern)
模式介绍:
解释器模式是一种行为设计模式,它定义了一个语言的文法,并且建立一个解释器来解释该语言中的句子。它属于行为型模式,通过定义语法的方式来解决特定问题。
应用场景:
当有一个语言需要解释执行,并且可以将该语言的句子表示为一个抽象语法树时,可以使用解释器模式。例如,编译器、正则表达式解析、SQL 解析器等。
当需要实现一种特定语言的解释器,且该语言的句子可以表达为一个语法树时,解释器模式可以提供一种灵活的解决方案。
优点:
易于扩展和改变文法。通过增加新的解释器,可以修改或扩展语言的解析规则,而无需修改现有的解释器。
实现了文法和解释器之间的松耦合关系。每个解释器都是独立的,可以单独改变和复用。
缺点:
解释器模式可能会引起类膨胀。每个语法规则都可能需要实现一个具体的解释器,这可能会导致类的数量急剧增加。
复杂的文法可能难以维护和理解。当语法规则复杂或者文法变动频繁时,维护解释器可能会变得困难。
代码示例:
/**
* 抽象表达式类,声明一个抽象的解释操作
*/
public interface Expression {
boolean interpret(String context);
}
/**
* 终结符表达式类,实现抽象表达式接口,对文法中的终结符进行解释操作
*/
public class TerminalExpression implements Expression {
private String data;
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
return context.contains(data);
}
}
/**
* 非终结符表达式类,实现抽象表达式接口,对文法中的非终结符进行解释操作
*/
public class OrExpression implements Expression {
private Expression expr1;
private Expression expr2;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
/**
* 应用上下文
*/
public class InterpreterContext {
private Expression defineGrammar(String[] rules) {
Expression rule1 = new TerminalExpression(rules[0]);
Expression rule2 = new TerminalExpression(rules[1]);
return new OrExpression(rule1, rule2);
}
public void interpret(String context, String[] rules) {
Expression define = defineGrammar(rules);
System.out.println(context + " is " + define.interpret(context));
}
}
/**
* 应用
*/
public class InterpreterClient {
public static void main(String[] args) {
InterpreterContext context = new InterpreterContext();
String context1 = "Rule1";
String context2 = "Rule2";
context.interpret(context1, new String[]{"Rule1", "Rule2"});
context.interpret(context2, new String[]{"Rule2", "Rule3"});
}
}
示例讲解:
Expression
接口定义了一个解释操作 interpret
,所有的表达式都将实现这个接口。TerminalExpression
和 OrExpression
分别表示终结符和非终结符表达式,实现了具体的解释逻辑。InterpreterContext
类包含解释器之外的一些全局信息,负责解释器模式的上下文管理和调用
在 main
方法中,创建了一个 Context
对象,并且通过 interpret
方法进行文法解释,输出结果。