1. 工厂模式(Factory Pattern)
简要说明:工厂模式用于创建对象,而不将具体类的信息暴露给客户端,客户端通过工厂接口来创建对象。
示例:假设有一个产品接口(如Car
)和多个实现类(如Sedan
、SUV
)。工厂类(如CarFactory
)负责创建Car
接口的实现类实例。
interface Car {
void drive();
}
class Sedan implements Car {
public void drive() {
System.out.println("Driving a Sedan.");
}
}
class SUV implements Car {
public void drive() {
System.out.println("Driving an SUV.");
}
}
class CarFactory {
public static Car getCar(String type) {
if ("sedan".equalsIgnoreCase(type)) {
return new Sedan();
} else if ("suv".equalsIgnoreCase(type)) {
return new SUV();
}
return null;
}
}
再举例个实用的例子,它涉及到创建一个日志记录器的工厂,该工厂可以根据不同的配置或参数返回不同类型的日志记录器实例。
首先,我们定义一个日志记录器的接口(Logger),然后创建几个实现了该接口的具体日志记录器类(如ConsoleLogger
、FileLogger
等)。接着,我们创建一个日志记录器工厂(LoggerFactory),它负责根据传入的参数或配置来创建并返回相应的日志记录器实例。
// 日志记录器接口
interface Logger {
void log(String message);
}
// 控制台日志记录器
class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Console: " + message);
}
}
// 文件日志记录器
class FileLogger implements Logger {
private String filePath;
public FileLogger(String filePath) {
this.filePath = filePath;
}
@Override
public void log(String message) {
// 这里只是模拟写入文件,实际情况下会写入到文件中
System.out.println("File (" + filePath + "): " + message);
}
}
// 日志记录器工厂
class LoggerFactory {
// 根据日志类型返回相应的日志记录器实例
public static Logger getLogger(String loggerType) {
if ("console".equalsIgnoreCase(loggerType)) {
return new ConsoleLogger();
} else if ("file".equalsIgnoreCase(loggerType)) {
// 假设这里从某处获取了文件路径,这里只是示例
String filePath = "/path/to/logfile.log";
return new FileLogger(filePath);
}
// 可以添加更多类型的日志记录器
return null; // 如果没有找到匹配的日志记录器类型,则返回null或抛出异常
}
}
// 客户端代码
public class FactoryPatternExample {
public static void main(String[] args) {
Logger consoleLogger = LoggerFactory.getLogger("console");
if (consoleLogger != null) {
consoleLogger.log("This is a console log message.");
}
Logger fileLogger = LoggerFactory.getLogger("file");
if (fileLogger != null) {
fileLogger.log("This is a file log message.");
}
}
}
在这个例子中,LoggerFactory
类是一个工厂类,它有一个静态方法getLogger
,该方法接收一个字符串参数(表示日志记录器的类型),并根据这个参数返回相应的日志记录器实例。客户端代码通过调用LoggerFactory.getLogger
方法来获取日志记录器实例,并调用其log
方法来记录日志。
这种方式的好处是,如果将来需要添加新的日志记录器类型(比如数据库日志记录器),我们只需要在LoggerFactory
类中添加相应的逻辑来创建并返回新的日志记录器实例,而不需要修改客户端代码,从而实现了对扩展的开放和对修改的封闭(开闭原则)。
2. 单例模式(Singleton Pattern)
简要说明:确保一个类只有一个实例,并提供一个全局访问点。
示例:
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3. 观察者模式(Observer Pattern)
简要说明:一种行为型模式,用于建立一种对象与对象之间的依赖关系,以便当一个对象改变其状态时,所有依赖于它的对象都得到通知并自动更新。
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);
}
}
class Subject {
List<Observer> observers = new ArrayList<>();
void attach(Observer observer) {
observers.add(observer);
}
void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
// 观察者接口
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 + " 收到消息: " + 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) {
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 class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver("Observer1");
Observer observer2 = new ConcreteObserver("Observer2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.notifyObservers("Hello, Observers!");
// 可以移除一个观察者并再次通知
subject.removeObserver(observer1);
subject.notifyObservers("This message will not be sent to Observer1");
}
}
4. 策略模式(Strategy Pattern)
简要说明:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。此模式让算法的变化独立于使用算法的客户。
示例:
interface Strategy {
int doOperation(int num1, int num2);
}
class OperationAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
再举例个经典实现例子。单例模式确保一个类仅有一个实例,并提供一个全局访问点来获取该实例。
// 单例类
public class Singleton {
// 私有静态变量,持有类的唯一实例
private static Singleton instance;
// 私有构造函数,防止外部通过new创建实例
private Singleton() {}
// 公共静态方法,返回类的唯一实例
// 使用懒汉式(线程不安全版本),为了简化示例
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// 示例方法,展示单例的使用
public void doSomething() {
System.out.println("Doing something...");
}
// 注意:上面的懒汉式实现是线程不安全的。
// 在多线程环境下,可能需要使用双重检查锁定(Double-Checked Locking)或其他同步机制来确保线程安全。
// 下面是一个线程安全的双重检查锁定实现示例:
// 线程安全的懒汉式单例
// 使用volatile关键字确保instance变量的可见性和禁止指令重排序
private static volatile Singleton safeInstance;
// 线程安全的公共静态方法
public static Singleton getSafeInstance() {
if (safeInstance == null) {
synchronized (Singleton.class) {
if (safeInstance == null) {
safeInstance = new Singleton();
}
}
}
return safeInstance;
}
// 客户端代码示例
public static void main(String[] args) {
// 调用单例获取实例
Singleton singleton = Singleton.getInstance();
singleton.doSomething();
// 尝试获取另一个实例,但实际上是同一个实例
Singleton anotherSingleton = Singleton.getInstance();
System.out.println(singleton == anotherSingleton); // 输出 true
// 使用线程安全的单例获取实例
Singleton safeSingleton = Singleton.getSafeInstance();
safeSingleton.doSomething();
}
}
在上面的例子中,Singleton
类通过私有化其构造函数来防止外部通过new
关键字创建实例。它提供了一个公共的静态方法getInstance()
来返回类的唯一实例。为了线程安全,我们还展示了如何使用双重检查锁定(Double-Checked Locking)模式来实现懒汉式单例的线程安全版本。
请注意,虽然双重检查锁定可以提高性能(因为它减少了同步的开销),但它也增加了代码的复杂性。在Java中,从Java 5开始,可以使用enum
来实现更简洁、自动支持序列化的线程安全单例模式。此外,对于简单的应用或不需要延迟初始化的场景,也可以直接使用饿汉式单例(即在类加载时就完成实例的初始化)。
5. 装饰器模式(Decorator Pattern)
简要说明:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
示例:
interface Component {
void operation();
}
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent's operation");
}
}
class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedFunctionality();
}
void addedFunctionality() {
System.out.println("New Functionality Added by ConcreteDecoratorA");
}
}
装饰者模式允许向一个现有的对象添加新的功能,同时又不改变其结构。就增加功能来说,装饰者模式相比生成子类更为灵活。这种模式创建了一个包装对象,也就是装饰器,来包裹真实的对象。
// 定义一个接口,作为所有被装饰对象的基类
interface Component {
void operation();
}
// 具体的被装饰对象
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("执行具体的操作");
}
}
// 装饰者抽象类
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体的装饰者
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedFunctionality();
}
public void addedFunctionality() {
System.out.println("添加了额外的功能A");
}
}
// 客户端代码
public class DecoratorPatternDemo {
public static void main(String[] args) {
Component component = new ConcreteComponent();
// 创建装饰者
component = new ConcreteDecoratorA(component);
// 执行操作
component.operation();
}
}
6. 访问者模式(Visitor Pattern)
这个模式主要用于在不修改对象结构的前提下,为对象结构中的元素添加新的操作。它允许我们增加新的操作而无需修改现有的类层次结构,这有助于保持类的封装性和系统的可扩展性。
访问者模式通常包含以下几个角色:
- Visitor(访问者):接口声明了每个元素(Element)应该接受的操作。
- ConcreteVisitor(具体访问者):实现了Visitor接口,为每个Element类实现一个操作。
- Element(元素):声明了一个接受访问者(accept)的方法,该方法通常以一个访问者对象作为参数。
- ConcreteElement(具体元素):实现了Element接口,存储数据,并定义一个或多个接受操作(accept),以便在元素上执行访问者中的操作。
- ObjectStructure(对象结构):能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素。
以下是一个简单的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
,它实现了对两种元素的操作。客户端代码通过调用元素的accept
方法,并使用具体的访问者来执行操作,从而在不修改元素类的情况下增加了新的行为。
访问者模式特别适用于对象结构相对稳定,但经常需要在此结构上定义新的操作的情况。然而,由于它增加了类的数量(具体访问者和元素类),并可能使得系统变得复杂,因此在使用时需要权衡其利弊。
6. 备忘录模式(Memento Pattern)
这个模式主要用于在不破坏封装性的前提下,捕获并保存一个对象的内部状态,以便在将来某个时刻可以将对象恢复到这个状态。备忘录模式通常与命令模式结合使用,以实现可撤销的操作。
备忘录模式包含以下几个主要角色:
-
Originator(原发器):负责创建一个备忘录,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。原发器根据需要决定备忘录存储哪些状态。
-
Memento(备忘录):负责存储原发器的内部状态,并可防止原发器以外的其他对象访问备忘录。备忘录有两个接口:窄接口(提供给其他对象使用,只暴露有限的、必要的操作)和宽接口(提供给原发器使用,允许原发器访问所有状态,以便恢复)。
-
Caretaker(管理者):负责保存备忘录对象,不能对备忘录对象的内容进行访问或检查。
以下是一个简单的Java示例,展示了备忘录模式的应用:
// 备忘录接口
interface Memento {
// 这里可以根据需要定义方法来获取或设置状态
// 但通常备忘录是私有的,只通过原发器来操作
}
// 具体备忘录
class GameStateMemento implements Memento {
private String state;
public GameStateMemento(String state) {
this.state = state;
}
// 通常这里不提供公共的getter或setter,因为备忘录应由原发器管理
// 但为了示例,我们提供一个方法来展示状态
public String getState() {
return state;
}
}
// 原发器
class Game {
private String state;
// 创建一个备忘录
public Memento createMemento() {
return new GameStateMemento(state);
}
// 从备忘录恢复状态
public void restoreMemento(Memento memento) {
if (memento instanceof GameStateMemento) {
this.state = ((GameStateMemento) memento).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 MementoPatternDemo {
public static void main(String[] args) {
Game game = new Game();
game.setState("Game Started");
Caretaker caretaker = new Caretaker();
caretaker.setMemento(game.createMemento()); // 保存游戏状态
game.setState("Game Over");
System.out.println("Current State: " + game.getState());
game.restoreMemento(caretaker.getMemento()); // 恢复游戏状态
System.out.println("Restored State: " + game.getState());
}
}
在这个例子中,Game
类作为原发器,负责创建和恢复游戏的状态。GameStateMemento
类作为备忘录,存储了游戏的状态。Caretaker
类作为管理者,负责保存备忘录对象。客户端代码通过原发器创建备忘录,并将其保存在管理者中,然后在需要时从管理者中检索备忘录来恢复游戏的状态。
备忘录模式的主要优点是它提供了一种恢复状态的机制,同时保持了封装性,使得对象状态的保存和恢复不需要暴露给外部对象。然而,由于需要为每个需要保存状态的对象都实现备忘录和原发器,因此可能会增加类的数量,使系统变得复杂。因此,在决定使用备忘录模式之前,需要仔细权衡其利弊。
总结:
这些设计模式在Java和其他面向对象编程语言中都非常有用,它们可以帮助开发者以更灵活、可维护和可扩展的方式设计软件系统。