设计模式
六大原则
1、开放封闭原则
Open Close Principle
OCP:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
OCP原则要求设计者在需要修改现有代码时,不应该直接修改已有的代码,而是应该通过扩展现有代码来实现新的功能或修改。
代码实例:
实现计算机:Operation
是策略接口,定义一个 calculate
方法用于执行数学运算。Addition
和 Subtraction
是具体策略类,分别实现了加法和减法运算。Calculator
是上下文类,持有一个 Operation
对象,可以通过设置不同的策略对象来改变其行为。当需要添加新的数学运算时,只需要创建新的具体策略类,并在上下文中设置新的策略对象,而不需要修改现有的代码。
// 策略接口
interface Operation {
int calculate(int num1, int num2);
}
// 具体策略类:加法操作
class Addition implements Operation {
@Override
public int calculate(int num1, int num2) {
return num1 + num2;
}
}
// 具体策略类:减法操作
class Subtraction implements Operation {
@Override
public int calculate(int num1, int num2) {
return num1 - num2;
}
}
// 上下文类
class Calculator {
private Operation operation;
public void setOperation(Operation operation) {
this.operation = operation;
}
public int performOperation(int num1, int num2) {
if (operation == null) {
throw new IllegalStateException("Operation not set");
}
return operation.calculate(num1, num2);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator();
// 使用加法操作
calculator.setOperation(new Addition());
int result1 = calculator.performOperation(5, 3);
System.out.println("5 + 3 = " + result1);
// 使用减法操作
calculator.setOperation(new Subtraction());
int result2 = calculator.performOperation(8, 4);
System.out.println("8 - 4 = " + result2);
}
}
2、里氏代换原则
Liskov Substitution Principle
LSP:在使用基类的任何地方都可以替换为使用其继承的子类,即完美的替换基类。
LSP原则要求子类型(派生类或子类)必须能够替换其基类型(基类或父类)的任何地方,而不破坏程序的正确性。
代码实例:
abstract class Animal {
abstract void makeSound();
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
// 里氏代换原则:可以在任何需要 Animal 类型的地方使用 Dog 或 Cat 类的对象
dog.makeSound();
cat.makeSound();
}
}
3、依赖倒转原则
Dependence Inversion Principle
DIP原则:
- 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
- 抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
DIP原则指导着建立松耦合的软件架构,使得高层模块不依赖于低层模块的具体实现细节,而是依赖于抽象。
该原则的核心思想是通过抽象来实现模块之间的松耦合,从而提高代码的灵活性和可维护性。当系统需要进行变更时,可以通过替换具体实现而不影响高层模块的代码。
代码实例:
考虑一个简单的电灯类和电灯开关类的依赖关系,根据依赖倒转原则,我们应该抽象出一个开关接口,电灯类依赖于这个开关接口,而不是具体的开关实现。
// 开关接口
interface Switch {
void turnOn();
void turnOff();
}
// 电灯类
class Light {
private Switch switcher;
public Light(Switch switcher) {
this.switcher = switcher;
}
public void toggle() {
if (switcher != null) {
switcher.turnOn();
switcher.turnOff();
}
}
}
// 电灯开关类
class LightSwitch implements Switch {
@Override
public void turnOn() {
System.out.println("Light is on");
}
@Override
public void turnOff() {
System.out.println("Light is off");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Switch lightSwitch = new LightSwitch();
Light light = new Light(lightSwitch);
light.toggle();
}
}
4、接口隔离原则
Interface Segregation Principle
ISP原则强调接口的独立性和单一性,即客户端不应该强制依赖于其不需要的接口。
ISP原则要求将大接口拆分为多个小接口,客户端只依赖于它们需要的接口,而不需要依赖于不需要的接口。如此可避免不必要的依赖,降低耦合度,提高代码的灵活性和可维护性。
代码实例:
考虑一个简单的打印机应用程序,它有一个打印机类 Printer
,可以打印文本和图像。按照接口隔离原则,我们应该将打印文本和打印图像的功能分别定义在两个接口中,而不是定义在同一个接口中。
// 打印文本接口
interface TextPrinter {
void printText(String text);
}
// 打印图像接口
interface ImagePrinter {
void printImage(String imageUrl);
}
// 打印机类实现打印文本接口和打印图像接口
class Printer implements TextPrinter, ImagePrinter {
@Override
public void printText(String text) {
System.out.println("Printing text: " + text);
}
@Override
public void printImage(String imageUrl) {
System.out.println("Printing image: " + imageUrl);
}
}
public class Client {
public static void main(String[] args) {
Printer printer = new Printer();
// 打印文本
printer.printText("Hello, World!");
// 打印图像
printer.printImage("https://example.com/image.png");
}
}
5、迪米特法则
Demeter Principle
迪米特法则,又称为最少知道原则(Law of Demeter,LoD)
该原则的核心思想是一个对象应当对其他对象有尽可能少的了解,即对象之间应该保持松散的耦合关系。
迪米特法则可以概括为以下几点:
- 每个单元(类、模块、函数等)只应该与其密切相关的对象进行交互,而不是与一大堆不相关的对象进行交互。
- 类之间的通信应该通过最直接的方式进行,避免通过中间多层的对象进行传递消息。
- 不要让一个单元了解整个系统的结构,而应该只关注与之直接交互的对象。
迪米特法则的目的是降低系统的耦合度,提高系统的灵活性和可维护性。通过减少对象之间的直接联系,可以使得系统的各个模块更容易被独立开发、测试、维护和重用。
代码实例:
考虑一个购物车系统,其中包含商品、购物车和订单三个类。按照迪米特法则,购物车类应该尽可能少地了解商品类和订单类的内部结构,而是通过商品类和订单类提供的接口进行交互。
// 商品类
class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
// 其他方法...
}
// 订单类
class Order {
private List<Product> products;
public Order() {
this.products = new ArrayList<>();
}
public void addProduct(Product product) {
products.add(product);
}
// 其他方法...
}
// 购物车类
class ShoppingCart {
private List<Product> products;
public ShoppingCart() {
this.products = new ArrayList<>();
}
public void addProduct(Product product) {
products.add(product);
}
public void checkout(Order order) {
for (Product product : products) {
order.addProduct(product);
}
products.clear();
}
}
public class Client {
public static void main(String[] args) {
Product product1 = new Product("iPhone", 999.99);
Product product2 = new Product("iPad", 699.99);
ShoppingCart cart = new ShoppingCart();
cart.addProduct(product1);
cart.addProduct(product2);
Order order = new Order();
cart.checkout(order);
}
}
6、单一职责原则
Principle of single responsibility
SRP原则指出一个类或模块应该有且只有一个引起它变化的原因,或者说一个类或模块应该只负责一种职责。
SRP原则要求一个类或模块只做一件事情,并且做好这件事情。如果一个类或模块负责了多个不同的职责,那么它的设计就不够清晰,也不够灵活。
遵循SRP原则有助于提高代码的内聚性,即相关功能应该放在一起,不相关功能应该分开。
代码实例:
考虑一个简单的日志记录器类 Logger
,它负责将日志信息记录到文件和控制台。按照单一职责原则,我们应该将文件日志记录和控制台日志记录分别放在两个类中。
// 日志记录器类
class Logger {
public void logToFile(String message) {
// 将日志信息记录到文件
System.out.println("Log to file: " + message);
}
public void logToConsole(String message) {
// 将日志信息记录到控制台
System.out.println("Log to console: " + message);
}
}
// 文件日志记录器类
class FileLogger {
public void log(String message) {
// 将日志信息记录到文件
System.out.println("Log to file: " + message);
}
}
// 控制台日志记录器类
class ConsoleLogger {
public void log(String message) {
// 将日志信息记录到控制台
System.out.println("Log to console: " + message);
}
}
public class Client {
public static void main(String[] args) {
FileLogger fileLogger = new FileLogger();
fileLogger.log("Error: File not found");
ConsoleLogger consoleLogger = new ConsoleLogger();
consoleLogger.log("Warning: Memory is running low");
}
}
GRASP模式
General Responsibility Assignment Software Patterns ,即"通用责任分配软件模式"
面向对象设计中常用的设计原则
GRASP模式旨在帮助设计者更好地分配类(对象)的职责,使得系统更加易于理解、扩展和维护
专家模式
任务应该分配给具有最多所需信息的类。即一个对象应该包含或掌握与其任务相关的信息。
创造者原则
当一个类A需要创建另一个类B的实例时,类A应该是类B的聚合体,组合体,或者类A持有类B的引用。避免在类A中直接实例化类B。
低耦合原则
耦合(Coupling):耦合描述模块之间的依赖关系。
高耦合意味着模块之间的依赖性很强,一个模块的修改可能会导致其他模块的修改。
低耦合意味着模块之间的依赖性很弱,一个模块的修改不太可能影响其他模块。
在软件设计中,耦合指的是模块间的依赖关系。低耦合意味着模块间的依赖关系越少越好,这样修改一个模块不会影响其他模块。
高内聚原则
内聚(Cohesion):内聚描述模块内部元素之间的关联程度,即一个模块内部各个元素(如方法、属性等)彼此之间的联系。
高内聚意味着模块内的元素彼此之间关联紧密,每个模块只负责一项清晰的任务。
低内聚意味着模块内的元素关联性较弱,模块可能会包含多种不相关的功能。
一个模块内的各个元素彼此关联紧密,只负责一个清晰的任务。高内聚的模块更容易理解、维护和重用。
控制器原则
确定哪个类或对象负责接收用户输入、协调系统操作,并作出相应的响应。控制器将用户请求转发给适当的对象来处理。
多态原则
通过继承和接口实现多态性,允许不同的对象对同一消息作出不同的响应。
纯虚构
为了降低耦合度,创建一个新的类或对象来处理系统中的特定任务,而不受现有领域对象的限制。
中介原则
引入一个中介者来管理对象之间的通信,减少对象之间的直接依赖关系。
受保护变量原则
通过封装和抽象,保护系统中易变的部分,使得系统对变化的影响最小化。
设计模式分类
创建型模式
- 工厂方法模式:将对象的创建和使用分离开来,客户端只需要知道工厂方法,而不需要知道具体的产品类。
- 抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。
- 建造者模式:将一个复杂对象的构建过程与其表示分离开来,使得同样的构建过程可以创建不同的表示。
- 单例模式:确保某个类只有一个实例,并提供一个全局访问点来访问该实例。
- 原型模式:通过复制现有对象来创建新对象,而不是通过实例化类来创建对象。
1、工厂方法模式
将对象的创建和使用分离开来,客户端只需要知道工厂方法,而不需要知道具体的产品类。
工厂方法模式包括以下几个角色:
- 抽象产品(Product):定义了产品的接口,是工厂方法所创建的对象的父类。
- 具体产品(Concrete Product):实现了抽象产品接口的具体类。
- 抽象工厂(Creator):定义了一个抽象的工厂方法,用于创建产品对象。通常是一个抽象类,其中可能包含一些通用的逻辑代码。
- 具体工厂(Concrete Creator):继承自抽象工厂,实现了工厂方法,用于创建具体的产品对象。
以下是一个简单的工厂方法模式的示例(以创建不同类型的日志记录器为例):
// 抽象产品
interface Logger {
void log(String message);
}
// 具体产品 - 文件日志记录器
class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Log message to file: " + message);
}
}
// 具体产品 - 数据库日志记录器
class DatabaseLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Log message to database: " + message);
}
}
// 抽象工厂
abstract class LoggerFactory {
abstract Logger createLogger();
}
// 具体工厂 - 文件日志记录器工厂
class FileLoggerFactory extends LoggerFactory {
@Override
Logger createLogger() {
return new FileLogger();
}
}
// 具体工厂 - 数据库日志记录器工厂
class DatabaseLoggerFactory extends LoggerFactory {
@Override
Logger createLogger() {
return new DatabaseLogger();
}
}
2、抽象工厂模式
提供一个接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。
抽象工厂模式包含以下几个角色:
- 抽象工厂(Abstract Factory):定义了创建产品对象的方法接口,它是工厂方法模式的升级版,用于创建一系列相关或依赖对象的家族。
- 具体工厂(Concrete Factory):实现了抽象工厂接口,用于创建具体的产品对象。
- 抽象产品(Abstract Product):定义了产品对象的接口,是具体产品对象的父类。
- 具体产品(Concrete Product):实现了抽象产品接口,是被具体工厂创建的对象。
抽象工厂模式将每个具体工厂与一组具体产品相关联,这样客户端就可以通过选择不同的具体工厂来创建不同类型的产品对象,从而实现了一种产品族的配置。
以下是一个简单的抽象工厂模式的示例(以创建不同类型的手机和配件为例):
// 抽象产品A - 手机
interface Phone {
void make();
}
// 具体产品A - 苹果手机
class ApplePhone implements Phone {
@Override
public void make() {
System.out.println("Make Apple Phone");
}
}
// 具体产品A - 华为手机
class HuaweiPhone implements Phone {
@Override
public void make() {
System.out.println("Make Huawei Phone");
}
}
// 抽象产品B - 手机配件
interface PhoneAccessory {
void produce();
}
// 具体产品B - 苹果手机配件
class AppleAccessory implements PhoneAccessory {
@Override
public void produce() {
System.out.println("Produce Apple Accessory");
}
}
// 具体产品B - 华为手机配件
class HuaweiAccessory implements PhoneAccessory {
@Override
public void produce() {
System.out.println("Produce Huawei Accessory");
}
}
// 抽象工厂
interface PhoneFactory {
Phone createPhone();
PhoneAccessory createPhoneAccessory();
}
// 具体工厂A - 苹果手机工厂
class AppleFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new ApplePhone();
}
@Override
public PhoneAccessory createPhoneAccessory() {
return new AppleAccessory();
}
}
// 具体工厂B - 华为手机工厂
class HuaweiFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new HuaweiPhone();
}
@Override
public PhoneAccessory createPhoneAccessory() {
return new HuaweiAccessory();
}
}
3、建造者模式
将一个复杂对象的构建过程与其表示分离开来,使得同样的构建过程可以创建不同的表示。
建造者模式包含以下几个角色:
- 产品(Product):表示被构建的复杂对象。在建造过程完成后,通常会返回一个包含所有属性的最终产品对象。
- 抽象建造者(Builder):定义了构建产品对象的抽象方法,包括设置各个属性的方法。
- 具体建造者(Concrete Builder):实现了抽象建造者接口,负责构建和装配产品的各个部件,并定义了具体的构建步骤。
- 指挥者(Director):负责调用具体建造者来构建产品对象。它不直接创建产品对象,而是通过调用具体建造者的方法来构建产品。
建造者模式通常适用于以下情况:
- 创建对象的构建过程比较复杂,需要很多步骤或者涉及到很多部件。
- 希望将对象的构建过程和表示分离开来,以便能够灵活地组合不同的部件和构建步骤。
以下是一个简单的建造者模式的示例(以创建一个电脑对象为例):
// 产品类 - 电脑
class Computer {
private String cpu;
private String memory;
private String hardDisk;
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setMemory(String memory) {
this.memory = memory;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
// 其他方法...
}
// 抽象建造者
interface ComputerBuilder {
void buildCpu();
void buildMemory();
void buildHardDisk();
Computer getResult();
}
// 具体建造者 - 高配版电脑建造者
class HighConfigComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer();
@Override
public void buildCpu() {
computer.setCpu("Intel i9");
}
@Override
public void buildMemory() {
computer.setMemory("32GB");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("1TB SSD");
}
@Override
public Computer getResult() {
return computer;
}
}
// 指挥者
class Director {
private ComputerBuilder builder;
public void setBuilder(ComputerBuilder builder) {
this.builder = builder;
}
public Computer construct() {
builder.buildCpu();
builder.buildMemory();
builder.buildHardDisk();
return builder.getResult();
}
}
4、单例模式
确保某个类只有一个实例,并提供一个全局访问点来访问该实例。
实现单例模式的一般步骤包括:
- 私有构造函数:确保外部不能通过构造函数来创建该类的实例。
- 静态成员变量:在类中定义一个静态成员变量来保存该类的唯一实例。
- 静态方法:提供一个静态方法来访问该唯一实例,并确保在需要时进行实例化
public class Singleton {
private static Singleton instance;
// 私有构造函数
private Singleton() {}
// 静态方法获取唯一实例
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
5、原型模式
通过复制现有对象来创建新对象,而不是通过实例化类来创建对象。
原型模式包含以下几个角色:
- 原型接口(Prototype):声明了一个克隆自身的方法,用于复制自身来创建新对象。
- 具体原型类(Concrete Prototype):实现了原型接口,负责实现克隆自身的方法。
- 客户端(Client):负责使用原型对象来创建新对象的客户端代码。
原型模式通常适用于以下情况:
- 当创建对象的成本比较高昂,或者对象的创建过程比较复杂,但新对象与现有对象相似度很高时。
- 当需要避免通过子类进行复杂的初始化配置时,而是希望通过复制一个已有的实例来创建新实例时。
以下是一个简单的原型模式的示例:
// 原型接口
interface Prototype {
Prototype clone();
}
// 具体原型类
class ConcretePrototype implements Prototype {
private String field;
public ConcretePrototype(String field) {
this.field = field;
}
public String getField() {
return field;
}
@Override
public Prototype clone() {
// 创建一个新对象,并将当前对象的属性复制给新对象
return new ConcretePrototype(this.field);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
ConcretePrototype prototype = new ConcretePrototype("data");
ConcretePrototype clone = (ConcretePrototype) prototype.clone();
System.out.println("Original Object: " + prototype.getField());
System.out.println("Cloned Object: " + clone.getField());
}
}
结构型模式
- 适配器模式:将一个类的接口转换成客户端所期待的另一个接口,使得原本由于接口不兼容而不能一起工作的类能够协同工作。
- 装饰器模式:允许向一个对象动态地添加新的功能,而无需修改其源代码。
- 代理模式:通过代理对象控制对原始对象的访问。
- 外观模式:提供一个统一的接口,用于访问子系统中的一组接口。
- 桥接模式:将抽象部分与实现部分分离,使它们可以独立变化,从而可以动态地组合不同的抽象和实现。
- 组合模式:允许将对象组合成树形结构以表示“部分-整体”的层次结构。
- 享元模式:通过共享对象来减少内存使用和提高性能。在享元模式中,对象被分为内部状态(Intrinsic State)和外部状态(Extrinsic State),其中内部状态可以被多个对象共享,而外部状态需要在对象之间单独维护。
1、适配器模式
将一个类的接口转换成客户端所期待的另一个接口,使得原本由于接口不兼容而不能一起工作的类能够协同工作。
适配器模式包含以下几个角色:
- 目标接口(Target):定义客户端使用的接口,客户端期待的接口。
- 适配器(Adapter):实现了目标接口,并包装了一个需要适配的类的对象,在目标接口中调用被包装对象的方法。
- 被适配者(Adaptee):需要被适配的类,其接口与目标接口不兼容。
- 客户端(Client):通过目标接口与适配器交互,调用适配器的方法来实现需要的功能。
适配器模式通常适用于以下情况:
- 当需要使用一个已经存在的类,而它的接口不符合需求时。
- 当需要创建一个可复用的类,与一些不相关或不可预见的类协作时。
以下是一个简单的适配器模式的示例(以一个日志记录器适配器为例):
// 目标接口
interface Logger {
void log(String message);
}
// 具体目标 - 文件日志记录器
class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Log message to file: " + message);
}
}
// 适配器类
class LoggerAdapter implements Logger {
private DatabaseLogger databaseLogger;
public LoggerAdapter(DatabaseLogger databaseLogger) {
this.databaseLogger = databaseLogger;
}
@Override
public void log(String message) {
databaseLogger.writeLog(message);
}
}
// 被适配者 - 数据库日志记录器
class DatabaseLogger {
public void writeLog(String message) {
System.out.println("Log message to database: " + message);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Logger fileLogger = new FileLogger();
fileLogger.log("This is a log message to file.");
// 使用适配器将数据库日志记录器适配成目标接口
DatabaseLogger databaseLogger = new DatabaseLogger();
Logger dbLoggerAdapter = new LoggerAdapter(databaseLogger);
dbLoggerAdapter.log("This is a log message to database.");
}
}
2、装饰器模式
允许向一个对象动态地添加新的功能,而无需修改其源代码。
装饰器模式包含以下几个角色:
- 抽象构件(Component):定义了一个对象接口,可以给这些对象动态地添加新的职责。
- 具体构件(Concrete Component):实现了抽象构件接口,是被装饰的对象,可以给这个对象添加新的职责。
- 装饰器(Decorator):持有一个抽象构件的引用,并实现了抽象构件的接口,可以动态地给具体构件对象添加新的职责。
- 具体装饰器(Concrete Decorator):具体的装饰器类,实现了装饰器接口,并且通过构造函数接收一个抽象构件对象,在原有对象的基础上添加新的功能。
装饰器模式通常适用于以下情况:
- 需要向一个对象添加额外的功能,而不想影响到其他对象。
- 需要动态地给对象添加功能,而且添加的功能可以动态撤销。
以下是一个简单的装饰器模式的示例(以咖啡和配料为例):
// 抽象构件 - 咖啡
interface Coffee {
String getDescription();
double cost();
}
// 具体构件 - 普通咖啡
class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple Coffee";
}
@Override
public double cost() {
return 1.0;
}
}
// 装饰器 - 抽象装饰器
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
@Override
public double cost() {
return decoratedCoffee.cost();
}
}
// 具体装饰器 - 奶油
class CreamDecorator extends CoffeeDecorator {
public CreamDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", Cream";
}
@Override
public double cost() {
return super.cost() + 0.5;
}
}
// 具体装饰器 - 糖
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", Sugar";
}
@Override
public double cost() {
return super.cost() + 0.2;
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Coffee simpleCoffee = new SimpleCoffee();
System.out.println("Description: " + simpleCoffee.getDescription() + ", Cost: $" + simpleCoffee.cost());
// 添加奶油装饰器
Coffee creamCoffee = new CreamDecorator(simpleCoffee);
System.out.println("Description: " + creamCoffee.getDescription() + ", Cost: $" + creamCoffee.cost());
// 添加糖装饰器
Coffee sugarCoffee = new SugarDecorator(simpleCoffee);
System.out.println("Description: " + sugarCoffee.getDescription() + ", Cost: $" + sugarCoffee.cost());
// 添加奶油和糖装饰器
Coffee creamAndSugarCoffee = new CreamDecorator(new SugarDecorator(simpleCoffee));
System.out.println("Description: " + creamAndSugarCoffee.getDescription() + ", Cost: $" + creamAndSugarCoffee.cost());
}
}
3、代理模式
通过代理对象控制对原始对象的访问。
代理模式包含以下几个角色:
- 抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样就在任何使用真实主题的地方都可以使用代理主题。
- 真实主题(Real Subject):定义了代理所代表的真实对象,是最终要引用的对象。
- 代理(Proxy):保存了对真实主题的引用,并提供与真实主题相同的接口,客户端通过代理类来访问真实主题。
- 客户端(Client):通过代理来访问真实主题。
代理模式通常适用于以下情况:
- 需要在访问一个对象时添加额外的功能,但又不想修改该对象的代码。
- 需要对访问某个对象的方式进行控制,例如权限控制、延迟加载等。
以下是一个简单的代理模式的示例:
// 抽象主题
interface Image {
void display();
}
// 真实主题
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading image from disk: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
// 代理
class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
// 图片加载过程是延迟的,只有在真正调用 display() 方法时才会加载
image.display();
// 再次调用 display() 方法,不需要重新加载图片
image.display();
}
}
4、外观模式
提供一个统一的接口,用于访问子系统中的一组接口。
外观模式包含以下几个角色:
- 外观(Facade):提供了一个简单的接口,隐藏了子系统的复杂性,使得客户端可以更容易地与子系统进行交互。
- 子系统(Subsystem):包含一组相关的类,实现了子系统的功能,但这些类对客户端来说是透明的,即客户端不需要直接与它们进行交互。
外观模式通常适用于以下情况:
- 当一个复杂系统的子系统之间存在复杂的依赖关系,需要一个统一的接口来简化客户端与子系统之间的交互。
- 当客户端需要使用一个复杂的子系统,但只需要与该子系统的一部分进行交互时。
以下是一个简单的外观模式的示例:
// 子系统A
class SubsystemA {
public void operationA() {
System.out.println("SubsystemA operation");
}
}
// 子系统B
class SubsystemB {
public void operationB() {
System.out.println("SubsystemB operation");
}
}
// 子系统C
class SubsystemC {
public void operationC() {
System.out.println("SubsystemC operation");
}
}
// 外观
class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
private SubsystemC subsystemC;
public Facade() {
this.subsystemA = new SubsystemA();
this.subsystemB = new SubsystemB();
this.subsystemC = new SubsystemC();
}
// 提供一个简单的接口,隐藏了子系统的复杂性
public void operation() {
subsystemA.operationA();
subsystemB.operationB();
subsystemC.operationC();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 客户端通过外观来访问子系统,而不需要直接与子系统进行交互
Facade facade = new Facade();
facade.operation();
}
}
5、桥接模式
将抽象部分与实现部分分离,使它们可以独立变化,从而可以动态地组合不同的抽象和实现。
桥接模式包含以下几个角色:
- 抽象部分(Abstraction):定义了抽象部分的接口,并维护一个指向实现部分的引用。
- 扩充抽象类(Refined Abstraction):拓展了抽象部分的接口,通常为抽象类,在抽象部分的基础上添加了更多的方法或行为。
- 实现部分(Implementor):定义了实现部分的接口,该接口不一定与抽象部分完全相同,但它们之间必须是相互独立的。
- 具体实现类(Concrete Implementor):实现了实现部分的接口,提供具体的实现。
桥接模式通常适用于以下情况:
- 当需要避免在抽象部分和实现部分之间形成静态的绑定关系时。
- 当一个类存在两个(或多个)变化的维度,且需要在这些维度上独立扩展时。
以下是一个简单的桥接模式的示例:
// 实现部分接口
interface Implementor {
void operationImpl();
}
// 具体实现类A
class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("Concrete Implementor A operation");
}
}
// 具体实现类B
class ConcreteImplementorB implements Implementor {
@Override
public void operationImpl() {
System.out.println("Concrete Implementor B operation");
}
}
// 抽象部分
abstract class Abstraction {
protected Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
// 扩充抽象类
class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
implementor.operationImpl();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Implementor implementorA = new ConcreteImplementorA();
Implementor implementorB = new ConcreteImplementorB();
Abstraction abstractionA = new RefinedAbstraction(implementorA);
abstractionA.operation();
Abstraction abstractionB = new RefinedAbstraction(implementorB);
abstractionB.operation();
}
}
6、组合模式
允许将对象组合成树形结构以表示“部分-整体”的层次结构。
组合模式包含以下几个角色:
- 组件(Component):声明了组合对象和叶子对象的公共接口,可以包含所有子类共有的行为和属性。
- 叶子(Leaf):表示树中的叶子节点对象,叶子节点没有子节点。
- 组合(Composite):表示树中的组合对象,组合对象可以包含叶子对象和其他组合对象作为其子节点。
- 客户端(Client):通过组合对象和叶子对象的共同接口来进行操作。
组合模式通常适用于以下情况:
- 当想要表示对象的“部分-整体”层次结构,并且希望用户统一地对待所有对象时。
- 当希望用户忽略组合对象与叶子对象之间的差异,并一致地使用它们时。
以下是一个简单的组合模式的示例:
import java.util.ArrayList;
import java.util.List;
// 抽象组件
interface Component {
void operation();
}
// 叶子组件
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println("Leaf " + name + " operation");
}
}
// 组合组件
class Composite implements Component {
private List<Component> children = new ArrayList<>();
public void add(Component component) {
children.add(component);
}
public void remove(Component component) {
children.remove(component);
}
@Override
public void operation() {
System.out.println("Composite operation");
for (Component component : children) {
component.operation();
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 创建组合对象
Composite composite = new Composite();
// 添加叶子对象
composite.add(new Leaf("Leaf A"));
composite.add(new Leaf("Leaf B"));
// 创建子组合对象
Composite subComposite = new Composite();
subComposite.add(new Leaf("Leaf C"));
subComposite.add(new Leaf("Leaf D"));
// 将子组合对象添加到父组合对象
composite.add(subComposite);
// 执行组合对象的操作
composite.operation();
}
}
7、享元模式
通过共享对象来减少内存使用和提高性能。在享元模式中,对象被分为内部状态(Intrinsic State)和外部状态(Extrinsic State),其中内部状态可以被多个对象共享,而外部状态需要在对象之间单独维护。
享元模式(Flyweight Pattern)是一种结构型设计模式,其目的是通过共享对象来减少内存使用和提高性能。在享元模式中,对象被分为内部状态(Intrinsic State)和外部状态(Extrinsic State),其中内部状态可以被多个对象共享,而外部状态需要在对象之间单独维护。
享元模式包含以下几个角色:
- 享元工厂(Flyweight Factory):负责创建和管理享元对象,确保对象被正确地共享和重复使用。
- 抽象享元(Flyweight):声明了共享对象的接口,通过这个接口可以接收和操作外部状态。
- 具体享元(Concrete Flyweight):实现了抽象享元接口,并实现了内部状态的共享。
- 客户端(Client):通过享元工厂获取享元对象,并设置或获取外部状态,然后使用享元对象。
享元模式通常适用于以下情况:
- 当系统中存在大量相似对象,这些对象具有一些共同的属性,可以抽取出来作为内部状态进行共享。
- 当需要缓存对象以提高性能时,可以使用享元模式来重复使用相似对象。
以下是一个简单的享元模式的示例:
import java.util.HashMap;
import java.util.Map;
// 抽象享元
interface Shape {
void draw(String color);
}
// 具体享元
class Circle implements Shape {
private String color;
public Circle() {
this.color = null;
}
@Override
public void draw(String color) {
System.out.println("Drawing circle with color: " + color);
}
}
// 享元工厂
class ShapeFactory {
private static final Map<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle) circleMap.get(color);
if (circle == null) {
circle = new Circle();
circleMap.put(color, circle);
}
return circle;
}
}
// 客户端
public class Client {
private static final String[] colors = {"Red", "Green", "Blue"};
public static void main(String[] args) {
for (int i = 0; i < 10; ++i) {
Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
circle.draw(getRandomColor());
}
}
private static String getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}
}
行为型模式
- 策略模式:策略模式定义一系列算法,并将每个算法封装起来,使它们可以相互替换。
- 模板方法模式:模板方法模式定义一个操作中的算法的框架,将某些步骤延迟到子类中实现。
- 观察者模式:观察者模式定义一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。
- 迭代子模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
- 责任链模式:责任链模式允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理它。责任链模式将请求发送者和接收者解耦,使得多个对象都有机会处理请求,同时避免了请求发送者需要知道处理请求的具体处理者的情况。
- 命令模式:将请求封装成一个对象,从而允许使用不同的请求、队列或者日志来参数化其他对象,并支持可撤销的操作。
- 备忘录模式:允许在不暴露对象实现细节的情况下保存和恢复对象的内部状态。备忘录模式通常用于需要记录对象状态历史、撤销操作或者提供快照功能的场景。
- 状态模式:允许对象在内部状态改变时改变它的行为,看起来好像改变了它的类。状态模式将每个状态封装成一个类,并将对象的行为委托给当前状态对象。
- 访问者模式:访问者模式能够将算法与对象结构分离开来,使得可以在不改变对象结构的情况下定义新的操作。访问者模式的核心思想是在不改变元素的类的前提下,通过定义访问者类来对元素进行操作。
- 中介者模式:中介者模式通过封装一系列对象之间的交互方式,来减少对象之间的直接依赖关系,从而降低系统的耦合性。中介者模式将系统中各个对象之间的交互行为集中到中介者对象中进行处理,而不是让对象之间相互引用。
- 解释器模式:解释器模式定义一种语言的文法,并且构建一个解释器来解释这个语言中的句子。解释器模式通常用于处理复杂的语法或规则,它将一个表达式解析成一个抽象语法树,并提供一种灵活的方式来进行解释、执行或操作。
1、策略模式
策略模式定义一系列算法,并将每个算法封装起来,使它们可以相互替换。
策略模式包含以下几个角色:
- 策略接口(Strategy):定义了所有支持的算法的通用接口。
- 具体策略(Concrete Strategy):实现了策略接口,提供了具体的算法实现。
- 上下文(Context):维护一个对策略对象的引用,同时提供了一个接口,供客户端调用。
策略模式通常适用于以下情况:
- 当需要在运行时选择算法时,可以使用策略模式来动态地切换算法。
- 当一个类有多种行为,而这些行为在不同的场景下可能发生变化时。
以下是一个简单的策略模式的示例:
// 策略接口
interface PaymentStrategy {
void pay(int amount);
}
// 具体策略 - 支付宝支付
class AliPayStrategy implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " via AliPay");
}
}
// 具体策略 - 微信支付
class WeChatPayStrategy implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " via WeChatPay");
}
}
// 上下文
class PaymentContext {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void pay(int amount) {
if (paymentStrategy != null) {
paymentStrategy.pay(amount);
} else {
System.out.println("No payment method selected");
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
PaymentContext paymentContext = new PaymentContext();
// 使用支付宝支付
paymentContext.setPaymentStrategy(new AliPayStrategy());
paymentContext.pay(100);
// 使用微信支付
paymentContext.setPaymentStrategy(new WeChatPayStrategy());
paymentContext.pay(200);
}
}
2、模板方法模式
模板方法模式定义一个操作中的算法的框架,将某些步骤延迟到子类中实现。
模板方法模式包含以下几个角色:
- 抽象模板类(Abstract Template):定义了一个模板方法,其中包含了算法的骨架,具体步骤可以由子类实现。
- 具体模板类(Concrete Template):实现了抽象模板类中的具体步骤,完成算法的各个步骤。
- 钩子方法(Hook Method):在抽象模板类中定义的可选步骤,子类可以选择性地实现。
- 具体子类(Concrete Subclass):实现了抽象模板类中的抽象方法,以完成具体的算法。
模板方法模式通常适用于以下情况:
- 当有一些通用的步骤,但各个子类又有不同的实现时,可以将这些通用步骤放在抽象模板类中实现。
- 当不想让子类改变算法的整体结构,但又允许子类改变算法中的某些特定步骤时。
以下是一个简单的模板方法模式的示例:
// 抽象模板类
abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
// 模板方法,定义了算法的骨架
public final void play() {
initialize();
startPlay();
endPlay();
}
}
// 具体模板类 - 篮球游戏
class BasketballGame extends Game {
@Override
void initialize() {
System.out.println("Basketball game initialized");
}
@Override
void startPlay() {
System.out.println("Basketball game started");
}
@Override
void endPlay() {
System.out.println("Basketball game ended");
}
}
// 具体模板类 - 足球游戏
class FootballGame extends Game {
@Override
void initialize() {
System.out.println("Football game initialized");
}
@Override
void startPlay() {
System.out.println("Football game started");
}
@Override
void endPlay() {
System.out.println("Football game ended");
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Game basketballGame = new BasketballGame();
basketballGame.play();
System.out.println();
Game footballGame = new FootballGame();
footballGame.play();
}
}
3、观察者模式
观察者模式也被称为发布-订阅(Publish-Subscribe)模式。
观察者模式定义一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。
观察者模式包含以下几个角色:
- 主题(Subject):也称为被观察者或可观察者,它维护一系列观察者对象,并提供添加、删除和通知观察者的方法。
- 观察者(Observer):定义了一个更新接口,使得在主题状态改变时能够接收到通知并进行相应的处理。
- 具体主题(Concrete Subject):实现了主题接口,负责维护具体的观察者列表,并在状态改变时发送通知给观察者。
- 具体观察者(Concrete Observer):实现了观察者接口,定义了在接收到主题通知时所采取的具体行动。
观察者模式通常适用于以下情况:
- 当一个对象的改变需要通知其他对象,并且不知道这些对象是谁时,可以使用观察者模式。
- 当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少个对象需要改变时,也可以使用观察者模式。
以下是一个简单的观察者模式的示例:
import java.util.ArrayList;
import java.util.List;
// 主题
interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
// 具体主题
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private int state;
public void setState(int state) {
this.state = state;
notifyObservers();
}
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
// 观察者
interface Observer {
void update(int state);
}
// 具体观察者A
class ConcreteObserverA implements Observer {
@Override
public void update(int state) {
System.out.println("ConcreteObserverA: State changed to " + state);
}
}
// 具体观察者B
class ConcreteObserverB implements Observer {
@Override
public void update(int state) {
System.out.println("ConcreteObserverB: State changed to " + state);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observerA = new ConcreteObserverA();
Observer observerB = new ConcreteObserverB();
subject.attach(observerA);
subject.attach(observerB);
subject.setState(10);
subject.setState(20);
}
}
4、迭代子模式
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
迭代子模式包含以下几个角色:
- 迭代器接口(Iterator):定义了访问和遍历聚合对象元素的接口。
- 具体迭代器(Concrete Iterator):实现了迭代器接口,负责对聚合对象进行遍历并记录当前位置。
- 聚合接口(Aggregate):定义了创建迭代器对象的接口。
- 具体聚合(Concrete Aggregate):实现了聚合接口,负责创建对应的具体迭代器对象。
迭代子模式通常适用于以下情况:
- 当需要对聚合对象进行遍历并且不想暴露其内部结构时,可以使用迭代子模式。
- 当需要提供一种统一的方法来访问不同类型的聚合对象时,也可以使用迭代子模式。
以下是一个简单的迭代子模式的示例:
import java.util.ArrayList;
import java.util.List;
// 迭代器接口
interface Iterator {
boolean hasNext();
Object next();
}
// 具体迭代器
class ConcreteIterator implements Iterator {
private List<Object> elements;
private int position = 0;
public ConcreteIterator(List<Object> elements) {
this.elements = elements;
}
@Override
public boolean hasNext() {
return position < elements.size();
}
@Override
public Object next() {
if (hasNext()) {
return elements.get(position++);
}
return null;
}
}
// 聚合接口
interface Aggregate {
Iterator createIterator();
}
// 具体聚合
class ConcreteAggregate implements Aggregate {
private List<Object> elements = new ArrayList<>();
public void add(Object element) {
elements.add(element);
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(elements);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate();
aggregate.add("Element 1");
aggregate.add("Element 2");
aggregate.add("Element 3");
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
5、责任链模式
责任链模式允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理它。责任链模式将请求发送者和接收者解耦,使得多个对象都有机会处理请求,同时避免了请求发送者需要知道处理请求的具体处理者的情况。
责任链模式包含以下几个角色:
- 处理者接口(Handler):定义了处理请求的接口,通常包含一个指向下一个处理者的引用。
- 具体处理者(Concrete Handler):实现了处理者接口,负责处理请求,如果自己无法处理,则将请求传递给下一个处理者。
- 客户端(Client):创建并且发送请求到处理者链中的第一个处理者。
责任链模式通常适用于以下情况:
- 当有多个对象可以处理同一个请求,并且客户端不知道哪个对象能够处理时,可以使用责任链模式。
- 当需要在不明确指定接收者的情况下,通过一组对象之一来处理请求时,也可以使用责任链模式。
以下是一个简单的责任链模式的示例:
// 处理者接口
interface Handler {
void handleRequest(int request);
}
// 具体处理者A
class ConcreteHandlerA implements Handler {
private Handler nextHandler;
public void setNextHandler(Handler handler) {
this.nextHandler = handler;
}
@Override
public void handleRequest(int request) {
if (request < 10) {
System.out.println("ConcreteHandlerA handles request: " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
// 具体处理者B
class ConcreteHandlerB implements Handler {
private Handler nextHandler;
public void setNextHandler(Handler handler) {
this.nextHandler = handler;
}
@Override
public void handleRequest(int request) {
if (request >= 10 && request < 20) {
System.out.println("ConcreteHandlerB handles request: " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 创建处理者对象
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
// 设置处理者之间的关系
handlerA.setNextHandler(handlerB);
// 发送请求
handlerA.handleRequest(5);
handlerA.handleRequest(15);
handlerA.handleRequest(25);
}
}
6、命令模式
将请求封装成一个对象,从而允许使用不同的请求、队列或者日志来参数化其他对象,并支持可撤销的操作。
命令模式包含以下几个角色:
- 命令接口(Command):声明了执行请求的方法。
- 具体命令(Concrete Command):实现了命令接口,负责执行具体的请求。
- 调用者(Invoker):负责调用命令对象执行请求。
- 接收者(Receiver):知道如何执行一个请求,任何类都可能成为一个接收者。
命令模式通常适用于以下情况:
- 当需要将请求发送者与请求接收者解耦时,可以使用命令模式。
- 当需要支持撤销操作时,可以使用命令模式。
以下是一个简单的命令模式的示例:
// 命令接口
interface Command {
void execute();
}
// 具体命令
class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
}
// 接收者
class Receiver {
public void action() {
System.out.println("Receiver executes action");
}
}
// 调用者
class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
}
}
7、备忘录模式
允许在不暴露对象实现细节的情况下保存和恢复对象的内部状态。备忘录模式通常用于需要记录对象状态历史、撤销操作或者提供快照功能的场景。
备忘录模式包含以下几个角色:
- 发起人(Originator):负责创建备忘录对象,以保存当前内部状态,并可以使用备忘录对象恢复内部状态。
- 备忘录(Memento):负责存储发起人对象的内部状态。
- 管理者(Caretaker):负责管理备忘录对象,可以保存和获取备忘录对象,并通知发起人对象。
备忘录模式通常适用于以下情况:
- 当需要保存和恢复对象的内部状态,同时又不希望暴露对象的实现细节时,可以使用备忘录模式。
- 当需要提供撤销操作或者历史记录功能时,也可以使用备忘录模式。
以下是一个简单的备忘录模式的示例:
// 备忘录类
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 发起人类
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 getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
// 管理者类
class Caretaker {
private Memento memento;
public void saveMemento(Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
// 修改发起人状态并保存备忘录
originator.setState("State 1");
Memento memento = originator.saveStateToMemento();
caretaker.saveMemento(memento);
// 修改发起人状态
originator.setState("State 2");
// 恢复发起人状态
Memento savedMemento = caretaker.getMemento();
originator.getStateFromMemento(savedMemento);
System.out.println("Restored State: " + originator.getState());
}
}
8、状态模式
允许对象在内部状态改变时改变它的行为,看起来好像改变了它的类。状态模式将每个状态封装成一个类,并将对象的行为委托给当前状态对象。
状态模式包含以下几个角色:
- 上下文(Context):定义客户端感兴趣的接口,维护一个对状态对象的引用,并将请求委托给当前状态对象处理。
- 抽象状态(State):定义一个接口以封装与上下文的一个特定状态相关的行为。
- 具体状态(Concrete State):实现抽象状态定义的接口,并且负责处理状态相关的行为。
状态模式通常适用于以下情况:
- 当对象的行为取决于它的状态,并且在运行时可以根据状态改变行为时,可以使用状态模式。
- 当有大量的条件语句来控制一个对象的行为时,可以使用状态模式来提高代码的可维护性。
以下是一个简单的状态模式的示例:
// 上下文
class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public void request() {
state.handleRequest(this);
}
}
// 抽象状态
interface State {
void handleRequest(Context context);
}
// 具体状态A
class ConcreteStateA implements State {
@Override
public void handleRequest(Context context) {
System.out.println("Handling request in State A");
context.setState(new ConcreteStateB());
}
}
// 具体状态B
class ConcreteStateB implements State {
@Override
public void handleRequest(Context context) {
System.out.println("Handling request in State B");
context.setState(new ConcreteStateA());
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.setState(new ConcreteStateA());
context.request();
context.request();
}
}
9、访问者模式
访问者模式能够将算法与对象结构分离开来,使得可以在不改变对象结构的情况下定义新的操作。访问者模式的核心思想是在不改变元素的类的前提下,通过定义访问者类来对元素进行操作。
访问者模式包含以下几个角色:
- 访问者接口(Visitor):定义了对每个元素访问的操作,可以在不修改具体元素类的情况下定义新的操作。
- 具体访问者(Concrete Visitor):实现了访问者接口中定义的操作,对元素进行具体的处理。
- 元素接口(Element):定义了一个
accept
方法,该方法接受一个访问者对象作为参数,以便让访问者访问自身。 - 具体元素(Concrete Element):实现了元素接口中的
accept
方法,接受访问者对象的访问,并调用访问者的方法进行处理。 - 对象结构(Object Structure):存储了具体元素对象,提供了接受访问者对象的方法,以便访问者能够遍历访问其中的元素。
访问者模式通常适用于以下情况:
- 当一个对象结构包含许多具体类对象,而且需要对这些对象进行不同的操作时,可以使用访问者模式。
- 当对象的操作需要多次变更,但是对象本身不希望发生变化时,可以使用访问者模式。
以下是一个简单的访问者模式的示例:
// 访问者接口
interface Visitor {
void visit(ElementA element);
void visit(ElementB element);
}
// 具体访问者A
class ConcreteVisitorA implements Visitor {
@Override
public void visit(ElementA element) {
System.out.println("ConcreteVisitorA visits ElementA");
}
@Override
public void visit(ElementB element) {
System.out.println("ConcreteVisitorA visits ElementB");
}
}
// 具体访问者B
class ConcreteVisitorB implements Visitor {
@Override
public void visit(ElementA element) {
System.out.println("ConcreteVisitorB visits ElementA");
}
@Override
public void visit(ElementB element) {
System.out.println("ConcreteVisitorB visits ElementB");
}
}
// 元素接口
interface Element {
void accept(Visitor visitor);
}
// 具体元素A
class ElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素B
class ElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 对象结构
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 Client {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new ElementA());
objectStructure.attach(new ElementB());
Visitor visitorA = new ConcreteVisitorA();
objectStructure.accept(visitorA);
Visitor visitorB = new ConcreteVisitorB();
objectStructure.accept(visitorB);
}
}
10、中介者模式
中介者模式通过封装一系列对象之间的交互方式,来减少对象之间的直接依赖关系,从而降低系统的耦合性。中介者模式将系统中各个对象之间的交互行为集中到中介者对象中进行处理,而不是让对象之间相互引用。
中介者模式包含以下几个角色:
- 中介者(Mediator):定义了一个接口用于与各个同事对象进行通信,可以通过该接口将具体同事对象的通知传递给其他同事对象。
- 具体中介者(Concrete Mediator):实现了中介者接口,负责协调各个同事对象的交互行为。
- 同事类(Colleague):定义了一个接口用于与中介者进行通信,每个同事对象都知道中介者对象,并可以通过中介者来通知其他同事对象。
- 具体同事类(Concrete Colleague):实现了同事接口,每个具体同事类都知道自己的中介者对象,并通过中介者对象来与其他同事对象进行通信。
中介者模式通常适用于以下情况:
- 当对象之间存在复杂的交互关系,并且导致对象之间紧密耦合时,可以使用中介者模式来解耦对象之间的关系。
- 当一个对象行为发生改变可能会影响到其他对象,但又不希望对象之间直接耦合时,可以使用中介者模式。
以下是一个简单的中介者模式的示例:
// 中介者接口
interface Mediator {
void send(String message, Colleague colleague);
}
// 具体中介者
class ConcreteMediator implements Mediator {
private Colleague colleague1;
private Colleague colleague2;
public void setColleague1(Colleague colleague) {
this.colleague1 = colleague;
}
public void setColleague2(Colleague colleague) {
this.colleague2 = colleague;
}
@Override
public void send(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.receive(message);
} else {
colleague1.receive(message);
}
}
}
// 同事接口
interface Colleague {
void send(String message);
void receive(String message);
}
// 具体同事类A
class ConcreteColleagueA implements Colleague {
private Mediator mediator;
public ConcreteColleagueA(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void send(String message) {
mediator.send(message, this);
}
@Override
public void receive(String message) {
System.out.println("ConcreteColleagueA received: " + message);
}
}
// 具体同事类B
class ConcreteColleagueB implements Colleague {
private Mediator mediator;
public ConcreteColleagueB(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void send(String message) {
mediator.send(message, this);
}
@Override
public void receive(String message) {
System.out.println("ConcreteColleagueB received: " + message);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator);
ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator);
mediator.setColleague1(colleagueA);
mediator.setColleague2(colleagueB);
colleagueA.send("Hello from ColleagueA");
colleagueB.send("Hi from ColleagueB");
}
}
11、解释器模式
解释器模式定义一种语言的文法,并且构建一个解释器来解释这个语言中的句子。解释器模式通常用于处理复杂的语法或规则,它将一个表达式解析成一个抽象语法树,并提供一种灵活的方式来进行解释、执行或操作。
解释器模式包含以下几个角色:
- 抽象表达式(Abstract Expression):定义了一个抽象的解释操作,通常包含一个
interpret()
方法,用于解释表达式。 - 终结符表达式(Terminal Expression):实现了抽象表达式接口,表示语法中的终结符,不再包含子表达式。
- 非终结符表达式(Nonterminal Expression):实现了抽象表达式接口,表示语法中的非终结符,通常包含多个子表达式。
- 上下文(Context):包含解释器之外的一些全局信息,通常被解释器用来保存解释器中间结果或者共享信息。
解释器模式通常适用于以下情况:
- 当有一个语言需要解释执行,并且可以将该语言的语法表示为一个表达式文法树时,可以使用解释器模式。
- 当需要按照特定的规则进行解析语言或表达式时,可以使用解释器模式。
以下是一个简单的解释器模式的示例:
// 抽象表达式
interface Expression {
int interpret();
}
// 终结符表达式
class NumberExpression implements Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret() {
return number;
}
}
// 非终结符表达式
class AddExpression implements Expression {
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
// 上下文
class Context {
private String input;
private int output;
public Context(String input) {
this.input = input;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public int getOutput() {
return output;
}
public void setOutput(int output) {
this.output = output;
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 构建解释器上下文
Context context = new Context("1+2+3");
// 解析表达式
Expression expression = parseExpression(context.getInput());
// 执行解释器
context.setOutput(expression.interpret());
// 输出结果
System.out.println(context.getOutput());
}
// 解析表达式
private static Expression parseExpression(String input) {
String[] tokens = input.split("\\+");
Expression expression = null;
for (int i = 0; i < tokens.length; i++) {
if (expression == null) {
expression = new NumberExpression(Integer.parseInt(tokens[i]));
} else {
expression = new AddExpression(expression, new NumberExpression(Integer.parseInt(tokens[i])));
}
}
return expression;
}
}
标签:String,对象,void,class,new,设计模式,public
From: https://www.cnblogs.com/myhikari/p/18160025