- 创建型模式 解决对象创建的问题,关注对象的实例化过程以及如何灵活创建对象,例如单例模式、工厂模式等。
- 结构型模式 解决类或对象之间的组合和关联关系,关注类或对象之间的静态结构,如如何组合类和对象以实现更大的结构。
- 行为型模式 解决对象之间的交互和协作问题,关注对象之间的动态行为和通信模式,如如何让不同的对象合作完成某个任务。
一、创建型模式
1、简单工厂模式
简单工厂模式(Simple Factory Pattern),它提供了一个统一的接口来创建对象,但将对象的实例化过程隐藏在工厂类的内部。简单工厂模式通常由一个工厂类负责根据传入的参数来创建不同类型的对象,客户端代码只需通过工厂类来获取所需的对象,而不需要直接与具体的对象实例化过程打交道。
简单工厂模式包含以下几个角色:
-
工厂(Factory): 工厂类负责创建具体的对象实例。它通常包含一个静态方法,根据传入的参数来实例化不同类型的对象。
-
产品(Product): 产品是具体被创建的对象类型。工厂根据不同的参数来创建不同的产品。
-
客户端(Client): 客户端代码通过工厂类来获取所需的产品对象,而不需要直接与产品的实例化过程打交道。
下面是一个简单工厂模式的示例代码:
// 产品接口
interface Product {
void display();
}
// 具体产品A
class ConcreteProductA implements Product {
@Override
public void display() {
System.out.println("This is Product A.");
}
}
// 具体产品B
class ConcreteProductB implements Product {
@Override
public void display() {
System.out.println("This is Product B.");
}
}
// 工厂类
class Factory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ConcreteProductA();
} else if ("B".equals(type)) {
return new ConcreteProductB();
}
throw new IllegalArgumentException("Unsupported product type: " + type);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 通过工厂类创建产品对象
Product productA = Factory.createProduct("A");
productA.display();
Product productB = Factory.createProduct("B");
productB.display();
}
}
在这个示例中,工厂类 Factory
根据传入的参数来创建不同类型的产品对象,客户端通过调用工厂类的静态方法来获取所需的产品对象,而不需要直接与产品的实例化过程打交道。这样,客户端和具体产品的实现解耦合,使得代码更加灵活和可维护。
2、工厂方法模式
工厂方法模式(Factory Method Pattern),它提供了一个将对象的实例化延迟到子类的工厂方法中的解决方案。这意味着将对象的创建过程委托给子类来实现,从而使得客户端代码可以通过调用工厂方法来创建对象,而不需要直接与具体的对象类耦合。
工厂方法模式通常由一个抽象工厂类和多个具体工厂子类组成。抽象工厂类定义了一个工厂方法,用于创建产品对象,具体工厂子类负责实现工厂方法,并根据具体的需求来创建相应的产品对象。
工厂方法模式包含以下几个角色:
-
抽象产品(Product): 定义了产品对象的接口,描述了产品对象的行为。
-
具体产品(Concrete Product): 实现了抽象产品接口,具体产品对象由具体工厂子类来创建。
-
抽象工厂(Factory): 定义了一个抽象工厂方法,用于创建产品对象。
-
具体工厂(Concrete Factory): 实现了抽象工厂方法,根据具体的需求来创建具体的产品对象。
下面是一个工厂方法模式的示例代码:
// 抽象产品
interface Product {
void display();
}
// 具体产品A
class ConcreteProductA implements Product {
@Override
public void display() {
System.out.println("This is Product A.");
}
}
// 具体产品B
class ConcreteProductB implements Product {
@Override
public void display() {
System.out.println("This is Product B.");
}
}
// 抽象工厂
interface Factory {
Product createProduct();
}
// 具体工厂A
class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂B
class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建具体工厂A
Factory factoryA = new ConcreteFactoryA();
// 使用工厂A创建产品A
Product productA = factoryA.createProduct();
productA.display();
// 创建具体工厂B
Factory factoryB = new ConcreteFactoryB();
// 使用工厂B创建产品B
Product productB = factoryB.createProduct();
productB.display();
}
}
在这个示例中,抽象工厂 Factory
定义了一个抽象工厂方法 createProduct()
,具体工厂子类 ConcreteFactoryA
和 ConcreteFactoryB
分别实现了该工厂方法,并根据具体的需求来创建对应的产品对象。客户端代码通过调用具体工厂的工厂方法来获取所需的产品对象,而不需要直接与具体的产品类耦合。这样,客户端和具体产品的实现解耦合,使得代码更加灵活和可维护。
3、抽象工厂模式
抽象工厂模式(Abstract Factory Pattern),它提供了一个接口来创建一系列相关或相互依赖的对象,而不需要指定它们的具体类。抽象工厂模式通常由一个抽象工厂类和多个具体工厂类组成,每个具体工厂类负责创建一组相关的产品对象,而抽象工厂类定义了一个抽象的工厂方法,用于创建这些产品对象。
抽象工厂模式与工厂方法模式相比,它更加抽象和灵活,可以用于创建一组相关的产品对象,而不仅限于单个产品。因此,抽象工厂模式适用于需要创建一系列相关对象的场景。
抽象工厂模式包含以下几个角色:
-
抽象工厂(Abstract Factory): 定义了一个抽象的工厂方法,用于创建一系列相关的产品对象。
-
具体工厂(Concrete Factory): 实现了抽象工厂方法,负责创建一组相关的产品对象。
-
抽象产品(Abstract Product): 定义了一系列相关的产品对象的接口。
-
具体产品(Concrete Product): 实现了抽象产品接口,具体的产品对象由具体工厂类来创建。
下面是一个抽象工厂模式的示例代码:
// 抽象产品A
interface ProductA {
void display();
}
// 具体产品A1
class ConcreteProductA1 implements ProductA {
@Override
public void display() {
System.out.println("This is Product A1.");
}
}
// 具体产品A2
class ConcreteProductA2 implements ProductA {
@Override
public void display() {
System.out.println("This is Product A2.");
}
}
// 抽象产品B
interface ProductB {
void display();
}
// 具体产品B1
class ConcreteProductB1 implements ProductB {
@Override
public void display() {
System.out.println("This is Product B1.");
}
}
// 具体产品B2
class ConcreteProductB2 implements ProductB {
@Override
public void display() {
System.out.println("This is Product B2.");
}
}
// 抽象工厂
interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// 具体工厂1
class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
// 具体工厂2
class ConcreteFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建具体工厂1
AbstractFactory factory1 = new ConcreteFactory1();
// 使用工厂1创建产品A和产品B
ProductA productA1 = factory1.createProductA();
ProductB productB1 = factory1.createProductB();
productA1.display();
productB1.display();
// 创建具体工厂2
AbstractFactory factory2 = new ConcreteFactory2();
// 使用工厂2创建产品A和产品B
ProductA productA2 = factory2.createProductA();
ProductB productB2 = factory2.createProductB();
productA2.display();
productB2.display();
}
}
在这个示例中,抽象工厂 AbstractFactory
定义了两个抽象的工厂方法 createProductA()
和 createProductB()
,具体工厂子类 ConcreteFactory1
和 ConcreteFactory2
分别实现了这两个工厂方法,并根据具体的需求来创建对应的产品对象。客户端代码通过调用具体工厂的工厂方法来获取所需的产品对象,而不需要直接与具体的产品类耦合。这样,客户端和具体产品的实现解耦合,使得代码更加灵活和可维护。
4、单例模式
单例模式(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;
}
}
- 懒汉式单例(线程安全,但性能较差): 使用synchronized关键字保证了线程安全,但会影响性能。
public class Singleton {
private static Singleton instance;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 双重检查锁单例(Double-Checked Locking): 结合了懒汉式单例和饿汉式单例的优点,既保证了线程安全,又避免了性能问题。
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;
}
}
以上是几种常见的单例模式实现方式,每种方式都有其优缺点,选择适合自己需求的方式进行实现。
二、结构型模式
1、适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口,以使得原本不兼容的类可以协同工作。适配器模式通常用于以下情况:
- 当需要使用一个已经存在的类,但其接口与所需的接口不匹配时。
- 当需要复用一个类,但其接口与其他类的接口不兼容时。
- 当需要创建一个可以与多个类协同工作的类时。
适配器模式由以下几个角色组成:
-
目标接口(Target): 定义了客户端所期待的接口,适配器类通过实现该接口来与客户端进行交互。
-
适配器类(Adapter): 适配器类是适配器模式的核心,它通过实现目标接口,并包装或继承一个已存在的类,从而使得原本不兼容的类可以与客户端进行交互。
-
被适配者(Adaptee): 被适配者是需要被适配的类,它提供了客户端所需的功能,但其接口与目标接口不匹配。
下面是一个简单的适配器模式示例,假设有一个音频播放器类 AudioPlayer
,它可以播放MP3文件,但现在需要扩展它的功能,使得它可以播放其他格式的音频文件(如VLC和MP4),这时就可以使用适配器模式来实现:
// 目标接口
interface MediaPlayer {
void play(String audioType, String fileName);
}
// 具体实现类
class AudioPlayer implements MediaPlayer {
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file: " + fileName);
} else {
System.out.println("Unsupported format: " + audioType);
}
}
}
// 被适配者
class VlcPlayer {
public void playVlc(String fileName) {
System.out.println("Playing vlc file: " + fileName);
}
}
// 适配器类
class VlcPlayerAdapter implements MediaPlayer {
private VlcPlayer vlcPlayer;
public VlcPlayerAdapter(VlcPlayer vlcPlayer) {
this.vlcPlayer = vlcPlayer;
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
vlcPlayer.playVlc(fileName);
} else {
System.out.println("Unsupported format: " + audioType);
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
MediaPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "song.mp3");
MediaPlayer vlcPlayerAdapter = new VlcPlayerAdapter(new VlcPlayer());
vlcPlayerAdapter.play("vlc", "movie.vlc");
}
}
在这个示例中,AudioPlayer
是目标接口 MediaPlayer
的具体实现类,而 VlcPlayer
是被适配的类。VlcPlayerAdapter
是适配器类,它通过实现目标接口 MediaPlayer
,并包装了一个 VlcPlayer
对象,从而使得 VlcPlayer
能够与客户端代码进行交互。通过适配器模式,原本不兼容的 VlcPlayer
类可以被整合到客户端代码中,实现了功能的扩展和复用
2、装饰模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向一个对象动态地添加新的功能,同时又不改变其结构。装饰器模式通过将对象封装在一个装饰器类中,然后逐层地将装饰器类堆叠起来,从而实现对对象功能的逐步扩展。
在装饰器模式中,有几个关键角色:
-
组件接口(Component): 定义了被装饰对象的接口,可以是抽象类或接口。
-
具体组件(Concrete Component): 实现了组件接口的具体对象,是被装饰的原始对象。
-
装饰器(Decorator): 包含一个指向组件对象的引用,并实现了与组件接口相同的接口。装饰器通常会在其方法中调用被装饰对象的方法,并可以在调用前后添加额外的行为。
-
具体装饰器(Concrete Decorator): 实现了装饰器接口的具体装饰器类,负责对被装饰对象添加额外的功能。
下面是一个简单的装饰器模式示例,假设有一个窗口组件 Window
,我们希望能够动态地为窗口添加边框和滚动条功能:
// 组件接口
interface Window {
void draw();
}
// 具体组件
class SimpleWindow implements Window {
@Override
public void draw() {
System.out.println("Drawing a simple window.");
}
}
// 抽象装饰器
abstract class WindowDecorator implements Window {
protected Window window;
public WindowDecorator(Window window) {
this.window = window;
}
@Override
public void draw() {
window.draw();
}
}
// 具体装饰器:为窗口添加边框功能
class BorderDecorator extends WindowDecorator {
public BorderDecorator(Window window) {
super(window);
}
@Override
public void draw() {
super.draw();
System.out.println("Adding border to the window.");
}
}
// 具体装饰器:为窗口添加滚动条功能
class ScrollDecorator extends WindowDecorator {
public ScrollDecorator(Window window) {
super(window);
}
@Override
public void draw() {
super.draw();
System.out.println("Adding scrollbar to the window.");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建一个简单窗口
Window window = new SimpleWindow();
// 添加边框和滚动条功能
window = new BorderDecorator(window);
window = new ScrollDecorator(window);
// 绘制窗口
window.draw();
}
}
在这个示例中,Window
接口定义了窗口组件的基本行为。SimpleWindow
是具体的窗口组件,BorderDecorator
和 ScrollDecorator
是具体的装饰器,它们分别为窗口组件添加边框和滚动条功能。通过不断地嵌套装饰器,我们可以动态地扩展窗口的功能,而不需要修改原始组件的代码,这就是装饰器模式的核心思想。
3、代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,它允许我们创建一个代理对象来控制对其他对象的访问。代理对象通常充当被代理对象的中间人,客户端通过代理对象间接访问被代理对象,从而可以在访问过程中添加额外的功能,如权限控制、缓存、延迟加载等。
在代理模式中,通常有以下几个角色:
-
抽象主题(Subject): 定义了被代理对象和代理对象之间的共同接口,客户端通过该接口访问被代理对象。
-
具体主题(Real Subject): 实现了抽象主题接口的具体对象,是真正执行业务逻辑的对象。
-
代理(Proxy): 包含一个指向具体主题对象的引用,并实现了抽象主题接口。代理对象通常在其方法中调用具体主题对象的方法,并可以在调用前后添加额外的功能。
下面是一个简单的代理模式示例,假设有一个文件加载器 FileLoader
,我们希望能够通过代理来控制文件的访问权限:
// 抽象主题
interface File {
void load();
}
// 具体主题
class FileLoader implements File {
private String fileName;
public FileLoader(String fileName) {
this.fileName = fileName;
}
@Override
public void load() {
System.out.println("Loading file: " + fileName);
}
}
// 代理
class FileLoaderProxy implements File {
private String fileName;
private FileLoader fileLoader;
public FileLoaderProxy(String fileName) {
this.fileName = fileName;
}
@Override
public void load() {
// 在调用具体主题对象前可以添加额外的逻辑,如权限控制
if (fileLoader == null) {
fileLoader = new FileLoader(fileName);
}
fileLoader.load();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 直接通过具体主题对象加载文件
File file = new FileLoader("document.txt");
file.load();
// 通过代理对象加载文件
File proxy = new FileLoaderProxy("document.txt");
proxy.load();
}
}
在这个示例中,File
是抽象主题接口,定义了文件加载的方法。FileLoader
是具体主题对象,负责实际加载文件。FileLoaderProxy
是代理对象,它包含一个指向具体主题对象的引用,在调用具体主题对象的方法前可以添加额外的逻辑,如权限控制。通过使用代理对象,我们可以在访问文件时添加额外的功能,而无需修改原始对象的代码,这就是代理模式的优点。
三、行为型模式
1、策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使得它们可以相互替换。策略模式允许算法的变化独立于客户端使用算法的方式,从而使得客户端可以灵活地选择和切换不同的算法。
在策略模式中,有几个关键角色:
-
策略接口(Strategy): 定义了所有支持的算法的通用接口,它通常是一个接口或抽象类。
-
具体策略(Concrete Strategy): 实现了策略接口的具体算法实现。
-
上下文(Context): 维持一个对策略对象的引用,并在需要时调用策略对象的方法。上下文可以通过策略接口来调用具体策略的算法,也可以通过具体策略的实现类直接调用。
下面是一个简单的策略模式示例,假设有一个排序器 Sorter
,我们希望能够在运行时动态地选择不同的排序算法:
// 策略接口
interface SortStrategy {
void sort(int[] array);
}
// 具体策略:冒泡排序
class BubbleSort implements SortStrategy {
@Override
public void sort(int[] array) {
// 冒泡排序算法实现
System.out.println("Using bubble sort.");
}
}
// 具体策略:快速排序
class QuickSort implements SortStrategy {
@Override
public void sort(int[] array) {
// 快速排序算法实现
System.out.println("Using quick sort.");
}
}
// 上下文
class Sorter {
private SortStrategy strategy;
public Sorter(SortStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void performSort(int[] array) {
strategy.sort(array);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建排序器并指定初始策略
Sorter sorter = new Sorter(new BubbleSort());
// 使用冒泡排序
int[] array1 = {5, 2, 7, 1, 4};
sorter.performSort(array1);
// 切换为快速排序策略
sorter.setStrategy(new QuickSort());
// 使用快速排序
int[] array2 = {8, 3, 6, 9, 0};
sorter.performSort(array2);
}
}
在这个示例中,SortStrategy
是策略接口,定义了所有支持的排序算法的通用接口。BubbleSort
和 QuickSort
是具体策略类,分别实现了冒泡排序和快速排序算法。Sorter
是上下文类,维持一个对策略对象的引用,并在需要时调用策略对象的方法。客户端可以通过设置不同的策略来切换不同的排序算法,而不需要修改排序器的代码,从而实现了算法的动态切换,这就是策略模式的优点。
2、观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象(被观察者对象),当主题对象发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
在观察者模式中,有几个关键角色:
-
主题(Subject): 主题对象是被观察的对象,它包含了一组观察者对象,并提供了注册和移除观察者的方法,以及通知观察者的方法。
-
观察者(Observer): 观察者对象是依赖于主题的对象,它定义了一个更新接口,用于在主题状态发生变化时接收通知并更新自身。
-
具体主题(Concrete Subject): 具体主题对象是实际被观察的对象,它维护了一个观察者列表,并在状态变化时通知所有观察者。
-
具体观察者(Concrete Observer): 具体观察者对象是实际的观察者,它实现了观察者接口中的更新方法,并在接收到主题通知时执行相应的操作。
下面是一个简单的观察者模式示例,假设有一个气象站 WeatherStation
,我们希望能够让多个天气板 WeatherBoard
实时显示气象信息:
import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 观察者接口
interface Observer {
void update(String weatherInfo);
}
// 具体主题
class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
private String weatherInfo;
public void setWeatherInfo(String weatherInfo) {
this.weatherInfo = weatherInfo;
notifyObservers();
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(weatherInfo);
}
}
}
// 具体观察者
class WeatherBoard implements Observer {
private String boardName;
public WeatherBoard(String boardName) {
this.boardName = boardName;
}
@Override
public void update(String weatherInfo) {
System.out.println(boardName + " received weather update: " + weatherInfo);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建具体主题
WeatherStation weatherStation = new WeatherStation();
// 创建具体观察者
WeatherBoard board1 = new WeatherBoard("Board 1");
WeatherBoard board2 = new WeatherBoard("Board 2");
// 注册观察者
weatherStation.addObserver(board1);
weatherStation.addObserver(board2);
// 更新天气信息,观察者将收到通知并更新
weatherStation.setWeatherInfo("Sunny");
}
}
在这个示例中,Subject
是主题接口,定义了添加观察者、移除观察者和通知观察者的方法。Observer
是观察者接口,定义了更新方法。WeatherStation
是具体主题类,维护了一个观察者列表,并在更新天气信息时通知所有观察者。WeatherBoard
是具体观察者类,实现了更新方法,当收到天气更新通知时会更新自身的状态。通过观察者模式,可以实现主题与观察者之间的松耦合,让观察者能够自动响应主题的变化,从而实现了一种一对多的依赖关系。