详述java的设计模式(一)

1.观察者模式 

观察者模式(Observer Pattern)是一种常用的设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,它的所有依赖者都会得到通知并且自动更新。

在观察者模式中,有两个重要的角色,即主题(Subject)和观察者(Observer)。主题维护了一个观察者列表,并且提供了注册和删除观察者的接口,以及通知观察者的接口。观察者则定义了接收主题通知的方法,以便主题在状态发生改变时能够通知观察者。

使用观察者模式的好处在于,它可以让对象之间的耦合度降低,使得对象之间的交互更加灵活。如果我们需要增加一个新的观察者,只需要让其实现 Observer 接口,并将其注册到主题中即可,不需要修改原有的代码。

观察者模式在实际开发中广泛应用,例如 Java 中的事件监听机制、Android 中的广播机制等等。

示例

下面是一个以新闻为主题的观察者模式实例。

假设我们正在开发一个新闻订阅系统,我们需要订阅多个新闻源,这些新闻源可能来自不同的媒体,如报纸、电视、网络等。当新闻源发布新文章时,系统需要自动将新闻内容推送给已订阅的用户。

为了实现这个功能,我们可以使用观察者模式,其中,新闻源是被观察者(被观察的对象),订阅者是观察者(观察的对象)。

在这个系统中,新闻源发布新文章时,会触发一个事件,订阅者将监听这个事件并执行相应的操作,即将新文章推送给已订阅的用户。

下面是一个简单的实现:

创建被观察者接口 NewsPublisher,定义 register、unregister 和 notifyObservers 方法。

public interface NewsPublisher {
    void register(NewsSubscriber subscriber);
    void unregister(NewsSubscriber subscriber);
    void notifyObservers(String news);
}

创建观察者接口 NewsSubscriber,定义 update 方法。

public interface NewsSubscriber {
    void update(String news);
}

创建一个新闻源类 Newspaper 实现 NewsPublisher 接口,用于发布新闻。

import java.util.ArrayList;
import java.util.List;

public class Newspaper implements NewsPublisher {
    private List<NewsSubscriber> subscribers = new ArrayList<>();

    public void addNews(String news) {
        System.out.println("Newspaper: " + news);
        notifyObservers(news);
    }

    @Override
    public void register(NewsSubscriber subscriber) {
        subscribers.add(subscriber);
    }

    @Override
    public void unregister(NewsSubscriber subscriber) {
        subscribers.remove(subscriber);
    }

    @Override
    public void notifyObservers(String news) {
        for (NewsSubscriber subscriber : subscribers) {
            subscriber.update(news);
        }
    }
}

创建多个观察者类 TVNewsSubscriberWebNewsSubscriber 实现 NewsSubscriber 接口,用于接收新闻。

public class TVNewsSubscriber implements NewsSubscriber {
    private String name;

    public TVNewsSubscriber(String name) {
        this.name = name;
    }

    @Override
    public void update(String news) {
        System.out.println("TV News Subscriber " + name + ": " + news);
    }
}

public class WebNewsSubscriber implements NewsSubscriber {
    private String name;

    public WebNewsSubscriber(String name) {
        this.name = name;
    }

    @Override
    public void update(String news) {
        System.out.println("Web News Subscriber " + name + ": " + news);
    }
}

在客户端程序中,实例化 Newspaper 并向其中添加订阅者,然后调用 addNews 方法发布新闻。

package org.example.observer;

public class Test {
    public static void main(String[] args) {

        Newspaper newspaper = new Newspaper();

        newspaper.register( new WebNewsSubscriber("张三"));
        newspaper.register(new WebNewsSubscriber("李四"));
        newspaper.register(new WebNewsSubscriber("王五"));

        newspaper.addNews("1111");


    }
}

应用场景

Java的观察者模式主要应用在以下场景:

一个对象的状态发生改变,所有关心这个对象的对象都会收到通知,从而自动更新。

