JAVA 中常用的设计模式,含案例和注释

工厂模式(Factory Pattern)

工厂模式是一种创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们创建对象而不将创建逻辑暴露给客户端,并使用通用接口引用新创建的对象。

代码示例:

javaCopy code// 定义一个接口
interface Shape {
    void draw();
}

// 定义具体的实现类
class Rectangle implements Shape {
    public void draw() {
        System.out.println("Rectangle draw method");
    }
}

class Circle implements Shape {
    public void draw() {
        System.out.println("Circle draw method");
    }
}

// 定义一个工厂类
class ShapeFactory {
    public Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }

        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        }

        return null;
    }
}

// 使用工厂类创建对象
class FactoryPatternDemo {
    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();

        // 获取 Circle 的对象,并调用它的 draw 方法
        Shape shape1 = shapeFactory.getShape("CIRCLE");
        shape1.draw();

        // 获取 Rectangle 的对象,并调用它的 draw 方法
        Shape shape2 = shapeFactory.getShape("RECTANGLE");
        shape2.draw();
    }
}

单例模式(Singleton Pattern)

单例模式是一种创建型模式,它确保一个类只有一个实例,并提供对该实例的全局访问点。

代码示例:

javaCopy code// 使用饿汉式实现单例模式
class Singleton {
    // 创建 Singleton 的一个对象
    private static Singleton instance = new Singleton();

    // 让构造函数为 private,这样该类就不会被实例化
    private Singleton() {}

    // 获取唯一可用的对象
    public static Singleton getInstance() {
        return instance;
    }

    public void showMessage() {
        System.out.println("Hello World!");
    }
}

// 使用 Singleton 类来获取唯一的对象
class SingletonPatternDemo {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        singleton.showMessage();
    }
}

适配器模式(Adapter Pattern)

适配器模式是一种结构型设计模式,它能够将不兼容的对象包装成适配器对象,从而使得原本因为接口不兼容而无法一起工作的类可以一起工作。

在适配器模式中,有三种角色:

  • Target(目标接口):客户端所期望的接口,适配器对象要实现该接口。
  • Adaptee(被适配者):需要被适配的对象或类,它定义了客户端不能直接调用的接口。
  • Adapter(适配器):将被适配者包装成目标接口的对象。

以下是一个简单的例子,其中 AudioPlayer 是目标接口,MediaPlayer 是被适配者,MediaAdapter 是适配器:

AudioPlayer.java:

javaCopy codepublic class AudioPlayer implements MediaPlayer {
    MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {

        //播放 mp3 音乐文件的内置支持
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing mp3 file. Name: " + fileName);
        }
        //mediaAdapter 提供了播放其他文件格式的支持
        else if (audioType.equalsIgnoreCase("vlc")
                || audioType.equalsIgnoreCase("mp4")) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        }
        else {
            System.out.println("Invalid media. " + audioType + " format not supported");
        }
    }
}

MediaPlayer.java:

javaCopy codepublic interface MediaPlayer {
    public void play(String audioType, String fileName);
}

AdvancedMediaPlayer.java:

javaCopy codepublic interface AdvancedMediaPlayer {
    public void playVlc(String fileName);
    public void playMp4(String fileName);
}

VlcPlayer.java:

javaCopy codepublic class VlcPlayer implements AdvancedMediaPlayer{

   @Override
   public void playVlc(String fileName) {
      System.out.println("Playing vlc file. Name: "+ fileName);
   }

   @Override
   public void playMp4(String fileName) {
      //什么也不做
   }
}

Mp4Player.java:

javaCopy codepublic class Mp4Player implements AdvancedMediaPlayer{

   @Override
   public void playVlc(String fileName) {
      //什么也不做
   }

   @Override
   public void playMp4(String fileName) {
      System.out.println("Playing mp4 file. Name: "+ fileName);
   }
}

MediaAdapter.java:

javaCopy codepublic class MediaAdapter implements MediaPlayer {

    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType){
        if(audioType.equalsIgnoreCase("vlc") ){
            advancedMusicPlayer = new VlcPlayer();

        }else if (audioType.equalsIgnoreCase("mp4")){
            advancedMusicPlayer = new Mp4Player();
        }
    }

    @Override
    public void play(String audioType, String fileName) {

        if(audioType.equalsIgnoreCase("vlc")){
            advancedMusicPlayer.playVlc(fileName);
        }
        else if(audioType.equalsIgnoreCase("mp4")){
            advancedMusicPlayer.playMp4(fileName);
        }
    }
}

我们可以使用适配器模式,使得客户端可以通过 AudioPlayer 接口播放 vlc 或 mp4 。

代理模式(Proxy Pattern)

代理模式是一种常见的结构型设计模式,用于在不改变原始类(或对象)代码的情况下,为其提供一种间接的访问方式,从而实现对其行为进行控制或增强。

代理模式主要有三种形式:静态代理、动态代理和虚拟代理。其中,静态代理需要手动编写代理类,而动态代理和虚拟代理则可以使用Java反射机制进行自动生成。

以下是一个使用静态代理模式实现简单的缓存代理的示例代码,其中实现了一个接口UserService,并通过一个代理类UserServiceProxy来实现对其方法的缓存代理。

javaCopy code// 定义一个接口
public interface UserService {
    public void addUser(String userName);
}

// 实现接口的原始类
public class UserServiceImpl implements UserService {
    public void addUser(String userName) {
        System.out.println("Add user: " + userName);
    }
}

// 代理类,实现了同样的接口,并持有原始类的实例
public class UserServiceProxy implements UserService {
    private UserService userService;
    private Map<String, String> cache;

    public UserServiceProxy(UserService userService) {
        this.userService = userService;
        this.cache = new HashMap<>();
    }

    // 代理方法,首先从缓存中查找,如果不存在则调用原始类方法,并将结果放入缓存
    public void addUser(String userName) {
        if (cache.containsKey(userName)) {
            System.out.println("User " + userName + " already exists.");
        } else {
            userService.addUser(userName);
            cache.put(userName, userName);
            System.out.println("User " + userName + " added.");
        }
    }
}

// 使用代理类
public class Client {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy(userService);
        proxy.addUser("Tom");
        proxy.addUser("Jerry");
        proxy.addUser("Tom"); // 从缓存中获取
    }
}

在上述示例代码中,UserServiceProxy类是一个代理类,它实现了UserService接口,并持有一个UserService类型的实例。在代理类中实现了addUser方法,首先从缓存中查找是否已存在该用户,如果存在则直接返回,否则调用原始类UserServiceImpladdUser方法,并将结果放入缓存。这样,在使用代理类时,就可以自动实现对方法的缓存代理,从而提高程序的运行效率。

需要注意的是,以上示例中的代理模式为静态代理模式,需要手动编写代理类。如果需要实现动态代理或虚拟代理,则可以使用Java反射机制来自动生成代理类,从而简化程序的编写和维护。