你知道 的Java中常用的几种设计模式

1. 工厂模式(Factory Pattern)

简要说明:工厂模式用于创建对象,而不将具体类的信息暴露给客户端,客户端通过工厂接口来创建对象。

示例:假设有一个产品接口(如Car)和多个实现类(如SedanSUV)。工厂类(如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),然后创建几个实现了该接口的具体日志记录器类(如ConsoleLoggerFileLogger等)。接着,我们创建一个日志记录器工厂(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)

    这个模式主要用于在不修改对象结构的前提下,为对象结构中的元素添加新的操作。它允许我们增加新的操作而无需修改现有的类层次结构,这有助于保持类的封装性和系统的可扩展性。

访问者模式通常包含以下几个角色:

  1. Visitor(访问者):接口声明了每个元素(Element)应该接受的操作。
  2. ConcreteVisitor(具体访问者):实现了Visitor接口,为每个Element类实现一个操作。
  3. Element(元素):声明了一个接受访问者(accept)的方法,该方法通常以一个访问者对象作为参数。
  4. ConcreteElement(具体元素):实现了Element接口,存储数据,并定义一个或多个接受操作(accept),以便在元素上执行访问者中的操作。
  5. 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);  
    }  
}

      在这个例子中,我们定义了两个具体元素ConcreteElement1ConcreteElement2,它们都实现了Element接口,并提供了自己的数据。我们还定义了一个Visitor接口和一个具体访问者ConcreteVisitor1,它实现了对两种元素的操作。客户端代码通过调用元素的accept方法,并使用具体的访问者来执行操作,从而在不修改元素类的情况下增加了新的行为。

     访问者模式特别适用于对象结构相对稳定,但经常需要在此结构上定义新的操作的情况。然而,由于它增加了类的数量(具体访问者和元素类),并可能使得系统变得复杂,因此在使用时需要权衡其利弊。

6. 备忘录模式(Memento Pattern)

     这个模式主要用于在不破坏封装性的前提下,捕获并保存一个对象的内部状态,以便在将来某个时刻可以将对象恢复到这个状态。备忘录模式通常与命令模式结合使用,以实现可撤销的操作。

备忘录模式包含以下几个主要角色:

  1. Originator(原发器):负责创建一个备忘录,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。原发器根据需要决定备忘录存储哪些状态。

  2. Memento(备忘录):负责存储原发器的内部状态,并可防止原发器以外的其他对象访问备忘录。备忘录有两个接口:窄接口(提供给其他对象使用,只暴露有限的、必要的操作)和宽接口(提供给原发器使用,允许原发器访问所有状态,以便恢复)。

  3. 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和其他面向对象编程语言中都非常有用,它们可以帮助开发者以更灵活、可维护和可扩展的方式设计软件系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w1990xw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值