一个对象会多次发生改变,需要关心它的对象能够自动更新。

一个抽象模型有两个方面,其中一个方面依赖于另一个方面,可以使用观察者模式来解决。

在Java中,观察者模式常常被用在图形用户界面(GUI)系统中,比如一个按钮被点击,需要更新界面的状态。观察者模式还常常用于实现事件驱动的系统,比如Swing的事件驱动机制

生活实例

观察者模式是一种设计模式,其目的是在对象之间建立一种一对多的依赖关系,以便当一个对象的状态发生变化时,所有依赖于它的对象都能够得到通知并自动更新。以下是观察者模式在生活中的一些例子:

订阅服务:当你订阅某个服务时,例如邮件列表或社交媒体的通知,你就成为了一个观察者。当服务中有新的内容时,你会收到通知。

观察天气:当你在观察天气时,天气本身就是被观察者,而你是观察者。当天气发生变化时,你会收到通知。

股票交易:当你投资某只股票时,你可能会使用一个股票行情软件来观察它的价格。这个软件就是一个观察者,当股票价格发生变化时,你会收到通知。

游戏中的任务:在游戏中,任务通常会有一个状态,例如正在进行中或已完成。当你完成一个任务时,这个状态就会发生变化,游戏界面也会相应地更新。

这些都是观察者模式在生活中的一些常见例子。

2.单例模式

单例模式是一种常见的创建型设计模式,用于限制一个类只能创建一个对象实例。在单例模式中,一个类只有一个对象实例存在,并且该对象可以被系统中的所有其他对象所访问。

单例模式通常会在需要控制资源的情况下使用,例如在创建数据库连接、线程池或缓存对象等情况下。在这些场景下,单例模式可以确保系统中只有一个对象实例存在,从而避免了资源的浪费和冲突。

单例模式可以使用多种不同的实现方式,包括懒汉式、饿汉式、双重检查锁定、静态内部类、枚举等。每种实现方式都有其优缺点,需要根据具体的需求场景来选择合适的实现方式。

1.饿汉式单例模式

饿汉式单例模式是最简单的一种单例模式,实现简单,线程安全。在类加载的时候就完成了实例化,因此在调用时速度非常快,但是无法实现懒加载。

类加载时就创建了一个私有的、不可变的 Singleton 实例,任何时候都只会有一个实例存在,不会存在并发的情况,所以不会存在线程安全的问题。同时,由于 instance 被声明为 final,所以在获取 instance 时不会存在重排序的问题,保证了线程安全性。

public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

2.懒汉式单例模式(线程不安全)

懒汉式单例模式在调用 getInstance() 方法时才进行实例化,实现了懒加载,但是在多线程环境下是线程不安全的。

这个单例模式在多线程环境下会不安全,因为它没有考虑线程安全,当多个线程同时调用 getInstance() 方法时,可能会导致创建出多个实例。

例如,线程 A 和线程 B 同时调用 getInstance() 方法,此时 instance 为 null,A 执行到 instance = new Singleton(); 时,由于还没有完成初始化,线程 B 也会进入到 instance == null 的判断中,并且也会创建一个新的实例。这样,就会导致多个实例的创建。

public class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

为了解决懒汉式单例模式的线程不安全问题,可以通过在 getInstance() 方法中加入 synchronized 关键字来实现线程安全。但是这种方法效率低下,因为每次获取实例时都需要获取锁。

这段代码是线程安全的,因为在getInstance()方法上使用了synchronized关键字,该关键字可以保证在同一时刻只有一个线程可以访问这个方法。如果在多线程环境下有多个线程同时调用getInstance()方法,由于synchronized关键字的作用,只有一个线程能够执行instance = new Singleton()这一行代码,保证了只有一个实例被创建。

public class Singleton {    
private static Singleton instance = null;    
private Singleton() {}    
public static synchronized Singleton getInstance() {       
if (instance == null) {            
instance = new Singleton();        
}       
 return instance;    
}
}

