赞
踩
目录
17.责任链模式(Chain of Responsibility)
Java开发中的设计模式是软件工程中用于解决常见问题的一系列最佳实践。这些设计模式并不是Java特有的,但它们与Java等面向对象编程语言结合得非常好,因为设计模式本质上就是面向对象设计原则的具体化。
设计模式主要分为三大类:创建型模式、结构型模式和行为型模式。下面简要介绍每一类中的一些常见设计模式及其在Java开发中的应用。
创建型模式主要关注对象的创建过程。
单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。常用于配置例如文件的读取、数据库连接等。
工厂方法模式(Factory Method):定义一个用于创建对象的接口,但让子类决定要实例化哪个类。工厂方法让类的实例化推迟到子类中进行。
抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
原型模式(Prototype):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
结构型模式关注类和对象的组合。
适配器模式(Adapter):将一个类的接口转换成客户端所期待的另一种接口形式,使接口不兼容的类可以一起工作。
桥接模式(Bridge):将抽象部分与实现部分分离,使它们都可以独立地变化。
组合模式(Composite):将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
装饰器模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更为灵活。
外观模式(Facade):为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
享元模式(Flyweight):运用共享技术有效地支持大量细粒度对象的复用。
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。
行为型模式关注对象之间的通信和职责分配。
策略模式(Strategy):定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。
模板方法模式(Template Method):定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中实现。
观察者模式(Observer):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
责任链模式(Chain of Responsibility):为请求的发送者和接收者之间解耦,使多个对象都有机会处理这个请求,或者将这个请求传递到链上的下一个对象。
命令模式(Command):将一个请求封装为一个对象,从而使我们可用不同的请求、队列、日志来参数化其他对象。
备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
状态模式(State):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
中介者模式(Mediator):用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
解释器模式(Interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
这些设计模式在Java开发中有着广泛的应用,它们可以帮助开发者设计出更加灵活、可扩展和易于维护的软件系统。
懒汉式(线程安全):通过在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()
时都进行同步,影响性能。而双重检查锁定方式既保证了线程安全,又避免了不必要的同步开销。
工厂方法模式是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。
// 定义一个产品接口
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()
方法。这样就实现了在运行时根据不同的工厂类来创建不同类型的产品对象,这就是工厂方法模式的核心思想。
在抽象工厂模式中,我们定义一个抽象工厂接口,用于创建相关或依赖对象的家族,但不指定具体类。每个生成的工厂类能够返回该工厂所创建的对象类型的具体实例。
// 定义一个产品接口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
接口,并分别返回不同类型的产品实例
建造者模式(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
对象。
在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):
适配器模式(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
接口的要求,从而可以在老式的插座上使用。这是适配器模式的一个典型应用场景。
桥接模式(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
类型的引用,这允许我们在运行时将形状与不同的颜色组合起来,而不需要为每种颜色和形状的组合创建一个新的类。
通过桥接模式,我们实现了抽象与实现的解耦,使得系统的扩展变得更加容易。例如,如果我们想要添加新的颜色或新的形状,我们只需要创建新的颜色实现或形状类,而不需要修改现有的代码。
组合模式(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()
方法,可以递归地处理整个文件系统中的所有文件和文件夹。
装饰器模式(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装饰器来动态地添加调料和价格。这种方式使得代码更加灵活和可扩展,因为它允许我们在不修改现有类的情况下,通过添加新的装饰器来添加新的功能。
外观模式为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
假设我们有一个系统,该系统用于处理文件的各种操作,比如读取、写入和删除文件。这些操作分别由不同的类处理。为了简化对这些操作的调用,我们可以使用外观模式来提供一个统一的接口。
// 文件读取类
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
交互,这降低了系统的耦合度,也简化了客户端的使用。
享元模式(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
方法。
在实际应用中,你应该根据具体需求来设计享元类、工厂类和客户端代码,以确保内部状态被正确共享,并且外部状态能够灵活地影响对象的行为。
代理模式(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
方法。
策略模式(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
接口的策略类即可。
模板方法模式(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();
}
}
输出将展示不同咖啡类型的制备过程,其中冲泡和倒入杯中的步骤是固定的,但添加调料的步骤会根据咖啡类型的不同而有所不同。
观察者模式(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
接口,并在接收到通知时执行相应的操作。客户端代码通过创建主题和观察者对象,并将它们连接起来,展示了观察者模式的基本用法。
迭代器模式(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集合框架已经提供了丰富的迭代器实现,你不需要从头开始实现它们。
责任链模式(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
方法并传递日志级别和消息,我们可以将消息沿着链传递,直到它被适当的处理者处理。
在命令模式中,我们有一个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
,我为了展示命令模式中的灵活性而假设了其存在。在实际应用中,你可以根据需要创建多个具体命令类。
这个示例展示了命令模式的核心思想:将请求封装为对象,从而使你可用不同的请求、队列、日志来参数化其他对象。命令模式也支持可撤销的操作。
在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
类负责保存备忘录对象,并提供方法来获取和设置备忘录。在客户端代码中,我们演示了如何保存一个状态,更改状态,然后从保存的备忘录中恢复状态。
注意,这个示例中的备忘录模式是非常基本的,实际应用中可能需要更复杂的状态管理逻辑。
状态模式(State Pattern)是一种行为设计模式,它允许一个对象在其内部状态改变时改变它的行为,这个对象看起来好像修改了它的类。状态模式通常用于解决当一个对象的行为取决于它的状态时,并且这些状态又很多或者状态之间可能相互转换的情况。
下面是一个使用Java实现状态模式的简单示例。假设我们有一个交通信号灯系统,它有三种状态:红灯(Red)、黄灯(Yellow)和绿灯(Green)。每种状态都有特定的行为(即亮灯)和可能的转换(例如,从红灯到绿灯)。
首先,我们定义一个状态接口和具体的状态类(红灯、黄灯、绿灯)
// 状态接口
interface TrafficLightState {
void change(TrafficLight trafficLight);
}// 红灯状态
class RedLight implements TrafficLightState {
@Override
public void change(TrafficLight trafficLight) {
System.out.println("红灯亮,请停车");
// 假设红灯后是黄灯
trafficLight.setState(new YellowLight());
}
}// 黄灯状态
class YellowLight implements TrafficLightState {
@Override
public void change(TrafficLight trafficLight) {
System.out.println("黄灯亮,请准备");
// 假设黄灯后是绿灯
trafficLight.setState(new GreenLight());
}
}// 绿灯状态
class GreenLight implements TrafficLightState {
@Override
public void change(TrafficLight trafficLight) {
System.out.println("绿灯亮,请通行");
// 假设绿灯后又是红灯
trafficLight.setState(new RedLight());
}
}
然后,我们定义交通信号灯类,它包含当前状态和一个设置新状态的方法
class TrafficLight {
private TrafficLightState state;public TrafficLight() {
// 初始状态为红灯
this.state = new RedLight();
}public void setState(TrafficLightState state) {
this.state = state;
}// 外界触发状态变化
public void change() {
state.change(this);
}
}
最后,我们创建一个测试类来模拟交通信号灯的状态变化
public class TrafficLightTest {
public static void main(String[] args) {
TrafficLight trafficLight = new TrafficLight();// 模拟信号灯变化
for (int i = 0; i < 10; i++) {
trafficLight.change();
}
}
}
在这个例子中,我们创建了一个TrafficLight
对象,它有一个初始状态RedLight
。每次调用change()
方法时,当前状态对象都会执行其change()
方法,该方法不仅描述了当前状态的行为(打印信息),还决定了下一个状态是什么(通过调用setState()
方法)。这样,TrafficLight
对象就能够在不同的状态之间切换,而无需修改它的类。
访问者模式(Visitor Pattern)是一种将算法与它的操作对象分离出来的设计模式。这样可以在不修改对象结构的前提下增加新的操作。访问者模式适用于数据结构相对稳定,但作用于数据结构的操作经常改变的场景。
在Java中,实现访问者模式通常涉及四个角色:
访问者(Visitor):接口声明了一个或多个访问操作,形成对元素对象结构中各元素的操作。
具体访问者(ConcreteVisitor):实现访问者接口,针对数据结构中的具体元素实现其访问操作。
元素(Element):定义一个接受访问者的接口,这个接口通常包含一个或多个接受访问者对象的操作。
具体元素(ConcreteElement):实现了Element接口,存储或表示数据结构中的具体元素,并接受访问者的访问。
下面是一个简单的Java示例,说明如何使用访问者模式:
// 访问者接口
interface Visitor {
void visit(ConcreteElement1 element);
void visit(ConcreteElement2 element);
}// 具体访问者
class ConcreteVisitor1 implements Visitor {
@Override
public void visit(ConcreteElement1 element) {
System.out.println("Visiting ConcreteElement1: " + element.getData());
}@Override
public void visit(ConcreteElement2 element) {
System.out.println("Visiting ConcreteElement2: " + element.getData());
}
}// 元素接口
interface Element {
void accept(Visitor visitor);
}// 具体元素1
class ConcreteElement1 implements Element {
private String data;public ConcreteElement1(String data) {
this.data = data;
}public String getData() {
return data;
}@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}// 具体元素2
class ConcreteElement2 implements Element {
private String data;public ConcreteElement2(String data) {
this.data = data;
}public String getData() {
return data;
}@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}// 客户端代码
public class VisitorPatternDemo {
public static void main(String[] args) {
Element element1 = new ConcreteElement1("Element 1");
Element element2 = new ConcreteElement2("Element 2");Visitor visitor = new ConcreteVisitor1();
element1.accept(visitor);
element2.accept(visitor);
}
}
在这个示例中,我们定义了两个具体的元素类ConcreteElement1
和ConcreteElement2
,它们都实现了Element
接口。我们还定义了一个访问者接口Visitor
和一个具体的访问者类ConcreteVisitor1
。在客户端代码中,我们创建了两种类型的元素对象,并使用ConcreteVisitor1
来访问它们。通过accept
方法,元素对象接受访问者的访问,并调用访问者中对应的方法。这样,我们就实现了在数据结构不变的情况下,增加新的操作(即访问者)。
中介者模式(Mediator Pattern)是一种行为设计模式,用于减少对象之间的直接交互,从而降低它们之间的耦合度。在中介者模式中,一个中介对象来封装一系列的对象交互,使得各对象不需要显式地相互引用,从而使其可以独立地改变和复用。
下面是一个简单的Java代码示例,展示了中介者模式的应用。在这个例子中,我们将创建一个聊天室系统,其中有多名用户参与聊天。用户之间不直接通信,而是通过聊天室(即中介者)来交换信息。
首先,我们定义一个用户接口(ChatUser
),然后实现这个接口创建具体的用户类(User
)。
// 用户接口
interface ChatUser {
void sendMessage(String message);
void receiveMessage(String message, ChatUser sender);
void setMediator(ChatMediator mediator);
}// 用户类
class User implements ChatUser {
private String name;
private ChatMediator mediator;public User(String name) {
this.name = name;
}@Override
public void sendMessage(String message) {
mediator.sendMessage(this, message);
}@Override
public void receiveMessage(String message, ChatUser sender) {
System.out.println(sender.getName() + ": " + message);
}@Override
public void setMediator(ChatMediator mediator) {
this.mediator = mediator;
}public String getName() {
return name;
}
}
然后,我们定义中介者接口(ChatMediator
)和具体的中介者类(ChatRoom
)。
// 中介者接口
interface ChatMediator {
void register(ChatUser user);
void sendMessage(ChatUser user, String message);
}// 聊天室类,作为中介者
class ChatRoom implements ChatMediator {
private List<ChatUser> users = new ArrayList<>();@Override
public void register(ChatUser user) {
users.add(user);
user.setMediator(this);
}@Override
public void sendMessage(ChatUser user, String message) {
for (ChatUser u : users) {
if (u != user) {
u.receiveMessage(message, user);
}
}
}
}
最后,我们可以创建一个简单的测试类来演示中介者模式的工作方式。
public class MediatorPatternDemo {
public static void main(String[] args) {
ChatMediator chatRoom = new ChatRoom();ChatUser user1 = new User("Alice");
ChatUser user2 = new User("Bob");chatRoom.register(user1);
chatRoom.register(user2);user1.sendMessage("Hello Bob!");
user2.sendMessage("Hi Alice, how are you?");
}
}
在这个例子中,用户Alice和Bob通过聊天室(ChatRoom
)进行通信,而不是直接相互发送消息。聊天室作为中介者,负责在用户之间传递消息,从而降低了用户之间的耦合度。
解释器模式(Interpreter Pattern)是一种行为设计模式,它给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
假设我们有一个简单的表达式语言,它支持加、减和乘操作,并且我们想要通过解释器模式来解析和计算这些表达式。
下面是一个使用Java实现的解释器模式的简单示例:
// 抽象表达式类
interface Expression {
int interpret(Context context);
}// 终端表达式
class TerminalExpression implements Expression {
private String data;public TerminalExpression(String data) {
this.data = data;
}@Override
public int interpret(Context context) {
// 这里简单返回数字,实际应用中可能需要解析data
return Integer.parseInt(data);
}
}// 非终端表达式 - 加法
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(Context context) {
return left.interpret(context) + right.interpret(context);
}
}// 非终端表达式 - 减法
class SubtractExpression implements Expression {
private Expression left;
private Expression right;public SubtractExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}@Override
public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
}// 上下文环境类,这里暂不使用
class Context {
// 可以添加一些全局变量或方法等
}// 客户端代码
public class InterpreterPatternDemo {
public static void main(String[] args) {
Expression exp1 = new TerminalExpression("10");
Expression exp2 = new TerminalExpression("20");
Expression exp3 = new AddExpression(exp1, exp2);
Expression exp4 = new TerminalExpression("5");
Expression exp5 = new SubtractExpression(exp3, exp4);// 假设Context在这里没有实际作用
Context context = new Context();System.out.println(exp5.interpret(context)); // 输出 25
}
}
这个示例中,我们定义了抽象表达式Expression
接口,以及具体的终端表达式TerminalExpression
和非终端表达式AddExpression
、SubtractExpression
。通过组合这些表达式,我们可以构建出复杂的表达式树,并通过调用interpret
方法来计算表达式的值。
注意,这里的Context
类并未在示例中实际使用,但在实际应用中,它可能用于存储全局变量或方法,这些变量或方法可能在表达式的解释过程中被访问。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。