java开发设计模式详解
目录
一、概述
1. 创建型模式(5种)
2. 结构型模式(7种)
3. 行为型模式(11种)
二、代码示例说明
1.单例模式(Singleton)
2.工厂方法模式(Factory Method)
3.抽象工厂模式(Abstract Factory)
4.建造者模式(Builder)
5.原型模式 (Prototype)
6.适配器模式(Adapter)
7.桥接模式(Bridge)
8.组合模式(Composite)
9.装饰器模式(Decorator)
10.外观模式(Facade)
11.享元模式(Flyweight)
12.代理模式(Proxy)
13.策略模式(Strategy)
14.模板方法模式(Template Method)
15.观察者模式(Observer)
16.迭代器模式(Iterator)
17.责任链模式(Chain of Responsibility)
18.命令模式(Command)
19.备忘录模式(Memento)
一、概述
Java开发中的设计模式是软件工程中用于解决常见问题的一系列最佳实践。这些设计模式并不是Java特有的,但它们与Java等面向对象编程语言结合得非常好,因为设计模式本质上就是面向对象设计原则的具体化。
设计模式主要分为三大类:创建型模式、结构型模式和行为型模式。下面简要介绍每一类中的一些常见设计模式及其在Java开发中的应用。
1. 创建型模式(5种)
创建型模式主要关注对象的创建过程。
-
单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。常用于配置例如文件的读取、数据库连接等。
-
工厂方法模式(Factory Method):定义一个用于创建对象的接口,但让子类决定要实例化哪个类。工厂方法让类的实例化推迟到子类中进行。
-
抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
-
建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
-
原型模式(Prototype):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
2. 结构型模式(7种)
结构型模式关注类和对象的组合。
-
适配器模式(Adapter):将一个类的接口转换成客户端所期待的另一种接口形式,使接口不兼容的类可以一起工作。
-
桥接模式(Bridge):将抽象部分与实现部分分离,使它们都可以独立地变化。
-
组合模式(Composite):将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
-
装饰器模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更为灵活。
-
外观模式(Facade):为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
-
享元模式(Flyweight):运用共享技术有效地支持大量细粒度对象的复用。
-
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。
3. 行为型模式(11种)
行为型模式关注对象之间的通信和职责分配。
-
策略模式(Strategy):定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。
-
模板方法模式(Template Method):定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中实现。
-
观察者模式(Observer):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
-
迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
-
责任链模式(Chain of Responsibility):为请求的发送者和接收者之间解耦,使多个对象都有机会处理这个请求,或者将这个请求传递到链上的下一个对象。
-
命令模式(Command):将一个请求封装为一个对象,从而使我们可用不同的请求、队列、日志来参数化其他对象。
-
备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
-
状态模式(State):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
-
访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
-
中介者模式(Mediator):用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
-
解释器模式(Interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
这些设计模式在Java开发中有着广泛的应用,它们可以帮助开发者设计出更加灵活、可扩展和易于维护的软件系统。
二、代码示例说明
1.单例模式(Singleton)
懒汉式(线程安全):通过在getInstance()
方法上添加synchronized
关键字,可以解决线程安全问题,但会降低效率。
public class SingletonLazySafe {
private static SingletonLazySafe instance;private SingletonLazySafe() {}
public static synchronized SingletonLazySafe getInstance() {
if (instance == null) {
instance = new SingletonLazySafe();
}
return instance;
}
}
饿汉式:这种方式基于类加载机制避免了多线程同步问题,但是实例在类装载时就完成创建,没有达到Lazy Loading的效果
public class SingletonEager {
private static final SingletonEager instance = new SingletonEager();private SingletonEager() {}
public static SingletonEager getInstance() {
return instance;
}
}
双重检查锁定: 这种方式既实现了延迟加载,又保证了线程安全,但是需要注意instance
变量必须声明为volatile
类型。
public class SingletonDoubleChecked {
// 使用volatile关键字保证多线程下的可见性和禁止指令重排序
private static volatile SingletonDoubleChecked instance;private SingletonDoubleChecked() {}
public static SingletonDoubleChecked getInstance() {
if (instance == null) {
synchronized (SingletonDoubleChecked.class) {
if (instance == null) {
instance = new SingletonDoubleChecked();
}
}
}
return instance;
}
}
在实际应用中,推荐使用饿汉式(如果不需要延迟加载)或双重检查锁定方式(如果需要延迟加载)。懒汉式(线程安全)虽然解决了线程安全问题,但每次调用getInstance()
时都进行同步,影响性能。而双重检查锁定方式既保证了线程安全,又避免了不必要的同步开销。
2.工厂方法模式(Factory Method)
工厂方法模式是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。
// 定义一个产品接口
interface Product {
void use();
}// 实现产品接口的具体产品类
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductA");
}
}class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductB");
}
}// 定义一个创建产品对象的工厂接口
interface Creator {
Product factoryMethod();
}// 实现工厂接口的类,并具体指定返回的产品类型
class ConcreteCreatorA implements Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductA();
}
}class ConcreteCreatorB implements Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductB();
}
}// 客户端代码
public class FactoryMethodPatternDemo {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
Product productA = creatorA.factoryMethod();
productA.use();Creator creatorB = new ConcreteCreatorB();
Product productB = creatorB.factoryMethod();
productB.use();
}
}
在上面的代码中,Product
是一个产品接口,ConcreteProductA
和ConcreteProductB
是实现了Product
接口的具体产品类。Creator
是一个工厂接口,它声明了一个工厂方法factoryMethod()
,用于创建产品对象。ConcreteCreatorA
和ConcreteCreatorB
是实现了Creator
接口的具体工厂类,它们分别重写了factoryMethod()
方法,并返回了不同的具体产品实例。
在客户端代码中,我们通过不同的工厂类来创建不同的产品对象,并调用其use()
方法。这样就实现了在运行时根据不同的工厂类来创建不同类型的产品对象,这就是工厂方法模式的核心思想。
3.抽象工厂模式(Abstract Factory)
在抽象工厂模式中,我们定义一个抽象工厂接口,用于创建相关或依赖对象的家族,但不指定具体类。每个生成的工厂类能够返回该工厂所创建的对象类型的具体实例。
// 定义一个产品接口A
interface ProductA {
void use();
}// 实现产品接口A的具体产品类
class ConcreteProductA1 implements ProductA {
@Override
public void use() {
System.out.println("Using ConcreteProductA1");
}
}class ConcreteProductA2 implements ProductA {
@Override
public void use() {
System.out.println("Using ConcreteProductA2");
}
}// 定义一个产品接口B
interface ProductB {
void eat();
}// 实现产品接口B的具体产品类
class ConcreteProductB1 implements ProductB {
@Override
public void eat() {
System.out.println("Eating ConcreteProductB1");
}
}class ConcreteProductB2 implements ProductB {
@Override
public void eat() {
System.out.println("Eating ConcreteProductB2");
}
}// 抽象工厂接口
interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}// 具体工厂类A
class ConcreteFactoryA implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}// 具体工厂类B
class ConcreteFactoryB implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA2();
}@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
}// 客户端代码
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
AbstractFactory factoryA = new ConcreteFactoryA();ProductA productA1 = factoryA.createProductA();
productA1.use();ProductB productB1 = factoryA.createProductB();
productB1.eat();AbstractFactory factoryB = new ConcreteFactoryB();
ProductA productA2 = factoryB.createProductA();
productA2.use();ProductB productB2 = factoryB.createProductB();
productB2.eat();
}
}
在这个例子中,我们定义了两个产品接口ProductA
和ProductB
,以及它们的实现类。然后,我们定义了一个抽象工厂接口AbstractFactory
,它包含两个方法createProductA()
和createProductB()
,分别用于创建ProductA
和ProductB
类型的产品。接着,我们定义了两个具体工厂类ConcreteFactoryA
和ConcreteFactoryB
,它们实现了AbstractFactory
接口,并分别返回不同类型的产品实例
4.建造者模式(Builder)
建造者模式(Builder Pattern)是一种对象创建型设计模式,它通过将复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式通常包含以下几个角色:
-
Product(产品角色):被构建的复杂对象,包含多个组成部件的类。
-
Builder(抽象建造者):为创建Product对象的各个部件指定抽象接口。
-
ConcreteBuilder(具体建造者):实现Builder接口,构造和装配产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。
-
Director(指挥者):构建一个使用Builder接口的对象。它主要是用于隔离客户与对象的生产过程,并指导如何生成对象。
-
Client(客户端):向Director提供需求信息,不直接参与产品的构建过程
// 产品角色
class Product {
private String partA;
private String partB;// 产品的组成部分
void setPartA(String partA) {
this.partA = partA;
}void setPartB(String partB) {
this.partB = partB;
}// 显示产品
void show() {
System.out.println("Product Parts:");
System.out.println("Part A: " + partA);
System.out.println("Part B: " + partB);
}
}// 抽象建造者
interface Builder {
void buildPartA();
void buildPartB();
Product getResult();
}// 具体建造者
class ConcreteBuilder implements Builder {
private Product product = new Product();@Override
public void buildPartA() {
product.setPartA("PartA");
}@Override
public void buildPartB() {
product.setPartB("PartB");
}@Override
public Product getResult() {
return product;
}
}// 指挥者
class Director {
private Builder builder;public Director(Builder builder) {
this.builder = builder;
}// 构造复杂对象
public void construct() {
builder.buildPartA();
builder.buildPartB();
}
}// 客户端
public class BuilderPatternDemo {
public static void main(String[] args) {
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);director.construct();
Product product = builder.getResult();
product.show();
}
}
在这个例子中,Product
类是一个复杂对象,它由两个部分组成(PartA和PartB)。Builder
接口定义了构建复杂对象的接口,而ConcreteBuilder
类实现了这个接口,并具体构建了Product
对象。Director
类负责指导如何构建Product
对象,它通过Builder
接口与ConcreteBuilder
类进行交互。最后,在客户端代码中,我们创建了Builder
和Director
对象,并通过Director
来构建Product
对象。
5.原型模式 (Prototype)
在Java中,原型模式(Prototype Pattern)通常涉及到对象的克隆。当我们需要从一个现有的对象创建一个新的、完全相同的对象时,就可以使用原型模式。这通常通过实现Cloneable
接口并重写Object
类的clone()
方法来完成。但是,请注意,clone()
方法默认提供的是浅拷贝(shallow copy),它只复制对象本身和对象中的基本数据类型,但不会复制对象中的引用类型指向的对象。为了实现深拷贝(deep copy),我们需要自己编写复制引用类型指向的对象的逻辑。
以下是一个使用原型模式的Java代码示例,其中包含了浅拷贝和深拷贝的说明。我们将创建一个Person
类,该类包含基本数据类型和引用类型(例如,一个指向Address
对象的引用)。然后,我们将展示如何实现这个类的浅拷贝和深拷贝。
// Address 类,一个简单的引用类型
class Address {
String street;
String city;public Address(String street, String city) {
this.street = street;
this.city = city;
}@Override
public String toString() {
return "Address{" +
"street='" + street + '\'' +
", city='" + city + '\'' +
'}';
}
}// Person 类,实现了 Cloneable 接口以支持克隆
class Person implements Cloneable {
String name;
int age;
Address address; // 引用类型public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}// 浅拷贝方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}// 深拷贝方法
public Person deepClone() throws CloneNotSupportedException {
Person cloned = (Person) clone(); // 先进行浅拷贝
cloned.address = (Address) address.clone(); // 但这里需要修正,因为 Address 没有实现 Cloneable// 假设 Address 也实现了 Cloneable,则上面的行将有效
// 但为了演示,我们在这里手动复制 Address 对象
cloned.address = new Address(address.street, address.city);return cloned;
}@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}// 注意:这里我们假设 Address 类也实现了 Cloneable,但实际上它没有。
// 在实际项目中,如果 Address 或其他引用类型需要被深拷贝,它们也应该实现 Cloneable。
}// 注意:上面的代码中,Address 类并没有实现 Cloneable 接口,
// 因此直接调用 address.clone() 会导致编译错误。
// 为了简单起见,我们在 deepClone 方法中通过构造函数手动复制了 Address 对象。// 客户端代码
public class PrototypePatternDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("123 Elm Street", "Somewhere");
Person original = new Person("John Doe", 30, address);// 浅拷贝
Person shallowCloned = (Person) original.clone();
System.out.println("Original Person:");
System.out.println(original);
System.out.println("Shallow Cloned Person:");
System.out.println(shallowCloned);// 修改浅拷贝对象的 Address 的属性,将看到原始对象的 Address 属性也被修改了
shallowCloned.address.street = "456 Oak Street";
System.out.println("\nModified Shallow Cloned Person:");
System.out.println(shallowCloned);
System.out.println("Original Person (affected by shallow clone):");
System.out.println(original);// 深拷贝
Person deepCloned = original.deepClone();
System.out.println("\nOriginal Person again:");
System.out.println(original);
System.out.println("Deep Cloned Person:");
System.out.println(deepCloned);// 修改深拷贝对象的 Address 的属性,原始对象不会受到影响
deepCloned.address.street = "789 Pine Street";
System.out.println("\nModified Deep Cloned Person:");
System.out.println(deepCloned);
System.out.println("Original Person (unaffected by deep clone):
6.适配器模式(Adapter)
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许一个接口(通常是新的接口)与不符合该接口要求的类(通常是旧的类)协同工作。在适配器模式中,我们创建一个包装类(即适配器),该类负责接收客户端的请求,然后将这些请求转换为被适配者(Adaptee)可以理解的请求,并转发给被适配者。
以下是使用Java代码实现的适配器模式示例:
假设我们有一个老式的电源插座(TwoProngedOutlet
),它只能接受两个插头的电器(TwoProngedDevice
),但现在我们有一个新的电器(ThreeProngedDevice
),它有三个插头,无法直接插入老式的电源插座中。为了解决这个问题,我们可以创建一个适配器(ThreeToTwoAdapter
),它将三个插头的电器转换为两个插头的电器,以便能够插入老式的插座。
首先,定义电器接口和两个具体的电器类:
// 电器接口
interface Device {
void connect();
}// 两个插头的电器
class TwoProngedDevice implements Device {
@Override
public void connect() {
System.out.println("Connecting two-pronged device.");
}
}// 三个插头的电器
class ThreeProngedDevice {
public void plugInThreePronged() {
System.out.println("Plugging in three-pronged device.");
}
}
然后,定义适配器类,该类继承自ThreeProngedDevice
并实现Device
接口:
// 适配器类
class ThreeToTwoAdapter extends ThreeProngedDevice implements Device {
@Override
public void connect() {
// 调用三个插头的电器的方法,并添加适配逻辑
// 在这里,我们简单地模拟了适配过程
plugInThreePronged();
// 假设我们进行了某种适配操作,使得ThreeProngedDevice能作为TwoProngedDevice使用
System.out.println("Adapting three-pronged device to two-pronged outlet.");
}
}
最后,使用这些类来展示适配器模式的工作原理:
public class AdapterPatternDemo {
public static void main(String[] args) {
// 创建一个老式的插座能接受的电器
Device twoProngedDevice = new TwoProngedDevice();
twoProngedDevice.connect();// 创建一个适配器,让三个插头的电器能在老式插座上使用
Device threeProngedDeviceThroughAdapter = new ThreeToTwoAdapter();
threeProngedDeviceThroughAdapter.connect(); // 适配器内部处理了转换
}
}
在这个示例中,ThreeToTwoAdapter
类是一个适配器,它使得原本不符合Device
接口(即只有两个插头的插座能接受的电器)的ThreeProngedDevice
类,能够通过适配器的方式满足Device
接口的要求,从而可以在老式的插座上使用。这是适配器模式的一个典型应用场景。
7.桥接模式(Bridge)
桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与它的实现部分分离,使它们都可以独立地变化。这种模式通常用于实现多层次的抽象结构,其中每一层抽象都使用下一层抽象的实现来构建。
在Java中,桥接模式通常通过组合关系来替代继承关系,以减少类之间的耦合。以下是一个简单的Java代码示例,展示了桥接模式的应用。
假设我们有一个绘图系统,需要支持多种形状(如圆形和矩形)和多种颜色。我们可以使用桥接模式将形状和颜色分开,使得它们可以独立地变化。
首先,我们定义一个颜色接口(Color)和一个形状抽象类(Shape),并在形状类中持有一个颜色对象的引用。
// 颜色接口
interface Color {
void applyColor();
}// 红色实现
class Red implements Color {
@Override
public void applyColor() {
System.out.println("Applying red color");
}
}// 蓝色实现
class Blue implements Color {
@Override
public void applyColor() {
System.out.println("Applying blue color");
}
}// 形状抽象类
abstract class Shape {
protected Color color;public Shape(Color color) {
this.color = color;
}public abstract void draw();
// 设置颜色
public void setColor(Color color) {
this.color = color;
}// 应用颜色
public void applyColor() {
color.applyColor();
}
}// 圆形类
class Circle extends Shape {
public Circle(Color color) {
super(color);
}@Override
public void draw() {
applyColor();
System.out.println("Drawing Circle");
}
}// 矩形类
class Rectangle extends Shape {
public Rectangle(Color color) {
super(color);
}@Override
public void draw() {
applyColor();
System.out.println("Drawing Rectangle");
}
}// 客户端代码
public class BridgePatternDemo {
public static void main(String[] args) {
Shape redCircle = new Circle(new Red());
Shape blueRectangle = new Rectangle(new Blue());redCircle.draw();
blueRectangle.draw();// 修改矩形的颜色
blueRectangle.setColor(new Red());
blueRectangle.draw();
}
}
在这个例子中,Color
接口和它的具体实现(Red
和Blue
)代表了颜色部分,而Shape
抽象类和它的具体实现(Circle
和Rectangle
)代表了形状部分。Shape
类持有一个Color
类型的引用,这允许我们在运行时将形状与不同的颜色组合起来,而不需要为每种颜色和形状的组合创建一个新的类。
通过桥接模式,我们实现了抽象与实现的解耦,使得系统的扩展变得更加容易。例如,如果我们想要添加新的颜色或新的形状,我们只需要创建新的颜色实现或形状类,而不需要修改现有的代码。
8.组合模式(Composite)
组合模式(Composite Pattern)允许你将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式让客户端可以一致地处理单个对象和对象的组合。
在组合模式中,我们通常会定义三个角色:
-
Component(抽象组件):为组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component的子组件。
-
Leaf(叶子节点):在组合中表示叶节点对象,叶节点没有子节点。
-
Composite(复合节点):在组合中表示容器对象,容器可以包含Component子类的实例。实现Component接口中用于管理子组件的方法。
下面是一个使用Java实现的简单组合模式示例,我们将构建一个简单的文件系统,包括文件夹(Composite)和文件(Leaf):
// Component 接口
interface Component {
void add(Component c);
void remove(Component c);
Component getChild(int i);
void operation(); // 定义所有组件共有的行为
}// Leaf 类
class File implements Component {
private String name;public File(String name) {
this.name = name;
}@Override
public void add(Component c) {
throw new UnsupportedOperationException("File cannot have children");
}@Override
public void remove(Component c) {
throw new UnsupportedOperationException("File cannot have children");
}@Override
public Component getChild(int i) {
throw new UnsupportedOperationException("File cannot have children");
}@Override
public void operation() {
System.out.println("File: " + name + " is processed.");
}
}// Composite 类
class Folder implements Component {
private String name;
private List<Component> children = new ArrayList<>();public Folder(String name) {
this.name = name;
}@Override
public void add(Component c) {
children.add(c);
}@Override
public void remove(Component c) {
children.remove(c);
}@Override
public Component getChild(int i) {
return children.get(i);
}@Override
public void operation() {
System.out.println("Folder: " + name + " is processed.");
for (Component c : children) {
c.operation();
}
}
}// 客户端代码
public class CompositePatternDemo {
public static void main(String[] args) {
Component root = new Folder("root");Component folder1 = new Folder("folder1");
Component folder2 = new Folder("folder2");Component file1 = new File("file1.txt");
Component file2 = new File("file2.txt");root.add(folder1);
root.add(folder2);folder1.add(file1);
folder2.add(file2);// 遍历和打印所有组件
root.operation();
}
}
在这个示例中,Component
是一个接口,定义了组件的通用行为。File
类代表叶子节点,它实现了 Component
接口但不允许有子节点。Folder
类代表复合节点,它可以包含其他 Component
对象(即文件和文件夹)。operation()
方法在 Folder
类中被重写以递归地处理所有子组件。
客户端代码通过创建文件夹和文件对象,并将它们组织成树状结构来演示组合模式的使用。通过调用根文件夹的 operation()
方法,可以递归地处理整个文件系统中的所有文件和文件夹。
9.装饰器模式(Decorator)
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许你通过添加一个或多个装饰器来动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。这种模式创建了一个包装对象,也就是装饰器,来包裹真实对象。
下面是一个使用Java实现的装饰器模式的简单示例。在这个例子中,我们将创建一个简单的咖啡系统,包括不同类型的咖啡和不同的调料(如牛奶和摩卡)。
首先,定义一个咖啡(Beverage)接口,所有的饮料都实现这个接口:
public interface Beverage {
String getDescription();
double cost();
}
然后,实现一个具体的咖啡类(如Espresso),它实现了Beverage接口:
public class Espresso implements Beverage {
@Override
public String getDescription() {
return "Espresso";
}@Override
public double cost() {
return 1.99;
}
}
接下来,定义一个装饰器抽象类,它同样实现了Beverage接口,并持有一个Beverage类型的对象引用:
public abstract class BeverageDecorator implements Beverage {
protected Beverage beverage;public BeverageDecorator(Beverage beverage) {
this.beverage = beverage;
}@Override
public String getDescription() {
return beverage.getDescription();
}@Override
public double cost() {
return beverage.cost();
}
}
现在,实现具体的装饰器类,比如Milk(牛奶)和Mocha(摩卡):
public class Milk extends BeverageDecorator {
public Milk(Beverage beverage) {
super(beverage);
}@Override
public String getDescription() {
return beverage.getDescription() + ", Milk";
}@Override
public double cost() {
return beverage.cost() + 0.10;
}
}public class Mocha extends BeverageDecorator {
public Mocha(Beverage beverage) {
super(beverage);
}@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}@Override
public double cost() {
return beverage.cost() + 0.20;
}
}
最后,我们可以这样使用装饰器模式来创建并打印不同调料的咖啡:
public class StarbuzzCoffee {
public static void main(String[] args) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());Beverage beverage2 = new Milk(new Espresso());
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());Beverage beverage3 = new Mocha(new Milk(new Espresso()));
System.out.println(beverage3.getDescription() + " $" + beverage3.cost());// 可以继续添加更多的装饰器
}
}
在这个例子中,我们创建了Espresso对象,并使用Milk和Mocha装饰器来动态地添加调料和价格。这种方式使得代码更加灵活和可扩展,因为它允许我们在不修改现有类的情况下,通过添加新的装饰器来添加新的功能。
10.外观模式(Facade)
外观模式为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
假设我们有一个系统,该系统用于处理文件的各种操作,比如读取、写入和删除文件。这些操作分别由不同的类处理。为了简化对这些操作的调用,我们可以使用外观模式来提供一个统一的接口。
// 文件读取类
class FileReader {
public String read(String filename) {
// 模拟读取文件内容
return "文件内容:" + filename;
}
}// 文件写入类
class FileWriter {
public void write(String filename, String content) {
// 模拟写入文件内容
System.out.println("向文件 " + filename + " 写入内容:" + content);
}
}// 文件删除类
class FileDeleter {
public void delete(String filename) {
// 模拟删除文件
System.out.println("删除文件:" + filename);
}
}
然后,我们定义一个外观类来封装这些操作:
// 文件操作外观类
class FileFacade {
private FileReader reader;
private FileWriter writer;
private FileDeleter deleter;public FileFacade() {
this.reader = new FileReader();
this.writer = new FileWriter();
this.deleter = new FileDeleter();
}// 提供一个统一的接口
public void performFileOperations() {
String filename = "example.txt";
String content = "Hello, Facade Pattern!";// 读取文件
String fileContent = reader.read(filename);
System.out.println(fileContent);// 写入文件
writer.write(filename, content);// 删除文件(这里仅为示例,实际可能不需要立即删除)
deleter.delete(filename);
}
}
最后,我们创建一个测试类来使用外观类:
public class FacadePatternDemo {
public static void main(String[] args) {
FileFacade facade = new FileFacade();
facade.performFileOperations();
}
}
在这个例子中,FileFacade
类为复杂的文件操作提供了一个简单的接口。客户端(在这个例子中是 FacadePatternDemo
类)只需要与 FileFacade
交互,而不需要直接与 FileReader
、FileWriter
和 FileDeleter
交互,这降低了系统的耦合度,也简化了客户端的使用。
11.享元模式(Flyweight)
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。它通过共享技术来支持大量细粒度的对象。在享元模式中,对象的状态被分为内部状态(intrinsic state)和外部状态(extrinsic state)。内部状态是不变的,可以在多个对象中共享;而外部状态是随环境变化的,不可以共享。
下面是一个Java代码示例,演示了享元模式的应用。在这个例子中,我们假设有一个简单的图形系统,需要绘制大量的圆形,但这些圆形可能只有少数几种颜色。为了节省内存,我们可以使用享元模式来共享相同颜色的圆形对象。
首先,定义圆形的接口和具体实现类(享元类):
// 圆形接口
interface Shape {
void draw(String color);
}// 圆形具体实现类(享元类)
class Circle implements Shape {
private String color; // 内部状态,通常设置为私有以封装对象// 构造函数,设置内部状态
public Circle(String color) {
this.color = color;
}// 使用内部状态和外部状态绘制圆形
@Override
public void draw(String color) {
System.out.println("Drawing Circle[" + color + ":" + this.color + "]");
}// 获取圆形颜色(用于演示)
public String getColor() {
return color;
}
}
注意:上面的draw
方法实际上接受了一个外部状态(即传递给方法的color
参数),但在这个例子中,我们主要关注内部状态(即对象创建时设置的color
)。在实际应用中,你可能需要设计接口和方法,以便更好地分离内部状态和外部状态。
接下来,定义享元工厂类,用于管理共享对象:
// 享元工厂类
class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap<>();// 获取圆形实例,如果已存在则直接返回,否则创建新的实例
public static Shape getCircle(String color) {
Circle circle = (Circle) circleMap.get(color);
if (circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating Circle of color : " + color);
}
return circle;
}
}
最后,客户端代码使用享元工厂来获取圆形实例并绘制它们:
public class FlyweightPatternDemo {
private static final String COLORS[] = { "Red", "Green", "Blue", "White", "Black" };public static void main(String[] args) {
for (int i = 0; i < 20; ++i) {
Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
circle.draw(getRandomColor()); // 注意:这里传递的color是外部状态,仅用于演示
}
}// 简单的随机数生成器,用于从预定义的颜色数组中获取颜色
private static String getRandomColor() {
return COLORS[(int) (Math.random() * COLORS.length)];
}
}
注意:在这个例子中,draw
方法同时接收了内部状态和外部状态作为参数,这实际上并不是享元模式的最佳实践。为了更清晰地展示享元模式,你应该设计接口和方法,使得内部状态(如圆形的颜色)在对象创建时确定,并通过不同的方法或参数来影响外部状态(如绘制时的位置、大小等)。然而,为了简化示例,这里将两者都作为参数传递给了draw
方法。
在实际应用中,你应该根据具体需求来设计享元类、工厂类和客户端代码,以确保内部状态被正确共享,并且外部状态能够灵活地影响对象的行为。
12.代理模式(Proxy)
代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用,并且可以通过代理对象来添加一些额外的功能,如权限控制、日志记录、缓存等。
在Java中,代理模式可以通过静态代理和动态代理两种方式实现。静态代理是在编译时就已经确定代理类的类,而动态代理是在运行时动态地生成代理类。这里我将分别给出静态代理和动态代理(使用JDK动态代理)的示例。
静态代理示例
首先,定义一个接口和该接口的实现类(即目标对象):
// 接口
interface Image {
void display();
}// 目标对象
class RealImage implements Image {
private String filename;public RealImage(String filename) {
this.filename = filename;
loadFromDisk(filename);
}// 模拟从磁盘加载图片
private void loadFromDisk(String filename) {
System.out.println("Loading " + filename);
}@Override
public void display() {
System.out.println("Displaying " + 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 ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");// 图片将从硬盘加载,但只在第一次调用display时
image.display(); // 输出:Loading test.jpg 和 Displaying test.jpg// 再次调用display时,图片已加载,因此不会再次从硬盘加载
image.display(); // 仅输出:Displaying test.jpg
}
}
动态代理示例(使用JDK动态代理)
首先,需要实现一个InvocationHandler接口,该接口定义了一个invoke方法,用于在代理对象调用方法时被调用。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 目标接口
interface Subject {
void request();
}// 目标对象
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("Handling request.");
}
}// 动态代理处理器
class DynamicProxyHandler implements InvocationHandler {
private Object subject;public DynamicProxyHandler(Object subject) {
this.subject = subject;
}@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(subject, args);
System.out.println("After method: " + method.getName());
return result;
}
}// 客户端代码
public class DynamicProxyPatternDemo {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
InvocationHandler handler = new DynamicProxyHandler(realSubject);// 创建代理对象
Subject subject = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler
);// 调用代理对象的方法
subject.request();
}
}
在这个动态代理示例中,我们创建了一个RealSubject
类作为目标对象,它实现了Subject
接口。然后,我们定义了一个DynamicProxyHandler
类,它实现了InvocationHandler
接口,用于在代理对象调用方法时添加额外逻辑。最后,我们使用Proxy.newProxyInstance
方法动态地创建了一个Subject
接口的代理对象,并在客户端代码中通过该代理对象调用request
方法。
13.策略模式(Strategy)
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列的算法,并将每一种算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。
在Java中,策略模式通常通过定义一系列的算法类(策略类),这些类都实现一个共同的接口。然后,客户端代码可以根据需要选择使用哪一个算法类。
下面是一个Java代码示例,展示了策略模式的应用。在这个例子中,我们将实现一个简单的排序算法选择,包括快速排序和冒泡排序两种策略。
首先,定义排序策略的接口:
// 排序策略接口
public interface SortStrategy {
void sort(int[] array);
}
然后,实现具体的排序算法(策略类):
// 快速排序策略
public class QuickSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
// 这里只是简单模拟,实际应实现快速排序算法
System.out.println("Using Quick Sort");
// ... 实现快速排序算法
}
}// 冒泡排序策略
public class BubbleSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
// 这里只是简单模拟,实际应实现冒泡排序算法
System.out.println("Using Bubble Sort");
// ... 实现冒泡排序算法
}
}
接下来,定义一个上下文类(Context),它接受客户的请求,然后委托给一个策略对象来执行具体的算法:
// 排序上下文
public class SortContext {
private SortStrategy strategy;public SortContext(SortStrategy strategy) {
this.strategy = strategy;
}// 设置排序策略
public void setSortStrategy(SortStrategy strategy) {
this.strategy = strategy;
}// 对数组进行排序
public void sortArray(int[] array) {
strategy.sort(array);
}
}
最后,客户端代码根据需要选择合适的排序策略:
public class StrategyPatternDemo {
public static void main(String[] args) {
int[] numbers = {5, 3, 8, 4, 2};// 使用快速排序策略
SortContext context = new SortContext(new QuickSortStrategy());
context.sortArray(numbers);// 假设后来决定使用冒泡排序策略
context.setSortStrategy(new BubbleSortStrategy());
context.sortArray(numbers);
}
}
注意:上面的sort
方法只是简单地打印出了使用的排序算法名称,并没有真正实现排序算法。在实际应用中,你应该在每个策略类中实现具体的排序算法。
策略模式的主要优点包括:
-
算法可以自由切换。
-
避免使用多重条件判断。
-
扩展性良好。如果增加一个新的排序算法,只需要增加一个实现了
SortStrategy
接口的策略类即可。
14.模板方法模式(Template Method)
模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
在Java中,模板方法模式通常通过定义一个抽象类来实现,这个抽象类中定义了一个或多个抽象方法(即算法的某些步骤),以及一个模板方法(即算法的骨架)。子类通过继承这个抽象类,并实现其中的抽象方法来完成算法的具体步骤。
下面是一个Java代码示例,展示了模板方法模式的应用。在这个例子中,我们将实现一个简单的咖啡制作流程,包括冲泡(brew)和添加调料(addCondiments)两个步骤。冲泡步骤是固定的,但添加调料的步骤可以根据不同的咖啡类型而有所不同。
首先,定义一个抽象类CaffeineBeverage
,其中包含模板方法prepareRecipe
和抽象方法brew
、addCondiments
:
abstract class CaffeineBeverage {
// 模板方法
final void prepareRecipe() {//烧水
boilWater();//冲泡
brew();//倒入杯子
pourInCup();
if (customerWantsCondiments()) {//添加调料
addCondiments();
}
}// 各个步骤的具体实现(除了brew和addCondiments)
void boilWater() {
System.out.println("Boiling water");
}abstract void brew();
void pourInCup() {
System.out.println("Pouring into cup");
}boolean customerWantsCondiments() {
return true;
}abstract void addCondiments();
}
然后,创建具体类Espresso
和HouseBlend
来继承CaffeineBeverage
,并实现brew
和addCondiments
方法:
class Espresso extends CaffeineBeverage {
@Override
void brew() {
System.out.println("在细粒咖啡中滴入浓缩咖啡");
}@Override
void addCondiments() {
System.out.println("添加浓缩咖啡调味品");
}
}class HouseBlend extends CaffeineBeverage {
@Override
void brew() {
System.out.println("浸泡自制混合咖啡");
}@Override
void addCondiments() {
System.out.println("添加自制混合调味品:牛奶和糖");
}@Override
boolean customerWantsCondiments() {
// 假设顾客总是想要House Blend的调料
return true;
}
}
最后,客户端代码可以通过创建Espresso
或HouseBlend
对象,并调用其prepareRecipe
方法来准备咖啡:
public class TemplateMethodPatternDemo {
public static void main(String[] args) {
CaffeineBeverage espresso = new Espresso();
System.out.println("Making an espresso:");
espresso.prepareRecipe();CaffeineBeverage houseBlend = new HouseBlend();
System.out.println("\nMaking a house blend coffee:");
houseBlend.prepareRecipe();
}
}
输出将展示不同咖啡类型的制备过程,其中冲泡和倒入杯中的步骤是固定的,但添加调料的步骤会根据咖啡类型的不同而有所不同。
15.观察者模式(Observer)
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
在Java中,实现观察者模式通常涉及到定义主题(Subject)接口和观察者(Observer)接口,然后创建具体的主题类和观察者类。不过,从Java 1.0开始,Java的java.util
包中就提供了一个Observable
类和Observer
接口,但自Java 9起,Observable
类已被标记为过时(deprecated),推荐使用其他方式实现观察者模式,如使用Java的事件监听器机制或第三方库。
下面是一个简化的Java代码示例,不依赖于java.util.Observable
和java.util.Observer
,而是手动实现观察者模式:
首先,定义观察者接口:
import java.util.ArrayList;
import java.util.List;// 观察者接口
interface Observer {
void update(String message);
}// 具体的观察者类
class ConcreteObserver implements Observer {
private String name;public ConcreteObserver(String name) {
this.name = name;
}@Override
public void update(String message) {
System.out.println(name + " received: " + message);
}
}
然后,定义主题接口和具体的主题类:
// 主题接口(通常包含一个注册观察者和通知观察者的方法)
interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers(String message);
}// 具体的主题类
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();@Override
public void registerObserver(Observer o) {
if (!observers.contains(o)) {
observers.add(o);
}
}@Override
public void removeObserver(Observer o) {
observers.remove(o);
}@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}// 假设这是主题状态变化时调用的方法
public void someBusinessLogic() {
// ... 执行一些业务逻辑
System.out.println("Business logic executed.");
notifyObservers("State has changed!");
}
}
最后,客户端代码:
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();Observer observer1 = new ConcreteObserver("Observer 1");
Observer observer2 = new ConcreteObserver("Observer 2");
Observer observer3 = new ConcreteObserver("Observer 3");subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.registerObserver(observer3);// 改变主题状态,并通知所有观察者
subject.someBusinessLogic();// 移除一个观察者
subject.removeObserver(observer2);// 再次改变主题状态,并通知所有观察者(除了observer2)
subject.someBusinessLogic();
}
}
在这个例子中,ConcreteSubject
类充当了主题的角色,它维护了一个观察者列表,并在状态变化时通过调用notifyObservers
方法来通知所有注册的观察者。ConcreteObserver
类实现了Observer
接口,并在接收到通知时执行相应的操作。客户端代码通过创建主题和观察者对象,并将它们连接起来,展示了观察者模式的基本用法。
16.迭代器模式(Iterator)
迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种方法顺序访问一个聚合对象(如列表、集合)中的各个元素,而又不暴露该对象的内部表示。迭代器模式将遍历的职责从聚合对象转移到迭代器对象上,从而使聚合对象与遍历算法分离。
在Java中,迭代器模式已经被内置在java.util.Iterator
接口和java.util.Collection
接口的实现中(如List
、Set
等)。下面,我将提供一个简单的自定义迭代器模式的示例,以便更好地理解其工作原理。
首先,定义一个聚合接口(例如,一个自定义的集合):
interface MyCollection {
Iterator createIterator();
}
接下来,定义迭代器接口,它包含遍历集合所需的方法:
interface Iterator {
boolean hasNext();
Object next();
}
然后,实现一个具体的聚合类(比如一个简单的数字列表):
import java.util.ArrayList;
import java.util.List;class NumberList implements MyCollection {
private List<Integer> numbers = new ArrayList<>();public void add(int number) {
numbers.add(number);
}@Override
public Iterator createIterator() {
return new NumberListIterator(this);
}// Getter 用于迭代器内部访问
public List<Integer> getNumbers() {
return numbers;
}
}
实现具体的迭代器类,它持有对聚合对象的引用,并遍历聚合对象中的元素:
class NumberListIterator implements Iterator {
private NumberList numberList;
private int currentIndex = 0;public NumberListIterator(NumberList numberList) {
this.numberList = numberList;
}@Override
public boolean hasNext() {
return currentIndex < numberList.getNumbers().size();
}@Override
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return numberList.getNumbers().get(currentIndex++);
}
}
最后,客户端代码使用迭代器遍历集合:
public class IteratorPatternDemo {
public static void main(String[] args) {
NumberList numberList = new NumberList();
numberList.add(1);
numberList.add(2);
numberList.add(3);Iterator iterator = numberList.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
在这个例子中,NumberList
是聚合对象,它维护了一个整数列表,并提供了创建迭代器的方法。NumberListIterator
是迭代器对象,它实现了Iterator
接口,并能够遍历NumberList
中的元素。客户端代码通过调用NumberList
的createIterator
方法来获取迭代器,并使用迭代器遍历集合。
注意,这个示例是为了说明迭代器模式的工作原理而简化的。在实际应用中,Java集合框架已经提供了丰富的迭代器实现,你不需要从头开始实现它们。
17.责任链模式(Chain of Responsibility)
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它为请求的发送者和接收者之间解耦提供了一种松耦合的方式,使得多个对象都有机会处理这个请求,或者将这个请求传递给链中的下一个对象,直到有一个对象处理它为止。
在Java中,实现责任链模式通常需要定义一个抽象的处理者(Handler)类,它包含一个指向下一个处理者的引用和一个处理请求的接口方法。具体的处理者类将继承这个抽象类并实现处理请求的方法。
下面是一个简单的Java代码示例,展示了责任链模式的应用。在这个例子中,我们将实现一个简单的日志处理系统,其中日志消息可以根据其优先级(如DEBUG、INFO、ERROR)被不同的处理器处理。
首先,定义日志级别和抽象的处理者接口:
// 日志级别枚举
public enum LogLevel {
DEBUG, INFO, ERROR
}// 抽象处理者
abstract class Logger {
protected int level;
protected Logger nextLogger;public void setNextLogger(Logger nextLogger) {
this.nextLogger = nextLogger;
}// 抽象方法,用于处理日志
public void write(LogLevel level, String message) {
if (this.level <= level.ordinal()) {
writeMessage(message);
}
if (nextLogger != null) {
nextLogger.write(level, message);
}
}// 具体写日志的方法,由子类实现
protected abstract void writeMessage(String message);
}
然后,定义具体的处理者类:
// 具体处理者:ConsoleLogger,只记录ERROR级别的日志
class ConsoleLogger extends Logger {
public ConsoleLogger(int level) {
this.level = level;
}@Override
protected void writeMessage(String message) {
System.out.println("Console: " + message);
}
}// 具体处理者:FileLogger,记录ERROR和INFO级别的日志
class FileLogger extends Logger {
private String fileName;public FileLogger(int level, String fileName) {
this.level = level;
this.fileName = fileName;
}@Override
protected void writeMessage(String message) {
// 假设有一个方法可以将日志写入文件
System.out.println("File: " + fileName + " - " + message);
}
}// 具体处理者:ErrorLogger,只记录ERROR级别的日志到特定文件
class ErrorLogger extends Logger {
private String errorFileName;public ErrorLogger(int level, String errorFileName) {
this.level = level;
this.errorFileName = errorFileName;
}@Override
protected void writeMessage(String message) {
// 假设有一个方法可以将错误日志写入特定文件
System.out.println("ErrorFile: " + errorFileName + " - " + message);
}
}
最后,客户端代码使用这些处理者构建责任链并发送日志消息:
public class ChainPatternDemo {
private static Logger getChainOfLoggers() {
// 创建链中的各个日志记录器
Logger errorLogger = new ErrorLogger(LogLevel.ERROR.ordinal(), "errors.log");Logger fileLogger = new FileLogger(LogLevel.INFO.ordinal(), "logs.log");
Logger consoleLogger = new ConsoleLogger(LogLevel.DEBUG.ordinal());
// 设置链的顺序
errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);return errorLogger;
}public static void main(String[] args) {
Logger loggerChain = getChainOfLoggers();loggerChain.write(LogLevel.ERROR, "This is an error message.");
loggerChain.write(LogLevel.INFO, "This is an information.");
loggerChain.write(LogLevel.DEBUG, "This is a debug level information.");
}
}
在这个例子中,我们创建了一个日志处理的责任链,其中ErrorLogger
只处理ERROR级别的日志,FileLogger
处理ERROR和INFO级别的日志,而ConsoleLogger
则处理所有级别的日志。通过调用write
方法并传递日志级别和消息,我们可以将消息沿着链传递,直到它被适当的处理者处理。
18.命令模式(Command)
在命令模式中,我们有一个Command
接口,它定义了执行操作的接口。然后,具体命令类实现了这个接口,并关联到一个接收者(Receiver)对象上,调用接收者相应的操作。调用者(Invoker)通过命令对象来执行操作,而不需要直接调用接收者的操作。
// Command接口
interface Command {
void execute();
}// 接收者
class Receiver {
public void action() {
System.out.println("Action performed by Receiver");
}
}// 具体命令类
class ConcreteCommand implements Command {
private Receiver receiver;public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}@Override
public void execute() {
receiver.action();
}
}// 调用者
class Invoker {
private Command command;public Invoker(Command command) {
this.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(command);
invoker.executeCommand(); // 输出: Action performed by Receiver// 可以在不修改Invoker类的前提下更换命令
Command anotherCommand = new AnotherConcreteCommand(); // 假设AnotherConcreteCommand是另一个实现了Command的类
invoker.setCommand(anotherCommand);
invoker.executeCommand(); // 执行AnotherConcreteCommand的execute方法
}
}// 假设的AnotherConcreteCommand类
class AnotherConcreteCommand implements Command {
@Override
public void execute() {
System.out.println("Executing another action");
}
}
请注意,由于问题中并未要求实现AnotherConcreteCommand
,我为了展示命令模式中的灵活性而假设了其存在。在实际应用中,你可以根据需要创建多个具体命令类。
这个示例展示了命令模式的核心思想:将请求封装为对象,从而使你可用不同的请求、队列、日志来参数化其他对象。命令模式也支持可撤销的操作。
19.备忘录模式(Memento)
在Java中,备忘录模式(Memento Pattern)是一种行为设计模式,它允许在不暴露对象内部状态的情况下,捕获并保存一个对象的内部状态,以便在将来某个时刻可以恢复到这个状态。备忘录模式通常涉及三个角色:发起人(Originator)、备忘录(Memento)和负责人(Caretaker)。
// 备忘录接口
interface Memento {
// 这里可以根据需要添加方法来访问备忘录中的状态
}// 具体备忘录类,保存发起人的状态
class OriginatorState implements Memento {
// 假设发起人有一个状态是字符串
private String state;public OriginatorState(String state) {
this.state = state;
}// 可能需要的方法来恢复状态(虽然在这个简单的示例中未直接使用)
public String getState() {
return state;
}
}// 发起人类
class Originator {
private String state;// 创建一个备忘录,保存当前状态
public Memento createMemento() {
return new OriginatorState(state);
}// 从备忘录恢复状态
public void restoreMemento(Memento memento) {
OriginatorState os = (OriginatorState) memento;
this.state = os.getState();
}// 更改状态的方法
public void setState(String state) {
this.state = state;
}// 获取当前状态的方法(为了演示)
public String getState() {
return state;
}
}// 负责人,负责保存和恢复备忘录
class Caretaker {
private Memento memento;// 设置备忘录
public void setMemento(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");
caretaker.setMemento(originator.createMemento());originator.setState("State #2");
System.out.println("Current State: " + originator.getState()); // 输出: State #2// 从备忘录恢复状态
originator.restoreMemento(caretaker.getMemento());
System.out.println("Restored State: " + originator.getState()); // 输出: State #1
}
}
在这个示例中,Originator
类有一个状态(在这个例子中是一个字符串),它可以通过 createMemento
方法来创建一个包含当前状态的备忘录对象。这个备忘录对象是一个 OriginatorState
类的实例,它实现了 Memento
接口。Caretaker
类负责保存备忘录对象,并提供方法来获取和设置备忘录。在客户端代码中,我们演示了如何保存一个状态,更改状态,然后从保存的备忘录中恢复状态。
注意,这个示例中的备忘录模式是非常基本的,实际应用中可能需要更复杂的状态管理逻辑。
相关文章:
java开发设计模式详解
目录 一、概述 1. 创建型模式(5种) 2. 结构型模式(7种) 3. 行为型模式(11种) 二、代码示例说明 1.单例模式(Singleton) 2.工厂方法模式(Factory Method) 3.抽象工厂模式(Abstract Factory) 4.建造者模式(Builder) 5.原型模式 (Prototype) 6.适…...
windows中node版本的切换(nvm管理工具),解决项目兼容问题 node版本管理、国内npm源镜像切换(保姆级教程,值得收藏)
前言 在工作中,我们可能同时在进行2个或者多个不同的项目开发,每个项目的需求不同,进而不同项目必须依赖不同版本的NodeJS运行环境,这种情况下,对于维护多个版本的node将会是一件非常麻烦的事情,nvm就是为…...
测试面试宝典(四十四)—— APP测试和web测试有什么区别?
一、系统架构和运行环境 APP 测试需要考虑不同的操作系统(如 iOS、Android 等)、设备型号和屏幕尺寸,以及各种网络连接状态(如 2G、3G、4G、WiFi 等)。而 Web 测试主要针对不同的浏览器(如 Chrome、Firefo…...
力扣高频SQL 50题(基础版)第三十七题
文章目录 力扣高频SQL 50题(基础版)第三十七题176.第二高的薪水题目说明实现过程准备数据实现方式结果截图总结 力扣高频SQL 50题(基础版)第三十七题 176.第二高的薪水 题目说明 Employee 表: ----------------- …...
web基础之CSS
web基础之CSS 文章目录 web基础之CSS一、CSS简介二、基本用法2、CSS应用方式2.1 行内样式2.2内部样式2.3外部样式 三、选择器1、标签选择器2、类选择器3、ID选择器4、选择器的优先级 四、常见的CSS属性1、字体属性2、文本属性3、背景属性4、表格属性5、盒子模型的属性6、定位 总…...
全球轻型卡车胎市场规划预测:2030年市场规模将接近1153亿元,未来六年CAGR为2.0%
一、引言 随着全球物流行业的持续发展,轻型卡车胎作为物流运输的关键消耗品,其市场重要性日益凸显。本文旨在探索轻型卡车胎行业的发展趋势、潜在商机及其未来展望。 二、市场趋势 全球轻型卡车胎市场的增长主要受全球物流行业增加、消费者对轮胎性能要…...
8.2 数据结构王道复习 2.3.3 2.3.7选择题错题review
王道中这章主讲了线性表的定义、基本操作、顺序表示、链式表示。下方内容主分了文字部分和代码部分,便于记忆和整理。 在901中这章的要求集中在链表的基础操作中,应用题大概会出问答题。 【当前每一小节的应用题待做,先把选择题过完ÿ…...
【DL】神经网络与机器学习基础知识介绍(二)【附程序】
原文:https://mengwoods.github.io/post/dl/009-dl-fundamental-2/ 文章目录 激活函数卷积神经网络超参数其他程序 激活函数 激活函数的目的是在模型中引入非线性,使网络能够学习和表示数据中的复杂模式。列出常见的激活函数。 线性函数: y…...
6万字嵌入式最全八股文面试题大全及参考答案(持续更新)
目录 冒泡排序算法的平均时间复杂度和最坏时间复杂度分别是多少?在什么情况下使用冒泡排序较为合适? 选择排序算法是稳定的排序算法吗?为什么? 插入排序在近乎有序的数组中表现如何?为什么? 快速排序的基本思想是什么?它在最坏情况下的时间复杂度是多少? 归并排序…...
iceberg 用户文档(持续更新)
iceberg 用户文档 表 Schema 变更查看表的元数据信息表参数变更 表 Schema 变更 Iceberg 支持使用 Alter table … alter column 语法对 Schema 进行变更,示例如下 -- spark sql -- 更改字段类型 ALTER TABLE prod.db.sample ALTER COLUMN measurement TYPE doubl…...
基于YOLOv8的船舶检测系统
基于YOLOv8的船舶检测系统 (价格85) 包含 【散货船,集装箱船,渔船,杂货船,矿砂船,客船】 6个类 通过PYQT构建UI界面,包含图片检测,视频检测,摄像头实时检测。 (该…...
使用腾讯云域名解析实现网站重定向
前言 最近,在CSDN平台上我写了一系列博客,希望能与同学分享一些技术心得。然而,每当需要向他人推荐我的博客时,那串复杂且缺乏规律的CSDN博客首页域名总让我感到不便。这让我开始思考,如果能将这一域名替换为一个既个…...
为什么相比直接使用new和std::shared_ptr构造函数,make_shared在内存分配和管理方面更为高效。
使用std::make_shared相比于直接使用new和std::shared_ptr构造函数在内存分配和管理方面更为高效,主要原因如下: 内存分配效率 std::make_shared通过一次内存分配来同时分配控制块(用于引用计数等)和对象的内存。这种方式减少了…...
7-Python数据类型——列表和元组的详解(增删改查、索引、切片、步长、循环)
一、列表 1.1 列表 list 有序且可变的容器,可以存放多个不同类型的元素 列表就是专门用来记录多个同种属性的值 列表:存储同一个类别的数据,方便操作 字符串,不可变:即:创建好之后内部就无法修改【内置…...
大数据-61 Kafka 高级特性 消息消费02-主题与分区 自定义反序列化 拦截器 位移提交 位移管理 重平衡
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…...
Google Gemma2 2B:语言模型的“小时代”到来?
北京时间8月1日凌晨(当地时间7月31日下午),Google发布了其Gemma系列开源语言模型的更新,在AI领域引发了巨大的震动。Google Developer的官方博客宣布,与6月发布的27B和9B参数版本相比,新的2B参数模型在保持…...
三线程顺序打印1-100
三线程顺序打印1-100 题目 三个线程顺序打印1-100; 解题 基本思路 首先需要创建三个线程, 确定使用线程1打印 num % 3 1 的数, 线程2打印 num % 3 2 的数, 线程3打印 num % 3 0 的数;使用 synchronized 同步锁让每次只有一个线程进行打印, 每个线程打印前先判断当前数是…...
中央处理器CPU
中央处理器CPU cpu的组成(从功能方面来看)cpu的执行过程★.取指令阶段★.解码阶段★.执行阶段 重点: 1.cpu的组成 2.cpu怎么执行程序(命令) cpu的组成(从功能方面来看) 寄存器:用来临…...
用Python实现AI人脸识别
实现AI人脸识别通常涉及到使用深度学习库,如TensorFlow或PyTorch,配合预训练的人脸识别模型。以下是一个使用Python和TensorFlow框架中的tensorflow_hub模块来加载和使用一个预训练的人脸识别模型的简单示例。 步骤 1: 安装必要的库 首先,你…...
MSPM0G3507_2024电赛自动行驶小车(H题)_问题与感悟
这次电赛题目选的简单了,还规定不能使用到摄像头,这让我之前学习的Opencv 4与树莓派无用武之地了,但我当时对于三子棋题目饶有兴趣,但架不住队友想稳奖,只能选择这个H题了...... 之后我还想抽空将这个E题三子棋题目做…...
C语言:指针(2)
一.数组名 在了解数组名前我们先看一段代码 int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[0]; 根据我们上一篇学习的知识,我们知道&arr[0]是数组第一个元素的地址,这时我们再看另一段代码的运行结果。 #include <stdio.h> int ma…...
数组——二维数组
数组(中) 二维数组 定义 二维数组本质上是一个行列式的组合,也就是说二维数组是有行和列两部分构成。二维数组数据是通过行列进行解读。 二维数组可被视为一个特殊的一维数组,相当于二维数组又是一个一维数组,只不过它的元素是一维数组。 …...
深入 Vue 组件与状态管理的教程
目录 深入 Vue 组件与状态管理的教程第一部分:深入组件1. 理解插槽(Slots)的使用1.1 基础插槽示例1.2 具名插槽1.3 作用域插槽 第二部分:Vue Router1. 学习 Vue Router 的基本配置1.1 基本路由配置1.2 嵌套路由1.3 路由参数 2. 导…...
Spring Boot 实现异步处理多个并行任务
在现代Web应用开发中,异步处理和多任务并行处理对于提高系统的响应性和吞吐量至关重要。Spring Boot 提供了多种机制来实现异步任务处理,本文将介绍如何利用这些机制来优化您的应用程序性能。 1. 引言 在高负载情况下,如果所有的请求都采用…...
TiDB系列之:使用Flink TiDB CDC Connector采集数据
TiDB系列之:使用Flink TiDB CDC Connector采集数据 一、依赖项二、Maven依赖三、SQL Client JAR四、如何创建 TiDB CDC 表五、连接器选项六、可用元数据七、特征一次性处理启动阅读位置多线程读取DataStream Source 八、数据类型映射 TiDB CDC 连接器允许从 TiDB 数…...
每日一道算法题 最接近的三数之和
题目 16. 最接近的三数之和 - 力扣(LeetCode) Python class Solution:def threeSumClosest(self, nums: List[int], target: int) -> int:nums.sort()nlen(nums)ans0min_diffinf # infinite 无穷for i in range(n-2):tmpnums[i]li1rn-1while l<…...
搭建自己的金融数据源和量化分析平台(六):下载并存储沪深两市上市公司财报
基于不依赖wind、某花顺等第三方平台数据的考虑,尝试直接从财报中解析三大报表进而计算ROE等财务指标,因此需要下载沪深两市的上市公司财报数据,便于后续从pdf中解析三大报表。 深市爬虫好做,先放深市爬虫: 根据时间段…...
C语言-常见关键字详解
一、const 关键字const用于声明常量,赋值后,其值不能再被修改。 示例: const int MAX_COUNT 100; 二、static static关键字在不同情境下有不同作用: 1.函数中的静态变量:保留变量状态,仅初始化一次&a…...
异步编程之std::future(一): 使用
目录 1.概述 2.std::future的基本用法 3.使用 std::shared_future 4.std::future的使用场景 5.总结 1.概述 在编程实践中,我们常常需要使用异步调用。通过异步调用,我们可以将一些耗时、阻塞的任务交给其他线程来执行,从而保证当前线程的…...
Vue3 + JS项目配置ESLint Pretter
前言 如果在开发大型项目 同时为多人协作开发 那么 ESLint 在项目中极为重要 在使用 ESLint 的同时 也需要使用 Pretter插件 统一对代码进行格式化 二者相辅相成 缺一不可 1. 安装 VsCode 插件 在 VsCode 插件市场搜索安装 ESLint 和 Pretter 2. 安装依赖 这里直接在 pac…...
廊坊开发区规划建设局网站/1000个关键词
做一件事,如果觉得难那就对了。容易达成的事,没有经过努力获取到的是没有成就感的。人生就像上坡,更高处的风景更值得期待。中国人民大学与加拿大女王大学金融硕士项目给予你前行的力量。 我们每个人的一生中,都是在不断攀登一座…...
做家装网站源码/网络销售就是忽悠人
问题描述先看下后台返回的Set-Cookie字段:查看浏览器Cookies:思路发现只有JESSIONID存入到了浏览器Storage中的Cookies。通过比较 Response Headers 中两个 set-cookie字段可以发现字段不同:JSESSIONID:path/ZTEV-JWT-Token&#…...
WordPress企业显示/江苏seo和网络推广
"""1.三级菜单 注册 登陆 注销2.进入每一个一级菜单,都会有下一级的菜单"""user_item dict()try:while True:print(-------Welcome sir-------)input_choice int(input(Please enter your choice:1:Registration 2:login 3:logout:…...
政府网站wap门户建设方案/bt种子万能搜索神器
cmd命令 1.system 导出全部表结构和数据 注意 :1.导出服务器的地址 192.168.1.252:1521/orcl 2. 登录名 ownerorcl (当前用户的登录名) 3. 导出文件的位置 fileD:\oracle\zkgs 4. 导出日志文件的位置 logD:\oracle\zkgs exp system/format192.168.1.252:1521/…...
网站备案完成通知书/企业网站建设需求分析
react-native之黄色警告Remote debugger is in a background tab which may cause apps to perform slowly. Fix this by foregrounding the tab (or opening it in a separate window).百度了一圈也没找到问题,感觉有点摸不着头脑 原来,只要把那个chrom…...
如何用普通电脑做网站服务器/关键词歌曲歌词
让城市变成生态公园—新型生态别墅设计 梦想家园-生态小屋 前言:上海世博会的主题是“城市让生活更美好”,是的,城市的确可以让生活更美好,关键是我们要去建设美好的城市。我觉得城市可以变得更美好。城市应该是一个巨大的生态公园…...