3.双重检查锁定单例模式

双重检查锁定单例模式通过加锁和双重判断的方式实现了懒加载和线程安全。

这段代码使用了“双重检查锁”机制,即在第一次检查对象实例是否存在时,如果不存在才会获得锁并创建实例。因为同步块使用了类对象Singleton.class作为锁对象,所以在同步块中可以保证只有一个线程进入并创建实例。同时使用了volatile关键字来确保多线程环境下的可见性,即一个线程修改了instance的值,其他线程能够立刻看到这个值的变化,从而避免出现脏读的情况。因此,这段代码是线程安全的。

public class Singleton {
    private static volatile Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

4.静态内部类单例模式

这种方式是使用了类的静态内部类来实现单例模式,具有线程安全的优点。类的静态内部类在类加载时只会被加载一次,同时静态内部类不会在单例类加载时被初始化,只有在调用静态内部类中的方法时才会被初始化,而静态初始化只会执行一次,因此能够保证线程安全。此外,由于 SingletonHolder 是私有静态内部类,因此外部无法访问,也保证了单例的唯一性。

public class Singleton {
    private Singleton() {}
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

5.枚举单例模式

枚举单例模式是一种更加简洁的实现方式,枚举常量只会被初始化一次,因此可以实现单例模式。

这个实现方式是使用了 Java 的枚举类型来实现单例模式,而 Java 的枚举类型保证了在任何情况下都只会有一个实例。因此这种实现方式是线程安全的。枚举类型的实现方式是由 JVM 内部保证线程安全的。在 Java 中,枚举类型是天然的单例模式,且枚举类型在多线程环境下也是线程安全的。

public enum Singleton {
    INSTANCE;
    public Singleton2 getInstance() {
        return Singleton2.getInstance();
    }
}

说明

1. 私有构造函数`Singleton()`:防止外部直接实例化该类,仅允许通过`getInstance()`方法获取该类的唯一实例。
2. 静态变量`instance`:该类的唯一实例。
3. 静态方法`getInstance()`:获取该类的唯一实例。如果`instance`为空,则使用双重检查锁定确保实例化时的线程安全。
4. 关键字`volatile`:确保多线程下的变量同步,保证内存可见性。
5. 同步锁`synchronized (Singleton.class)`:保证实例化时的线程安全,确保在多线程下只有一个线程能够进行实例化。

应用场景

Java单例模式通常在以下场景中使用:

1. 全局配置管理:比如说,整个应用程序只需要一个全局配置管理类,那么可以使用单例模式。
2. 缓存管理:如果应用程序需要维护一个全局的缓存,那么也可以使用单例模式。
3. 日志管理:如果一个应用程序需要持久化存储日志信息,那么也可以使用单例模式。
4. 全局对象的创建:如果整个应用程序只需要一个对象,那么可以使用单例模式。比如说,一个数据库连接池对象。
5. 资源共享:如果多个线程需要共享一个资源,那么可以使用单例模式。

总的来说,单例模式适用于需要维护唯一实例的场景,比如说,全局配置管理、缓存管理、日志管理、全局对象的创建、资源共享等。

生活实例

Plaintext
比如说,你现在正在烤肉,烤肉需要一个烤炉。在这里,烤炉就可以看作是单例模式中的单例对象,因为在整个烤肉过程中,你只需要使用一个烤炉。如果你每次烤肉都建立一个新的烤炉,那么很明显是不必要的,这样只会浪费时间和空间,而且也不利于烤肉效果的统一。因此,我们只需要使用一个烤炉即可。

再比如说,你在做一份报告,需要用到一个打印机。同样地,打印机也可以看作是单例模式中的单例对象。因为在整个报告处理过程中,你只需要使用一个打印机,如果每次都建立一个新的打印机,这样显然是不必要的。因此,我们只需要使用一个打印机即可。

猜你喜欢

转载自blog.csdn.net/qq_18235445/article/details/129313330