在软件开发的世界里,有一种智慧结晶被誉为“设计模式”。它们并非现成的代码库,也非具体的编程语言特性,而是经过无数开发者实践检验并传承下来的代码设计经验和解决方案。设计模式是对重复出现的问题的通用描述,它为我们提供了一种标准的方法来解决这些设计上的挑战,从而提升代码的可读性、可维护性和复用性,进一步保证了软件系统的稳定可靠。
设计模式的多元化分类
设计模式按照其核心解决的问题和应用场景,大致划分为三大类别:创建型模式、结构型模式和行为型模式。
1. 创建型模式(5种)
- 简单工厂模式:如同流水线上的装配工,它为客户提供一个统一的接口,用来创建所需的产品对象,巧妙地隐藏了具体产品的生产细节。
- 抽象工厂模式:更进一步,抽象工厂负责生成一族相关或相互依赖的对象,允许你在不指定具体类的情况下,切换整个产品族
- 单例模式:犹如独一无二的王冠,确保一个类在整个系统中只存在一个实例,并提供全局访问点,特别适用于那些需要频繁实例化却又仅需单个实例的情景。
- 建造者模式:扮演建筑师的角色,将复杂对象的构建过程与最终的表示相分离,允许多种方式构造相同类型的不同表现形态。
- 原型模式:通过克隆现有实例的方式创建新对象,减轻了频繁使用“new”关键字带来的压力,尤其适用于大量相似对象的快速创建。
2. 结构型模式(7种)
- 代理模式:作为代理人,它为其它对象提供了间接访问的途径,从而更好地控制对象的访问权限和行为。
- 适配器模式:像电源转换插头一样,它将一个类的接口转换为另一种接口,让原本因接口不匹配而无法协作的类之间达成和谐共生。
- 桥接模式:如同建筑中的桥梁,它分离了抽象部分与其实现部分,使得两者可以相对独立地变化发展。
- 装饰器模式:赋予对象增加新功能的能力,如同给家具贴金箔或镶嵌宝石,使功能增强变得灵活而不必更改原有类结构。
- 外观模式(门面模式):担当系统面向外部的友好界面,简化了子系统的使用难度,使得复杂的内部结构对外呈现简洁统一的形象。
- 组合模式:模拟现实世界中的“整体-部分”关系,使得单个对象和复合对象都能以一致的方式被处理。
- 享元模式:通过共享对象来高效支持大量细粒度的对象,从而节省内存资源,提升系统性能。
3. 行为型模式(11种)
- 策略模式:像是战术手册,它定义了一系列可互换的算法策略,使得算法的选择能够在运行时根据具体情况灵活变动。
- 观察者模式:构建了一个发布-订阅机制,当对象状态发生变化时,所有依赖它的对象都会收到通知并做出相应的响应。
- 责任链模式:形成一条处理请求的责任链条,每个对象都有机会处理请求,降低了请求发送者和接收者的直接耦合度。
- 模板方法模式:如同制作美食的菜谱,它在抽象类中定义了一个基本算法框架,允许子类重定义其中的特定步骤,实现了算法的部分个性化。
- 状态模式:允许对象在其内部状态改变时,相应地改变其行为,宛如物体随温度变化呈现出不同的物理性质。
- 访问者模式:在不改动对象自身结构的前提下,为对象引入新的操作,就如同为已有的展品馆增设新的解说员。
- 中介模式:扮演信息交换中心的角色,解耦了对象间的直接交互,使得它们可以通过中介间接通信,增强了系统的灵活性。
- 命令模式:将操作封装为可携带的命令对象,便于命令的存储、传递、执行和撤销,提升了系统功能的可扩展性。
- 备忘录模式:仿佛时光机器,它在不违反封装原则的前提下,捕获并储存对象的内部状态,允许在适当时候恢复至先前的状态。
- 迭代器模式:提供一种遍历聚合对象元素的标准方式,让用户可以透明地访问容器内的数据,无需关心其内部结构。
- 解释器模式:创建了一个表达式解析环境,允许按照预定义的文法解释特定的语言结构。
设计模式的代码实例及优缺点
创建型模式
1.简单工厂模式:
简单工厂模式是一种创建型设计模式,它提供一个专门的类(工厂类)来负责创建对象,而客户端只需传入参数,无需了解对象的具体创建过程
假设我们有一个动物工厂,可以根据传入的类型字符串创建不同的动物对象:
// 定义抽象产品 - 动物接口
public interface Animal {
void makeSound();
}
// 具体产品 - 猫类
public class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("The cat says: Meow");
}
}
// 具体产品 - 狗类
public class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("The dog says: Woof");
}
}
// 工厂类
public class AnimalFactory {
// 简单工厂方法,根据传入的类型字符串返回对应的动物对象
public static Animal createAnimal(String type) {
if ("cat".equalsIgnoreCase(type)) {
return new Cat();
} else if ("dog".equalsIgnoreCase(type)) {
return new Dog();
} else {
throw new IllegalArgumentException("Invalid animal type");
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 根据字符串"cat"创建猫对象
Animal myCat = AnimalFactory.createAnimal("cat");
myCat.makeSound();
// 根据字符串"dog"创建狗对象
Animal myDog = AnimalFactory.createAnimal("dog");
myDog.makeSound();
}
}
在这个例子中,AnimalFactory 类就是一个简单工厂,它根据传入的字符串参数决定创建哪种类型的 Animal 对象。客户端代码无需知道如何创建 Cat 或 Dog 实例,只需调用 AnimalFactory 的静态方法 createAnimal() 并传入适当的参数即可。
2.抽象工厂模式:
抽象工厂模式,它提供一个接口,用于创建相关或依赖对象家族的一个系列对象,而无需指定具体类。以下是一个简单的Java抽象工厂模式示例,假设我们有一个食品工厂,可以生产意大利风味和墨西哥风味的披萨和饮料:
// 抽象产品 - 披萨接口
public interface Pizza {
void prepare();
void bake();
void cut();
void box();
}
// 抽象产品 - 饮料接口
public interface Drink {
void make();
}
// 具体产品 - 意大利披萨
public class ItalianPizza implements Pizza {
@Override
public void prepare() {
System.out.println("Preparing Italian pizza...");
}
@Override
public void bake() {
System.out.println("Baking Italian pizza...");
}
@Override
public void cut() {
System.out.println("Cutting Italian pizza...");
}
@Override
public void box() {
System.out.println("Boxing Italian pizza...");
}
}
// 具体产品 - 墨西哥披萨
public class MexicanPizza implements Pizza {
@Override
public void prepare() {
System.out.println("Preparing Mexican pizza...");
}
@Override
public void bake() {
System.out.println("Baking Mexican pizza...");
}
@Override
public void cut() {
System.out.println("Cutting Mexican pizza...");
}
@Override
public void box() {
System.out.println("Boxing Mexican pizza...");
}
}
// 具体产品 - 意大利饮料
public class ItalianDrink implements Drink {
@Override
public void make() {
System.out.println("Making Italian drink...");
}
}
// 具体产品 - 墨西哥饮料
public class MexicanDrink implements Drink {
@Override
public void make() {
System.out.println("Making Mexican drink...");
}
// 抽象工厂
public interface FoodFactory {
Pizza createPizza();
Drink createDrink();
}
// 具体工厂 - 意大利食品工厂
public class ItalianFoodFactory implements FoodFactory {
@Override
public Pizza createPizza() {
return new ItalianPizza();
}
@Override
public Drink createDrink() {
return new ItalianDrink();
}
}
// 具体工厂 - 墨西哥食品工厂
public class MexicanFoodFactory implements FoodFactory {
@Override
public Pizza createPizza() {
return new MexicanPizza();
}
@Override
public Drink createDrink() {
return new MexicanDrink();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
FoodFactory italianFactory = new ItalianFoodFactory();
Pizza italianPizza = italianFactory.createPizza();
italianPizza.prepare();
italianPizza.bake();
italianPizza.cut();
italianPizza.box();
Drink italianDrink = italianFactory.createDrink();
italianDrink.make();
System.out.println("\n");
FoodFactory mexicanFactory = new MexicanFoodFactory();
Pizza mexicanPizza = mexicanFactory.createPizza();
mexicanPizza.prepare();
mexicanPizza.bake();
mexicanPizza.cut();
mexicanPizza.box();
Drink mexicanDrink = mexicanFactory.createDrink();
mexicanDrink.make();
}
}
在这个例子中,ItalianFoodFactory 和 MexicanFoodFactory 是两个具体工厂,它们分别实现了 FoodFactory 接口,可以根据需要创建意大利风味或墨西哥风味的披萨和饮料。客户端代码通过选择不同的工厂来获取不同口味的食品,而无需关心具体的创建过程。
3.单例模式
单例模式它确保一个类只有一个实例,并提供一个全局访问点。以下是Java单例模式的两种实现方式:饿汉式和懒汉式的实例:
饿汉式单例
public class SingletonHungry {
// 创建 SingletonHungry 类的一个对象
private static final SingletonHungry INSTANCE = new SingletonHungry();
// 让构造函数为 private,这样该类就不会被实例化
private SingletonHungry() {}
// 获取唯一可用的对象
public static SingletonHungry getInstance() {
return INSTANCE;
}
// 示例方法
public void showMessage() {
System.out.println("Hello from SingletonHungry");
}
}
懒汉式单例(线程不安全)
public class SingletonLazyUnsafe {
private static SingletonLazyUnsafe instance;
private SingletonLazyUnsafe() {}
public static SingletonLazyUnsafe getInstance() {
if (instance == null) {
instance = new SingletonLazyUnsafe();
}
return instance;
}
public void showMessage() {
System.out.println("Hello from SingletonLazyUnsafe");
}
}
懒汉式单例(线程安全,同步方法)
public class SingletonLazySafe {
private static SingletonLazySafe instance;
private SingletonLazySafe() {}
public static synchronized SingletonLazySafe getInstance() {
if (instance == null) {
instance = new SingletonLazySafe();
}
return instance;
}
public void showMessage() {
System.out.println("Hello from SingletonLazySafe");
}
}
特点与优缺点
特点:
- 单例模式确保在整个应用程序中,该类只会被实例化一次。
- 提供全局访问点,可以直接通过类的静态方法获取实例。
优点:
- 控制共享资源的访问,比如数据库连接、配置文件读取等。
- 降低系统内存开销,因为只有一个实例在内存中。
缺点:
- 单例模式一般没有接口,扩展困难。如果要扩展则需要修改原来的代码,违背开闭原则。
- 在并发环境下,懒汉式单例如果不加锁会导致产生多个实例,破坏单例。
饿汉式 vs 懒汉式区别:
- 饿汉式:类加载时就完成了初始化,线程安全,getInstance() 方法不会进行任何同步操作,因此效率较高。但如果 Singleton 类加载后并未使用,仍会占据内存,可能造成浪费。
- 懒汉式:
线程不安全:在多线程环境下,如果多个线程同时进入 getInstance() 方法,可能会创建多个实例,违反单例原则。
线程安全(同步方法):解决了多线程环境下的实例化问题,但每次调用 getInstance() 方法都需要进行同步,导致性能下降,尤其是高并发场景下影响较大。
总的来说,饿汉式在多线程环境下的性能较好,而懒汉式(线程安全版本)在单线程或多线程环境下也能确保单例,但在并发高的场景下可能会有性能瓶颈。现代 Java 开发中推荐使用双重检查锁定(Double-Check Locking)的方式来实现线程安全且延迟加载的懒汉式单例。
4.建造者模式
建造者模式(Builder Pattern)主要用于创建复杂对象的构建与表示分离,使得相同的构建过程可以创建不同的表示。下面是一个简单的Java实现示例,我们将创建一个计算机组装过程,包含CPU、内存、硬盘等组件,通过不同的建造者创建不同配置的计算机。
首先,定义抽象产品(Computer)和相关的组件:
// 抽象产品 - 计算机
public abstract class Computer {
protected String cpu;
protected int ram;
protected String hardDrive;
public String getCpu() {
return cpu;
}
public int getRam() {
return ram;
}
public String getHardDrive() {
return hardDrive;
}
public abstract double getPrice();
}
// 具体产品 - 低端计算机
public class BasicComputer extends Computer {
private double price;
public BasicComputer(String cpu, int ram, String hardDrive, double basePrice) {
this.cpu = cpu;
this.ram = ram;
this.hardDrive = hardDrive;
this.price = basePrice;
}
@Override
public double getPrice() {
return price;
}
}
// 具体产品 - 高端计算机
public class AdvancedComputer extends Computer {
private double price;
public AdvancedComputer(String cpu, int ram, String hardDrive, double basePrice) {
this.cpu = cpu;
this.ram = ram;
this.hardDrive = hardDrive;
this.price = basePrice + 500; // 假设高端计算机价格高于基础款500元
}
@Override
public double getPrice() {
return price;
}
}
// 抽象建造者
public abstract class ComputerBuilder {
protected Computer computer;
public abstract void buildCpu(String cpu);
public abstract void buildRam(int ram);
public abstract void buildHardDrive(String hardDrive);
public Computer getComputer() {
return computer;
}
}
// 具体建造者 - 低端计算机建造者
public class BasicComputerBuilder extends ComputerBuilder {
public BasicComputerBuilder() {
computer = new BasicComputer("", 0, "", 0);
}
@Override
public void buildCpu(String cpu) {
((BasicComputer) computer).cpu = cpu;
}
@Override
public void buildRam(int ram) {
((BasicComputer) computer).ram = ram;
}
@Override
public void buildHardDrive(String hardDrive) {
((BasicComputer) computer).hardDrive = hardDrive;
((BasicComputer) computer).price = calculatePrice();
}
private double calculatePrice() {
// 假设定价规则
return 3000;
}
}
// 具体建造者 - 高端计算机建造者
public class AdvancedComputerBuilder extends ComputerBuilder {
public AdvancedComputerBuilder() {
computer = new AdvancedComputer("", 0, "", 0);
}
@Override
public void buildCpu(String cpu) {
((AdvancedComputer) computer).cpu = cpu;
}
@Override
public void buildRam(int ram) {
((AdvancedComputer) computer).ram = ram;
}
@Override
public void buildHardDrive(String hardDrive) {
((AdvancedComputer) computer).hardDrive = hardDrive;
((AdvancedComputer) computer).price = calculatePrice();
}
private double calculatePrice() {
// 假设定价规则
return 6000;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
ComputerBuilder basicBuilder = new BasicComputerBuilder();
basicBuilder.buildCpu("Intel i5");
basicBuilder.buildRam(8);
basicBuilder.buildHardDrive("500GB SSD");
Computer basicComputer = basicBuilder.getComputer();
System.out.println("Basic Computer Price: " + basicComputer.getPrice());
ComputerBuilder advancedBuilder = new AdvancedComputerBuilder();
advancedBuilder.buildCpu("Intel i9");
advancedBuilder.buildRam(32);
advancedBuilder.buildHardDrive("1TB SSD");
Computer advancedComputer = advancedBuilder.getComputer();
System.out.println("Advanced Computer Price: " + advancedComputer.getPrice());
}
}
在这个示例中,抽象产品是Computer类及其子类,而ComputerBuilder是抽象建造者,BasicComputerBuilder和AdvancedComputerBuilder是具体的建造者。客户端代码通过建造者设置计算机的各种属性,最后获取组装好的计算机实例。
5.原型模式
原型模式(Prototype Pattern)它允许通过复制现有的对象来创建新的对象,而不是每次都通过构造函数来创建新的对象实例。以下是一个简单的Java实现原型模式的示例,我们将创建一个形状类(Shape)和几个具体的形状类(如Rectangle和Circle),并实现克隆方法以实现原型模式。
// 抽象原型类
public abstract class Shape implements Cloneable {
protected String id;
public Shape(String id) {
this.id = id;
}
public abstract void draw();
// 实现克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getId() {
return id;
}
}
// 具体原型类 - 矩形
public class Rectangle extends Shape {
private int width;
private int height;
public Rectangle(String id, int width, int height) {
super(id);
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("Drawing rectangle with ID: " + getId() + ", width: " + width + ", height: " + height);
}
// 可以根据需要覆盖父类的clone方法,确保深拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
Rectangle clonedRectangle = (Rectangle) super.clone();
// 如果类中包含引用类型变量,此处需要进行深拷贝处理
// 但在这个简单例子中,假设width和height是基本类型,所以默认clone()方法足够
return clonedRectangle;
}
}
// 具体原型类 - 圆形
public class Circle extends Shape {
private int radius;
public Circle(String id, int radius) {
super(id);
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing circle with ID: " + getId() + ", radius: " + radius);
}
@Override
protected Object clone() throws CloneNotSupportedException {
Circle clonedCircle = (Circle) super.clone();
// 同理,如果有引用类型变量,此处需要深拷贝
return clonedCircle;
}
}
// 客户端代码
public class PrototypePatternDemo {
public static void main(String[] args) {
try {
// 创建原始矩形和圆形
Shape rectangle = new Rectangle("Rect1", 10, 20);
Shape circle = new Circle("Cir1", 5);
// 使用原型模式克隆对象
Shape clonedRectangle = (Shape) rectangle.clone();
Shape clonedCircle = (Shape) circle.clone();
// 修改克隆对象的ID,验证是否成功克隆
clonedRectangle.setId("Rect2");
clonedCircle.setId("Cir2");
// 输出原始对象和克隆对象的信息
rectangle.draw();
clonedRectangle.draw();
circle.draw();
clonedCircle.draw();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
在这个示例中,Shape是抽象原型类,它定义了克隆方法。Rectangle和Circle是具体原型类,它们实现了抽象原型类的方法并提供了自己的克隆实现。客户端代码通过调用.clone()方法创建了原始对象的副本,改变了克隆对象的ID,验证了原型模式的有效性。
结构型模式
1.代理模式
代理模式(Proxy Pattern)它为其他对象提供一种代理以控制对这个对象的访问。这里是一个简单的Java代理模式实例,我们将创建一个图书服务接口、真实的图书服务类以及一个代理图书服务类,代理类将在访问真实服务之前添加额外的功能,例如权限检查或日志记录。
// 抽象主题(Subject)
public interface BookService {
void borrowBook(String bookId);
void returnBook(String bookId);
}
// 真实主题(Real Subject)
public class RealBookService implements BookService {
@Override
public void borrowBook(String bookId) {
System.out.println("Borrowing book with ID: " + bookId);
}
@Override
public void returnBook(String bookId) {
System.out.println("Returning book with ID: " + bookId);
}
}
// 代理主题(Proxy)
public class AccessControlBookService implements BookService {
private RealBookService realBookService;
public AccessControlBookService() {
this.realBookService = new RealBookService();
}
@Override
public void borrowBook(String bookId) {
checkAccess();
realBookService.borrowBook(bookId);
logAction("Borrowed book with ID: " + bookId);
}
@Override
public void returnBook(String bookId) {
checkAccess();
realBookService.returnBook(bookId);
logAction("Returned book with ID: " + bookId);
}
private void checkAccess() {
// 模拟权限检查
System.out.println("Checking user access permissions...");
// 如果无权访问,则抛出异常或拒绝服务
// ...
}
private void logAction(String action) {
// 模拟日志记录
System.out.println("Logging action: " + action);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
BookService bookService = new AccessControlBookService();
// 用户尝试借阅和归还书籍
bookService.borrowBook("book1");
bookService.returnBook("book1");
}
}
在这个示例中,RealBookService 是真实的服务类,它实现了 BookService 接口并提供了实际的借书和还书功能。AccessControlBookService 是代理类,它同样实现了 BookService 接口,并在调用实际服务之前增加了权限检查和日志记录功能。客户端代码通过代理类来间接访问真实服务,从而实现了对真实服务访问的控制和增强。
2.适配器模式
适配器模式(Adapter Pattern)它允许将一个类的接口转换成客户希望的另一个接口,从而使原本不兼容的类可以协同工作。以下是一个使用Java编写的适配器模式实例,我们将有一个接口MediaPlayer和一个实现该接口的类MediaPlayerImpl,以及一个现有类AudioPlayer,它拥有不同的播放音乐的方法。适配器AudioPlayerAdapter将会适配AudioPlayer类,使之满足MediaPlayer接口的要求。
// 原始接口,客户期望的工作方式
public interface MediaPlayer {
void play(String audioType, String fileName);
}
// 已有的音频播放器类,其方法不符合客户期望的接口
public class AudioPlayer {
public void playMP3(String fileName) {
System.out.println("Playing MP3 file: " + fileName);
}
public void playWAV(String fileName) {
System.out.println("Playing WAV file: " + fileName);
}
}
// 实现了MediaPlayer接口的类,但在此场景中不是我们想复用的类
public class MediaPlayerImpl implements MediaPlayer {
@Override
public void play(String audioType, String fileName) {
System.out.println("Playing media file: " + fileName + " of type: " + audioType);
}
}
// 适配器类,将AudioPlayer类适配到MediaPlayer接口
public class AudioPlayerAdapter implements MediaPlayer {
private AudioPlayer adaptee;
public AudioPlayerAdapter(AudioPlayer player) {
this.adaptee = player;
}
@Override
public void play(String audioType, String fileName) {
if ("mp3".equalsIgnoreCase(audioType)) {
adaptee.playMP3(fileName);
} else if ("wav".equalsIgnoreCase(audioType)) {
adaptee.playWAV(fileName);
} else {
System.out.println("Unsupported audio format.");
}
}
}
// 客户端代码
public class AdapterPatternDemo {
public static void main(String[] args) {
// 创建原始音频播放器对象
AudioPlayer audioPlayer = new AudioPlayer();
// 创建适配器并传入原始音频播放器
MediaPlayer mediaPlayer = new AudioPlayerAdapter(audioPlayer);
// 使用适配后的接口播放音乐
mediaPlayer.play("mp3", "song.mp3");
mediaPlayer.play("wav", "song.wav");
}
}
在上述例子中,AudioPlayerAdapter 类作为一个适配器,它实现了 MediaPlayer 接口,并持有一个 AudioPlayer 类型的对象,通过在 play 方法中调用 AudioPlayer 对象的相应方法,实现了对不同音频格式的支持,使得 AudioPlayer 能够以 MediaPlayer 接口的形式被外部调用。
3.桥梁模式:
桥梁模式(Bridge Pattern)它将抽象部分与它的实现部分分离,使它们都可以独立地变化。下面是一个使用Java编写的桥梁模式实例,我们将创建一个图形接口,以及颜色接口,然后创建实现这两种接口的具体类,并通过桥接模式使得颜色和图形可以自由组合。
// 抽象部分 - 图形接口
public interface Shape {
void draw(Color color);
}
// 抽象部分 - 颜色接口
public interface Color {
void fill();
}
// 实现部分 - 具体图形
public class Circle implements Shape {
@Override
public void draw(Color color) {
System.out.println("Drawing a circle with color:");
color.fill();
}
}
public class Square implements Shape {
@Override
public void draw(Color color) {
System.out.println("Drawing a square with color:");
color.fill();
}
}
// 实现部分 - 具体颜色
public class Red implements Color {
@Override
public void fill() {
System.out.println("Red color filled.");
}
}
public class Blue implements Color {
@Override
public void fill() {
System.out.println("Blue color filled.");
}
}
// 客户端代码
public class BridgePatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
Shape square = new Square();
// 组合不同的颜色和形状
circle.draw(new Red());
circle.draw(new Blue());
square.draw(new Red());
square.draw(new Blue());
}
}
在这个示例中,Shape 是抽象部分,它定义了图形绘制接口。Color 也是抽象部分,它定义了填充颜色的接口。Circle 和 Square 是图形的具体实现类,它们实现了 Shape 接口。Red 和 Blue 是颜色的具体实现类,它们实现了 Color 接口。通过桥接模式,我们可以独立地改变图形的种类和颜色,二者的变化互不影响。在客户端代码中,我们就可以随意组合各种图形和颜色。
4.装饰模式
装饰模式(Decorator Pattern)它允许在运行时给对象动态地添加新的行为,同时不会改变对象类。下面是一个简单的Java装饰模式实例,我们将创建一个基础咖啡类,然后通过装饰器来增加额外的功能,比如糖分、奶泡等。
// 基础咖啡类(Component)
public abstract class Coffee {
protected double cost;
public abstract double getCost();
public abstract String getDescription();
}
// 具体的咖啡类(Concrete Component)
public class SimpleCoffee extends Coffee {
public SimpleCoffee() {
this.cost = 1.0; // 假设基础咖啡价格为1元
}
@Override
public double getCost() {
return cost;
}
@Override
public String getDescription() {
return "Simple Coffee";
}
}
// 装饰器抽象类(Decorator)
public abstract class CoffeeDecorator extends Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public double getCost() {
return coffee.getCost();
}
@Override
public String getDescription() {
return coffee.getDescription();
}
}
// 具体装饰器类(Concrete Decorators)
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
// 添加糖的成本
this.cost = coffee.getCost() + 0.5;
}
@Override
public double getCost() {
return super.getCost();
}
@Override
public String getDescription() {
return coffee.getDescription() + ", with sugar";
}
}
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
// 添加牛奶的成本
this.cost = coffee.getCost() + 1.0;
}
@Override
public double getCost() {
return super.getCost();
}
@Override
public String getDescription() {
return coffee.getDescription() + ", with milk";
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Coffee simpleCoffee = new SimpleCoffee();
System.out.println(simpleCoffee.getDescription() + ": " + simpleCoffee.getCost());
Coffee coffeeWithSugar = new SugarDecorator(simpleCoffee);
System.out.println(coffeeWithSugar.getDescription() + ": " + coffeeWithSugar.getCost());
Coffee coffeeWithMilkAndSugar = new MilkDecorator(coffeeWithSugar);
System.out.println(coffeeWithMilkAndSugar.getDescription() + ": " + coffeeWithMilkAndSugar.getCost());
}
}
在这个例子中,Coffee 是抽象组件,SimpleCoffee 是具体组件。CoffeeDecorator 是抽象装饰器,而 SugarDecorator 和 MilkDecorator 是具体的装饰器。在客户端代码中,我们可以通过装饰器动态地给基础咖啡添加糖和牛奶,从而得到不同口味和价格的咖啡。
InputStream代表了输入流,具体的输入来源可以是 FileInputStream(文件),PipedInputStream(管道),ByteArrayInputStream(数组)
FileInputStream承接了InputStream的装饰模式的关键节点,他的实现类是一系列装饰器
- BufferedInputStream 代表用缓冲来进行装饰,使输入流具有缓冲功能
- LineNumberInputStream 代表用行号来进行装饰,操作输入流的时候就可以取得行号
- DataInputStream 的装饰,使得我们可以从输入流转换为java中的基本类型的值
5.门面模式
门面模式(Facade Pattern)它为子系统中的一组接口提供了一个统一的接口,简化了子系统与客户端之间的交互。下面是一个使用Java编写的门面模式实例,我们将模拟一个复杂的音响系统的操作,其中包括CD播放器、DVD播放器和广播接收器等子系统,通过一个音响控制系统门面类来简化用户操作。
// 子系统 - CD播放器
public class CDPlayer {
public void playCD() {
System.out.println("Playing CD...");
}
public void stopCD() {
System.out.println("Stopping CD...");
}
public void ejectCD() {
System.out.println("Ejecting CD...");
}
}
// 子系统 - DVD播放器
public class DVDPlayer {
public void playDVD() {
System.out.println("Playing DVD...");
}
public void stopDVD() {
System.out.println("Stopping DVD...");
}
public void ejectDVD() {
System.out.println("Ejecting DVD...");
}
}
// 子系统 - 广播接收器
public class Tuner {
public void turnOnRadio() {
System.out.println("Turning on radio...");
}
public void turnOffRadio() {
System.out.println("Turning off radio...");
}
public void setFrequency(int frequency) {
System.out.println("Setting frequency to " + frequency + " MHz...");
}
}
// 门面类 - 音响控制系统
public class AudioSystemFacade {
private CDPlayer cdPlayer;
private DVDPlayer dvdPlayer;
private Tuner tuner;
public AudioSystemFacade() {
cdPlayer = new CDPlayer();
dvdPlayer = new DVDPlayer();
tuner = new Tuner();
}
// 提供统一的接口来操作子系统
public void playCD() {
cdPlayer.playCD();
}
public void stopCD() {
cdPlayer.stopCD();
}
public void ejectCD() {
cdPlayer.ejectCD();
}
public void playDVD() {
dvdPlayer.playDVD();
}
public void stopDVD() {
dvdPlayer.stopDVD();
}
public void ejectDVD() {
dvdPlayer.ejectDVD();
}
public void turnOnRadio() {
tuner.turnOnRadio();
}
public void turnOffRadio() {
tuner.turnOffRadio();
}
public void setRadioFrequency(int frequency) {
tuner.setFrequency(frequency);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
AudioSystemFacade audioSystem = new AudioSystemFacade();
audioSystem.playCD();
audioSystem.turnOnRadio();
audioSystem.setRadioFrequency(90.5);
audioSystem.playDVD();
audioSystem.stopCD();
audioSystem.ejectDVD();
audioSystem.turnOffRadio();
}
}
在这个例子中,CDPlayer、DVDPlayer 和 Tuner 是子系统类,而 AudioSystemFacade 是门面类,它封装了子系统类的操作,提供了一个更简洁易用的接口给客户端。在客户端代码中,用户可以直接通过音响控制系统门面类控制整个音响系统,而无需直接操作各个子系统,降低了系统的复杂性。
6.组合模式
组合模式(Composite Pattern)它让客户端可以透明地处理单个对象和组合对象。下面是一个使用Java编写的组合模式实例,我们将模拟一个文件系统,其中包含文件(leaf)和文件夹(composite)两种对象,它们都继承自一个共同的抽象组件接口。
// 抽象组件
public abstract class FileSystemComponent {
private String name;
public FileSystemComponent(String name) {
this.name = name;
}
public String getName() {
return name;
}
// 抽象方法
public abstract void add(FileSystemComponent component);
public abstract void remove(FileSystemComponent component);
public abstract void print();
// 默认空实现
public void printDetails() {
// 文件对象不需要此方法,但在组合对象中可以重写
}
}
// 叶子组件 - 文件
public class File extends FileSystemComponent {
public File(String name) {
super(name);
}
@Override
public void add(FileSystemComponent component) {
throw new UnsupportedOperationException("Files cannot have children.");
}
@Override
public void remove(FileSystemComponent component) {
throw new UnsupportedOperationException("Files cannot have children.");
}
@Override
public void print() {
System.out.println("- " + this.getName());
printDetails();
}
@Override
public void printDetails() {
System.out.println("File details: This is a plain file.");
}
}
// 组合组件 - 文件夹
public class Directory extends FileSystemComponent {
private List<FileSystemComponent> children;
public Directory(String name) {
super(name);
this.children = new ArrayList<>();
}
@Override
public void add(FileSystemComponent component) {
this.children.add(component);
}
@Override
public void remove(FileSystemComponent component) {
this.children.remove(component);
}
@Override
public void print() {
System.out.println("+ " + this.getName());
for (FileSystemComponent child : children) {
child.print();
}
}
@Override
public void printDetails() {
System.out.println("Directory details: This is a directory containing:");
for (FileSystemComponent child : children) {
System.out.println(child.getName());
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Directory root = new Directory("root");
Directory documents = new Directory("documents");
File textFile = new File("example.txt");
root.add(documents);
documents.add(textFile);
root.print();
root.printDetails();
}
}
在这个例子中,FileSystemComponent 是抽象组件,包含了所有组件共有的行为。File 类代表叶子组件,无法添加或删除子组件。而 Directory 类代表组合组件,它可以包含其他的 FileSystemComponent 对象。客户端代码展示了如何透明地处理根目录、子目录以及文件的打印操作,无论是打印文件夹还是打印文件,都是通过调用 print() 方法完成的。
7.享元模式
享元模式(Flyweight Pattern)它主要用于减少大量细粒度对象的创建,通过共享内部状态较小的对象来节省内存。以下是一个使用Java编写的享元模式实例,我们将模拟一个画布上绘制多种颜色的小圆点,通过共享颜色对象来减少内存消耗。
// 抽象享元接口
public interface Shape {
void draw(GraphicsContext gc, int x, int y);
}
// 内部状态(非共享状态)
class CircleState {
private Color color;
CircleState(Color color) {
this.color = color;
}
Color getColor() {
return color;
}
}
// 具体享元类 - 小圆点
public class Circle implements Shape {
private static Map<Color, Circle> circles = new HashMap<>();
private CircleState state;
private Circle(CircleState state) {
this.state = state;
}
// 获取享元对象
public static Circle getCircle(Color color) {
if (!circles.containsKey(color)) {
circles.put(color, new Circle(new CircleState(color)));
}
return circles.get(color);
}
@Override
public void draw(GraphicsContext gc, int x, int y) {
gc.setFill(state.getColor());
gc.fillOval(x, y, 10, 10);
}
}
// 客户端代码
public class FlyweightPatternDemo {
public static void main(String[] args) {
GraphicsContext gc = ...; // 假设这是绘图上下文
// 创建大量不同位置但颜色相同的小圆点
for (int i = 0; i < 1000; i++) {
Circle circle = Circle.getCircle(Color.RED); // 重复的颜色只创建一个共享对象
circle.draw(gc, i * 15, i * 15);
}
}
}
在这个例子中,Circle 类是具体享元类,它实现了 Shape 抽象享元接口。Circle 类通过维护一个静态的哈希表 circles 来存储已创建的具有相同内部状态(颜色)的对象,新请求的颜色小圆点如果已经存在于哈希表中,则直接返回已存在的共享对象,避免了重复创建。在客户端代码中,尽管我们创建了大量的小圆点,但由于它们共享了颜色相同的内部状态,因此内存占用得到了有效的控制。
行为型模式
1.策略模式
策略模式允许我们在运行时改变对象的行为,通过将行为(算法)封装成一系列可互换的策略类。下面是一个使用Java编写的策略模式实例,假设有一个电商系统,其中包含不同的促销折扣计算策略,比如普通折扣、满减优惠、买一赠一等。
// 策略接口
interface DiscountStrategy {
double calculateDiscount(double originalPrice, int quantity);
}
// 具体策略类 - 普通折扣
class NormalDiscountStrategy implements DiscountStrategy {
@Override
public double calculateDiscount(double originalPrice, int quantity) {
return originalPrice * 0.9; // 九折优惠
}
}
// 具体策略类 - 满减优惠
class FullReductionStrategy implements DiscountStrategy {
private final double threshold;
private final double deduction;
public FullReductionStrategy(double threshold, double deduction) {
this.threshold = threshold;
this.deduction = deduction;
}
@Override
public double calculateDiscount(double originalPrice, int quantity) {
return quantity * originalPrice >= threshold ? originalPrice * quantity - deduction : originalPrice * quantity;
}
}
// 具体策略类 - 买一赠一
class BuyOneGetOneFreeStrategy implements DiscountStrategy {
@Override
public double calculateDiscount(double originalPrice, int quantity) {
int freeItems = quantity / 2; // 只考虑整除的情况,简化示例
return (quantity - freeItems) * originalPrice;
}
}
// 上下文类,管理并使用策略
class ShoppingCart {
private DiscountStrategy discountStrategy;
public ShoppingCart(DiscountStrategy strategy) {
this.discountStrategy = strategy;
}
public void setDiscountStrategy(DiscountStrategy strategy) {
this.discountStrategy = strategy;
}
public double getTotalPrice(double itemPrice, int itemCount) {
return discountStrategy.calculateDiscount(itemPrice, itemCount);
}
// 示例用法
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart(new NormalDiscountStrategy());
// 更改策略
cart.setDiscountStrategy(new FullReductionStrategy(100.0, 20.0));
// 使用当前策略计算总价
double totalPrice = cart.getTotalPrice(50.0, 3);
System.out.println("Total Price after discount: " + totalPrice);
}
}
在这个实例中:
- DiscountStrategy 是策略接口,定义了所有策略共有的业务方法 calculateDiscount。
- NormalDiscountStrategy, FullReductionStrategy, 和 BuyOneGetOneFreeStrategy 是三个具体策略类,每个都实现了 DiscountStrategy 接口,提供了不同的折扣计算逻辑。
- ShoppingCart 是上下文类,持有一个策略对象,并可以通过 setDiscountStrategy 方法在运行时切换不同的折扣策略,然后调用 getTotalPrice 方法利用当前策略来计算购物车中商品的总折扣价。
2.观察者模式
观察者模式(Observer Pattern)它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。下面是一个使用Java编写的观察者模式实例,我们将模拟天气预报系统,其中气象站是被观察者(Subject),而订阅天气预报的人是观察者(Observer)。
import java.util.ArrayList;
import java.util.List;
// 被观察者接口
interface WeatherStation {
void registerObserver(WeatherObserver observer);
void removeObserver(WeatherObserver observer);
void notifyObservers();
void updateWeatherData(float temperature, float humidity, float pressure);
}
// 观察者接口
interface WeatherObserver {
void update(float temp, float humidity, float pressure);
}
// 具体的被观察者 - 气象站
class ConcreteWeatherStation implements WeatherStation {
private List<WeatherObserver> observers;
private float temperature;
private float humidity;
private float pressure;
public ConcreteWeatherStation() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(WeatherObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(WeatherObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (WeatherObserver observer : observers) {
observer.update(temperature, humidity, pressure);
}
}
@Override
public void updateWeatherData(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers(); // 更新数据后通知所有观察者
}
}
// 具体的观察者 - 订阅者(如手机APP、电视节目)
class SmartphoneApp implements WeatherObserver {
@Override
public void update(float temp, float humidity, float pressure) {
System.out.println("Smartphone App updated with new weather data: Temp=" + temp + ", Humidity=" + humidity + ", Pressure=" + pressure);
}
}
class TVProgram implements WeatherObserver {
@Override
public void update(float temp, float humidity, float pressure) {
System.out.println("TV Program updated with new weather data: Temp=" + temp + ", Humidity=" + humidity + ", Pressure=" + pressure);
}
}
// 客户端代码
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteWeatherStation weatherStation = new ConcreteWeatherStation();
WeatherObserver smartphoneApp = new SmartphoneApp();
WeatherObserver tvProgram = new TVProgram();
weatherStation.registerObserver(smartphoneApp);
weatherStation.registerObserver(tvProgram);
weatherStation.updateWeatherData(25.0f, 70.0f, 1013.0f); // 更新天气数据
// 应该看到两个观察者都被通知到了新的天气数据
}
}
在这个例子中,ConcreteWeatherStation 是被观察者,它维护着一个观察者列表,并在天气数据更新时通知所有观察者。SmartphoneApp 和 TVProgram 是具体的观察者,它们实现了 update 方法以响应天气数据的变更。当气象站更新了天气数据,所有注册过的观察者都会收到通知并更新自身显示的数据。
3.责任链模式
责任链模式(Chain of Responsibility Pattern)它允许将请求沿着一个对象链传递直到有对象处理它。下面是一个使用Java编写的责任链模式实例,模拟一个员工请假申请审批流程,每个审批级别对应一个处理器节点,请求(请假申请)会按照层级顺序依次传递。
// 抽象处理器
abstract class LeaveApprover {
protected LeaveApprover nextApprover;
public void setNextApprover(LeaveApprover approver) {
this.nextApprover = approver;
}
public abstract void processRequest(Request request);
}
// 请求类
class Request {
private int days;
private String employeeName;
public Request(int days, String employeeName) {
this.days = days;
this.employeeName = employeeName;
}
public int getDays() {
return days;
}
public String getEmployeeName() {
return employeeName;
}
}
// 具体处理器类 - 主管
class Manager extends LeaveApprover {
@Override
public void processRequest(Request request) {
if (request.getDays() <= 3) {
System.out.println("Manager approved the leave request of " + request.getEmployeeName());
} else if (nextApprover != null) {
nextApprover.processRequest(request);
} else {
System.out.println("No one can approve the leave request of " + request.getEmployeeName());
}
}
}
// 具体处理器类 - 部门经理
class Director extends LeaveApprover {
@Override
public void processRequest(Request request) {
if (request.getDays() <= 7) {
System.out.println("Director approved the leave request of " + request.getEmployeeName());
} else if (nextApprover != null) {
nextApprover.processRequest(request);
} else {
System.out.println("No one can approve the leave request of " + request.getEmployeeName());
}
}
}
// 具体处理器类 - 总裁
class President extends LeaveApprover {
@Override
public void processRequest(Request request) {
System.out.println("President approved the leave request of " + request.getEmployeeName());
}
}
// 客户端代码
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
LeaveApprover president = new President();
LeaveApprover director = new Director();
LeaveApprover manager = new Manager();
manager.setNextApprover(director);
director.setNextApprover(president);
Request request1 = new Request(2, "Employee A");
Request request2 = new Request(5, "Employee B");
Request request3 = new Request(10, "Employee C");
manager.processRequest(request1);
manager.processRequest(request2);
manager.processRequest(request3);
}
}
在这个例子中,LeaveApprover 是抽象处理器类,每个具体处理器类(如 Manager、Director 和 President)都实现了 processRequest 方法来处理请求。请求(Request 类)会从最低级别的处理器(主管)开始传递,若当前处理器无法处理,则将请求传递给下一个处理器,直至找到能够处理请求的处理器,或到达链尾。
4.模板方法模式
模板方法模式(Template Method Pattern)它在一个抽象类中定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现。下面是一个使用Java编写的模板方法模式实例,我们将模拟一个制作饮料的过程,其中包含冲泡和加冰等通用步骤,但具体的饮品冲泡细节由子类来实现。
// 抽象模板类 - 制作饮料的基类
abstract class Beverage {
// 模板方法
public final void makeDrink() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
serve();
}
// 基本方法,由子类实现
protected abstract void brew();
// 基本方法,子类可以选择覆盖
protected boolean customerWantsCondiments() {
return true; // 默认顾客想要调料
}
// 已实现的钩子方法
protected void addCondiments() {
System.out.println("Adding condiments");
}
// 已实现的基本方法
private void boilWater() {
System.out.println("Boiling water");
}
// 已实现的基本方法
private void pourInCup() {
System.out.println("Pouring into cup");
}
// 已实现的基本方法
private void serve() {
System.out.println("Serving the drink");
}
}
// 具体模板类 - 制作咖啡
class Coffee extends Beverage {
@Override
protected void brew() {
System.out.println("Brewing coffee");
}
}
// 具体模板类 - 制作茶
class Tea extends Beverage {
@Override
protected void brew() {
System.out.println("Steeping tea");
}
// 覆盖钩子方法,可根据需求定制
@Override
protected boolean customerWantsCondiments() {
return false; // 假设喝茶的顾客一般不要调料
}
}
// 客户端代码
public class TemplateMethodPatternDemo {
public static void main(String[] args) {
Beverage coffee = new Coffee();
coffee.makeDrink();
System.out.println("\n---\n");
Beverage tea = new Tea();
tea.makeDrink();
}
}
在这个例子中,Beverage 是抽象模板类,它定义了制作饮料的完整流程(模板方法 makeDrink)。Coffee 和 Tea 是具体模板类,它们继承自 Beverage 并实现了抽象方法 brew,以适应各自独特的制作过程。此外,Tea 类还覆盖了钩子方法 customerWantsCondiments 来根据特定场景修改行为。客户端代码只需调用 makeDrink 方法即可完成整个饮料制作流程,具体细节由子类决定。
5.状态模式
状态模式(State Pattern)它允许对象在内部状态改变时改变其行为。下面是一个使用Java编写的简单状态模式实例,模拟ATM取款机根据账户的不同状态(正常、冻结、透支)执行不同的取款操作。
首先,我们定义一个抽象状态接口和几个具体状态类:
// 抽象状态接口
public interface AccountState {
void deposit(double amount);
void withdraw(double amount);
void setState(Account account);
}
// 正常状态
public class NormalState implements AccountState {
private Account account;
@Override
public void deposit(double amount) {
account.balance += amount;
System.out.println("Deposit successful. New balance: " + account.balance);
}
@Override
public void withdraw(double amount) {
if (account.balance >= amount) {
account.balance -= amount;
System.out.println("Withdrawal successful. New balance: " + account.balance);
} else {
account.changeState(new OverdraftState());
account.withdraw(amount);
}
}
@Override
public void setState(Account account) {
this.account = account;
}
}
// 冻结状态(这里为了简化仅演示正常和透支状态,冻结状态与透支状态处理类似)
// public class FrozenState implements AccountState { ... }
// 透支状态
public class OverdraftState implements AccountState {
private Account account;
@Override
public void deposit(double amount) {
account.balance += amount;
if (account.balance > 0) {
account.changeState(new NormalState());
}
System.out.println("Deposit successful, but account is still in overdraft. New balance: " + account.balance);
}
@Override
public void withdraw(double amount) {
System.out.println("Cannot withdraw from an overdrafted account.");
}
@Override
public void setState(Account account) {
this.account = account;
}
}
// ATM账户类,持有状态并负责状态切换
public class Account {
private double balance;
private AccountState currentState;
public Account() {
this.currentState = new NormalState();
currentState.setState(this);
}
public void deposit(double amount) {
currentState.deposit(amount);
}
public void withdraw(double amount) {
currentState.withdraw(amount);
}
// 更改状态的方法
public void changeState(AccountState newState) {
currentState = newState;
currentState.setState(this);
}
}
// 客户端代码
public class StatePatternDemo {
public static void main(String[] args) {
Account account = new Account();
account.deposit(1000); // 存入1000元
account.withdraw(500); // 取出500元
account.withdraw(700); // 试图取出700元,导致透支
account.deposit(300); // 再存入300元
}
}
在这个例子中,Account 类是环境类,它持有一个 AccountState 对象(即当前状态)。NormalState 和 OverdraftState 是具体的状态类,它们实现了 AccountState 接口并定义了在不同状态下执行存款和取款操作的具体行为。当账户余额不足以支持取款时,状态会从 NormalState 自动切换至 OverdraftState。在 OverdraftState 下,账户不能进行正常的取款操作。
6.访问者模式
访问者模式(Visitor Pattern)它使你能在不修改现有类层次结构的情况下向类添加新的操作。这种模式将数据结构与作用于结构上的操作解耦合。
下面是一个简单的访问者模式示例,我们将创建一个动物园系统,其中包含动物和游客两类实体,游客可以对各种动物进行参观操作。
首先,我们定义动物接口及其实现类:
// 抽象动物接口
public interface Animal {
void accept(AnimalVisitor visitor);
}
// 具体动物类
public class Lion implements Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visitLion(this);
}
}
public class Tiger implements Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visitTiger(this);
}
}
public class Panda implements Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visitPanda(this);
}
}
// 假设还有其他动物类如 Elephant, Giraffe 等...
接下来,我们定义访问者接口以及它的实现类,用于执行具体的访问操作:
// 抽象访问者接口
public interface AnimalVisitor {
void visitLion(Lion lion);
void visitTiger(Tiger tiger);
void visitPanda(Panda panda);
// 如果有更多的动物种类,也需要在此接口中添加相应方法
}
// 具体访问者 - 游客
public class ZooVisitor implements AnimalVisitor {
@Override
public void visitLion(Lion lion) {
System.out.println("Visited the lion and took a photo.");
}
@Override
public void visitTiger(Tiger tiger) {
System.out.println("Visited the tiger and watched its agility show.");
}
@Override
public void visitPanda(Panda panda) {
System.out.println("Visited the panda and fed it some bamboo.");
}
}
// 客户端代码
public class VisitorPatternDemo {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Lion());
animals.add(new Tiger());
animals.add(new Panda());
ZooVisitor zooVisitor = new ZooVisitor();
for (Animal animal : animals) {
animal.accept(zooVisitor);
}
}
}
在上述代码中,每个动物类都实现了 accept 方法,该方法接收一个 AnimalVisitor 参数,并调用访问者的相应方法来处理对该动物的访问操作。ZooVisitor 是具体访问者,它实现了 AnimalVisitor 接口中的所有 visit 方法,提供了针对每种动物的具体参观行为。在客户端代码中,遍历动物列表并对每个动物调用 accept 方法,这样就能根据不同动物类型执行相应的访问动作。
7.中介模式
中介模式(Mediator Pattern)它定义了一个中介对象来封装一系列的对象交互。中介使得各对象不需要显式地相互引用,从而使其松耦合,而且可以独立地改变它们之间的交互方式。下面是一个使用Java编写的中介模式实例,模拟一个聊天室系统,其中聊天室作为中介,管理多个用户的聊天消息发送和接收。
首先,我们定义抽象中介和用户类:
// 抽象中介
public interface ChatRoomMediator {
void showMessage(User user, String message);
void addUser(User user);
void removeUser(User user);
}
// 抽象用户类
public abstract class User {
protected ChatRoomMediator mediator;
private String name;
public User(ChatRoomMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
mediator.addUser(this);
}
public void sendMessage(String message) {
mediator.showMessage(this, message);
}
public String getName() {
return name;
}
}
// 具体用户类
public class ChatRoomUser extends User {
public ChatRoomUser(ChatRoomMediator mediator, String name) {
super(mediator, name);
}
}
然后,我们实现聊天室中介:
// 具体中介类 - 聊天室
public class ChatRoom implements ChatRoomMediator {
private List<User> users;
public ChatRoom() {
this.users = new ArrayList<>();
}
@Override
public void showMessage(User user, String message) {
System.out.println(user.getName() + ": " + message);
for (User u : users) {
if (u != user) {
u.receiveMessage(message);
}
}
}
@Override
public void addUser(User user) {
users.add(user);
System.out.println(user.getName() + " has joined the chat room.");
}
@Override
public void removeUser(User user) {
users.remove(user);
System.out.println(user.getName() + " has left the chat room.");
}
// 用户接收消息的方法,实际应用中可能需要用户类自己处理
public void receiveMessage(User sender, String message) {
System.out.println(sender.getName() + " said: " + message);
}
}
// 客户端代码
public class MediatorPatternDemo {
public static void main(String[] args) {
ChatRoomMediator chatRoom = new ChatRoom();
User user1 = new ChatRoomUser(chatRoom, "Alice");
User user2 = new ChatRoomUser(chatRoom, "Bob");
user1.sendMessage("Hello everyone!");
user2.sendMessage("Hi Alice!");
}
}
在这个例子中,ChatRoom 是中介类,它管理一组用户,并且当一个用户发送消息时,会通过 showMessage 方法将消息广播给聊天室内的其他用户。User 是抽象用户类,它通过中介类来发送消息,而不是直接与其他用户通信。ChatRoomUser 是具体用户类,它在构造函数中把自己加入到聊天室。在客户端代码中,我们创建了两个用户并让他们分别发送消息,消息会被聊天室中介正确地广播出去。
8.命令模式
命令模式(Command Pattern)它将请求封装为一个对象,从而使你可用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。下面是一个使用Java编写的命令模式实例,模拟一个遥控器系统,其中遥控器可以接受多个命令(例如打开电视、关闭电视等),并将这些命令传递给相应的设备执行。
首先,我们定义命令接口及其实现类:
// 命令接口
public interface Command {
void execute();
void undo();
}
// 打开电视命令
public class TurnOnTvCommand implements Command {
private Television television;
public TurnOnTvCommand(Television television) {
this.television = television;
}
@Override
public void execute() {
television.turnOn();
}
@Override
public void undo() {
television.turnOff();
}
}
// 关闭电视命令
public class TurnOffTvCommand implements Command {
private Television television;
public TurnOffTvCommand(Television television) {
this.television = television;
}
@Override
public void execute() {
television.turnOff();
}
@Override
public void undo() {
television.turnOn();
}
}
// 电视设备类
public class Television {
public void turnOn() {
System.out.println("TV is turned on");
}
public void turnOff() {
System.out.println("TV is turned off");
}
}
接下来,我们定义遥控器类,它将持有命令并执行它们:
// 遥控器类
public class RemoteControl {
private Command currentCommand;
public void setCommand(Command command) {
this.currentCommand = command;
}
public void pressButton() {
if (currentCommand != null) {
currentCommand.execute();
}
}
public void undoLastAction() {
if (currentCommand != null) {
currentCommand.undo();
}
}
}
// 客户端代码
public class CommandPatternDemo {
public static void main(String[] args) {
Television tv = new Television();
RemoteControl remote = new RemoteControl();
Command turnOnCommand = new TurnOnTvCommand(tv);
Command turnOffCommand = new TurnOffTvCommand(tv);
remote.setCommand(turnOnCommand);
remote.pressButton(); // 输出:TV is turned on
remote.setCommand(turnOffCommand);
remote.pressButton(); // 输出:TV is turned off
remote.undoLastAction(); // 输出:TV is turned on
}
}
在这个例子中,Command 是抽象命令类,TurnOnTvCommand 和 TurnOffTvCommand 是具体命令类,它们实现了 execute 和 undo 方法来执行打开和关闭电视的动作。RemoteControl 是调用者,它持有并执行命令对象。在客户端代码中,遥控器先设置打开电视命令并执行,然后设置关闭电视命令并执行,最后通过 undoLastAction 方法撤销上一次操作,再次打开电视。
9.备忘录模式
备忘录模式(Memento Pattern)它在不破坏封装性的前提下,捕获一个对象的内部状态并在该对象之外保存这个状态,以便之后恢复它。下面是一个使用Java编写的备忘录模式实例,模拟一个文本编辑器,其中备忘录用于保存和恢复文档内容。
首先,我们定义备忘录相关类:
// 备忘录接口
public interface Memento {
String getContent();
}
// 具体备忘录类 - 文档备忘录
public class DocumentMemento implements Memento {
private String content;
public DocumentMemento(String content) {
this.content = content;
}
@Override
public String getContent() {
return content;
}
}
// 原始类 - 文档
public class Document {
private String content;
public Document(String initialContent) {
this.content = initialContent;
}
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
// 创建备忘录
public Memento createMemento() {
return new DocumentMemento(content);
}
// 从备忘录恢复状态
public void restoreFromMemento(Memento memento) {
this.content = memento.getContent();
}
}
// 管理者类 - 备忘录管理者(在这里可省略,因为我们的例子相对简单)
// public class Caretaker { ... }
然后,在客户端代码中使用备忘录模式:
// 客户端代码
public class MementoPatternDemo {
public static void main(String[] args) {
Document document = new Document("Initial content...");
// 修改文档内容
document.setContent("New content...");
System.out.println("Current content: " + document.getContent());
// 创建并保存备忘录
Memento savedState = document.createMemento();
// 继续修改文档内容
document.setContent("Another new content...");
System.out.println("Current content after modification: " + document.getContent());
// 从备忘录恢复先前的内容
document.restoreFromMemento(savedState);
System.out.println("Restored content: " + document.getContent());
}
}
在这个例子中,Document 类是原始类,它拥有一个需要保存和恢复的状态(内容)。DocumentMemento 类是备忘录类,它捕获并存储了 Document 的内部状态。createMemento 方法用于创建备忘录,而 restoreFromMemento 方法则用来恢复之前保存的状态。在客户端代码中,我们首先创建一个文档对象并修改其内容,然后保存当前状态到备忘录,接着再次修改内容,最后恢复最初保存的状态。
10.迭代器模式
迭代器模式(Iterator Pattern)它提供了一种方法顺序访问聚合对象的元素,而又不暴露其底层表示。下面是一个使用Java编写的迭代器模式实例,模拟一个简单的集合(数组)和对应的迭代器。
首先,我们定义聚合类及其迭代器:
// 聚合接口
public interface Collection {
Iterator iterator();
}
// 迭代器接口
public interface Iterator {
boolean hasNext();
Object next();
}
// 具体聚合类 - 数组集合
public class ArrayCollection implements Collection {
private Object[] items;
public ArrayCollection(Object[] items) {
this.items = items;
}
@Override
public Iterator iterator() {
return new ArrayIterator(items);
}
}
// 具体迭代器类 - 数组迭代器
public class ArrayIterator implements Iterator {
private Object[] array;
private int position;
public ArrayIterator(Object[] array) {
this.array = array;
this.position = 0;
}
@Override
public boolean hasNext() {
return position < array.length;
}
@Override
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException("No more elements.");
}
return array[position++];
}
}
然后,在客户端代码中使用迭代器:
// 客户端代码
public class IteratorPatternDemo {
public static void main(String[] args) {
Object[] items = {"Apple", "Banana", "Cherry"};
Collection collection = new ArrayCollection(items);
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
在这个例子中,ArrayCollection 是具体聚合类,它实现了 Collection 接口,并在其 iterator 方法中返回一个 ArrayIterator 对象。ArrayIterator 是具体迭代器类,它实现了 Iterator 接口,提供了 hasNext 和 next 方法来遍历数组集合。在客户端代码中,我们创建一个 ArrayCollection 实例,并通过调用其 iterator 方法获取迭代器,然后使用迭代器遍历集合中的所有元素。
11.解释器模式
解释器模式(Interpreter Pattern)是一种行为设计模式,它提供了评估语言语法或表达式的方式。下面是一个使用Java编写的解释器模式实例,模拟一个简单的算术表达式解析器,它可以处理加法和乘法运算。
首先,我们定义表达式接口和它的各种实现:
// 表达式接口
public interface Expression {
int interpret();
}
//终结符表达式 - 常量
public class NumberExpression implements Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret() {
return number;
}
}
//终结符表达式 - 变量
public class VariableExpression implements Expression {
private String variableName;
private Map<String, Integer> context;
public VariableExpression(String variableName, Map<String, Integer> context) {
this.variableName = variableName;
this.context = context;
}
@Override
public int interpret() {
return context.get(variableName);
}
}
//非终结符表达式 - 加法
public class AddExpression implements Expression {
private Expression left, right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
//非终结符表达式 - 乘法
public class MultiplyExpression implements Expression {
private Expression left, right;
public MultiplyExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() * right.interpret();
}
}
然后,我们可以创建一个解释器类来构建和解析表达式:
public class InterpreterPatternDemo {
public static void main(String[] args) {
// 创建表达式上下文
Map<String, Integer> variables = new HashMap<>();
variables.put("x", 10);
variables.put("y", 5);
// 构建表达式
Expression expression = new MultiplyExpression(
new AddExpression(
new NumberExpression(2),
new VariableExpression("x", variables)
),
new VariableExpression("y", variables)
);
// 解析表达式
int result = expression.interpret();
System.out.println("Result: " + result); // 输出结果:60 (2 + 10 * 5)
}
}
在这个例子中,我们定义了 Expression 接口,以及一些实现了该接口的具体类,包括终结符表达式(如 NumberExpression 和 VariableExpression)和非终结符表达式(如 AddExpression 和 MultiplyExpression)。客户端代码创建了一个复杂的表达式树,并通过调用 interpret 方法来解析和计算表达式的值。