介绍常见的设计模式,如单例模式、工厂模式、观察者模式、策略模式、适配器模式等具体的实例

设计模式是软件开发中的一种解决问题的方案。设计模式可以帮助开发者更好地组织代码,提高代码的可读性、可维护性和可扩展性。在本篇博客中,我将介绍几种常见的设计模式,包括单例模式、工厂模式、观察者模式、策略模式、适配器模式,并给出具体的实例来讲解。

1、 单例模式

单例模式是一种常见的创建型设计模式。它保证一个类只有一个实例,并提供一个全局访问点。在Java中,单例模式有多种实现方式,最常见的是饿汉式和懒汉式。

饿汉式单例模式的实现代码如下:

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

懒汉式单例模式的实现代码如下:

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

2、 工厂模式

工厂模式是一种常见的创建型设计模式。它提供了一个统一的接口来创建对象,隐藏了对象的创建细节,使得客户端可以通过统一的接口来获取所需的对象。工厂模式有多种实现方式,最常见的是简单工厂模式和抽象工厂模式。

简单工厂模式的实现代码如下:

public interface Shape {
    void draw();
}

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

public class Rectangle implements Shape {
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

public class ShapeFactory {
    public static 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;
    }
}

抽象工厂模式的实现代码如下:

// Shape接口及其实现类
public interface Shape {
    void draw();
}

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

public class Rectangle implements Shape {
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

// Color接口及其实现类
public interface Color {
    void fill();
}

public class Red implements Color {
    public void fill() {
        System.out.println("Filling with red color");
    }
}

public class Green implements Color {
    public void fill() {
        System.out.println("Filling with green color");
    }
}

// 抽象工厂类
public abstract class AbstractFactory {
    public abstract Shape getShape(String shapeType);
    public abstract Color getColor(String colorType);
}

// 具体的工厂实现类
public class ShapeFactory extends AbstractFactory {
    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;
    }
    
    public Color getColor(String colorType) {
        return null;
    }
}

public class ColorFactory extends AbstractFactory {
    public Shape getShape(String shapeType) {
        return null;
    }
    
    public Color getColor(String colorType) {
        if(colorType == null) {
            return null;
        }
        if(colorType.equalsIgnoreCase("Red")) {
            return new Red();
        }
        else if(colorType.equalsIgnoreCase("Green")) {
            return new Green();
        }
        return null;
    }
}

// 工厂生成器类
public class FactoryProducer {
    public static AbstractFactory getFactory(String choice) {
        if(choice.equalsIgnoreCase("Shape")) {
            return new ShapeFactory();
        }
        else if(choice.equalsIgnoreCase("Color")) {
            return new ColorFactory();
        }
        return null;
    }
}

// 使用抽象工厂模式生成对象
public class Main {
    public static void main(String[] args) {
        AbstractFactory shapeFactory = FactoryProducer.getFactory("Shape");
        Shape circle = shapeFactory.getShape("Circle");
        circle.draw();
        
        AbstractFactory colorFactory = FactoryProducer.getFactory("Color");
        Color red = colorFactory.getColor("Red");
        red.fill();
    }
}

在上面的实现中,我们创建了一个抽象工厂 AbstractFactory,它有两个抽象方法 getShape()getColor(),分别用于创建不同类型的形状和颜色。然后,我们创建了两个具体工厂 ShapeFactoryColorFactory,它们分别实现了 AbstractFactory 接口,并且负责创建具体的形状和颜色对象。

接着,我们创建了两个形状对象 CircleRectangle,它们都实现了 Shape 接口,并且具有 draw() 方法用于绘制形状。然后,我们创建了两个颜色对象 RedGreen,它们都实现了 Color 接口,并且具有 fill() 方法用于填充颜色。

最后,在客户端代码中,我们根据需要创建相应的工厂对象(ShapeFactoryColorFactory),然后使用工厂对象创建具体的形状和颜色对象。这种方式可以实现对象的创建和使用的解耦,同时也可以方便地添加新的形状和颜色类型。

抽象工厂模式的优点包括:

  1. 可以方便地添加新的产品族,例如增加一个新的形状类型或颜色类型,只需要扩展抽象工厂和具体工厂即可,不需要修改客户端代码。

  2. 可以确保产品的一致性,例如在上面的实现中,ShapeFactoryColorFactory 只能创建对应的形状和颜色,这可以避免客户端代码错误地组合不匹配的形状和颜色。

  3. 可以实现高内聚低耦合的设计原则,将产品的创建和使用解耦,使得系统更加灵活、可扩展和易维护。

但是,抽象工厂模式也有一些缺点,例如:

  1. 增加新的产品族会很麻烦,需要修改抽象工厂和所有的具体工厂。

  2. 当产品族比较复杂时,抽象工厂和具体工厂的层次结构会变得很庞大,增加了系统的复杂性。

总之,抽象工厂模式是一种创建型设计模式,它提供了一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。在实际开发中,可以根据需要选择合适的设计模式来提高代码的可维护性、可扩展性和复用性。

3、 观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象间一种一对多的依赖关系,使得每当一个对象状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

该模式由两个角色组成:

  1. Subject(主题):被观察的对象,通常包含一个观察者列表,提供注册、删除观察者以及通知观察者的方法。

  2. Observer(观察者):观察主题对象的状态变化,当主题对象状态发生变化时,观察者对象会得到通知并进行相应的处理。

下面是一个使用观察者模式的实例:

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

interface Subject {
    void register(Observer o);
    void unregister(Observer o);
    void notifyObservers();
}

interface Observer {
    void update();
}

class WeatherData implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private int temperature;

    public void setTemperature(int temperature) {
        this.temperature = temperature;
        notifyObservers();
    }

    public int getTemperature() {
        return temperature;
    }

    @Override
    public void register(Observer o) {
        observers.add(o);
    }

    @Override
    public void unregister(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            o.update();
        }
    }
}

class User implements Observer {
    private String name;
    private WeatherData weatherData;

    public User(String name, WeatherData weatherData) {
        this.name = name;
        this.weatherData = weatherData;
        weatherData.register(this);
    }

    @Override
    public void update() {
        int temperature = weatherData.getTemperature();
        System.out.println(name + " got notified. Temperature is " + temperature);
    }
}

public class ObserverPatternDemo {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        User user1 = new User("John", weatherData);
        User user2 = new User("Jane", weatherData);
        User user3 = new User("Bob", weatherData);

        weatherData.setTemperature(30);
    }
}

上面的代码实现了一个天气预报系统,主题是 WeatherData 类,观察者是 User 类。当 WeatherData 对象的温度属性发生变化时,会调用 notifyObservers() 方法通知所有的观察者,并调用观察者的 update() 方法进行更新。每个 User 对象在创建时都会将自己注册为 WeatherData 对象的观察者,当温度发生变化时就会得到通知。

4、策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互相替换。通过策略模式,可以使算法独立于使用它的客户端而变化。

在策略模式中,有三个角色:

  • Context:上下文角色,用来操作算法的对象。
  • Strategy:策略角色,定义所有支持的算法的公共接口。
  • ConcreteStrategy:具体策略角色,实现了策略角色中定义的接口。

策略模式的优点是可以减少大量的 if/else 语句,增加代码的可扩展性和可维护性。下面我们来看一个简单的例子。

假设我们要设计一个计算器,它可以进行加、减、乘、除四种运算。我们可以使用策略模式来实现这个计算器,具体代码如下:

// 策略接口
public interface Strategy {
    int doOperation(int num1, int num2);
}

// 加法策略
public class OperationAdd implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

// 减法策略
public class OperationSubtract implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 - num2;
    }
}

// 乘法策略
public class OperationMultiply implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 * num2;
    }
}

// 除法策略
public class OperationDivide implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 / num2;
    }
}

// 使用策略的类
public class Calculator {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public int calculate(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

// 测试代码
public class StrategyPatternDemo {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();

        calculator.setStrategy(new OperationAdd());
        System.out.println("10 + 5 = " + calculator.calculate(10, 5));

        calculator.setStrategy(new OperationSubtract());
        System.out.println("10 - 5 = " + calculator.calculate(10, 5));

        calculator.setStrategy(new OperationMultiply());
        System.out.println("10 * 5 = " + calculator.calculate(10, 5));

        calculator.setStrategy(new OperationDivide());
        System.out.println("10 / 5 = " + calculator.calculate(10, 5));
    }
}

在上面的代码中,我们定义了一个策略接口 Strategy,其中包含一个 doOperation 方法,用于进行算法运算。我们还定义了四个具体的策略类,分别实现加、减、乘、除四种运算。

在 Calculator 类中,我们定义了一个 Strategy 类型的变量 strategy,并提供了 setStrategy 方法来设置当前使用的算法。在 calculate 方法中,我们就可以使用当前算法对输入的两个数字进行运算。

在 StrategyPatternDemo 测试代码中,我们创建了一个 Calculator 对象,并分别使用四种算法对输入的两个数字进行运算。这样我们就可以在运行时动态地切换算法,而不需要改变 Calculator 类的代码。这就是策略模式的优点之一。

5、  适配器模式

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一种接口,从而使原本由于接口不兼容而不能一起工作的类能够在一起工作。

适配器模式主要包含三个角色:目标接口(Target)、适配器(Adapter)和被适配者(Adaptee)。其中,目标接口是客户端所期望的接口,适配器是将被适配者的接口转换成目标接口的中间件,被适配者是需要被适配的接口。

下面给出一个使用适配器模式的例子:

假设我们有一个音乐播放器,可以播放MP3格式的音乐,但是我们有一些其他格式的音乐文件,如MP4、FLV、WMA等,我们也想要在音乐播放器中播放这些文件。为了实现这个功能,我们可以使用适配器模式。

首先,我们需要定义一个公共的音乐接口MusicPlayer,它包含播放音乐和停止播放音乐两个方法。

public interface MusicPlayer {
    void playMusic(String filePath);
    void stopMusic();
}

然后,我们需要定义一个MP3音乐播放器MP3Player,它实现了MusicPlayer接口,并可以播放MP3格式的音乐。

public class MP3Player implements MusicPlayer {
    public void playMusic(String filePath) {
        System.out.println("Playing MP3 file: " + filePath);
    }

    public void stopMusic() {
        System.out.println("Stopping MP3 player");
    }
}

接下来,我们需要定义适配器类,它将其他格式的音乐文件转换为MP3格式,以便可以在MP3Player中播放。我们定义一个名为MediaAdapter的适配器类,它实现了MusicPlayer接口,并包含一个private的AdvancedMusicPlayer接口类型的对象。

public class MediaAdapter implements MusicPlayer {
    private AdvancedMusicPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("MP4")) {
            advancedMusicPlayer = new MP4Player();
        } else if (audioType.equalsIgnoreCase("FLV")) {
            advancedMusicPlayer = new FLVPlayer();
        } else if (audioType.equalsIgnoreCase("WMA")) {
            advancedMusicPlayer = new WMAPlayer();
        }
    }

    public void playMusic(String filePath) {
        advancedMusicPlayer.playAdvancedMusic(filePath);
    }

    public void stopMusic() {
        advancedMusicPlayer.stopAdvancedMusic();
    }
}

在适配器类中,我们在构造方法中根据传入的音频类型来创建不同的AdvancedMusicPlayer对象,然后在playMusic()和stopMusic()方法中调用AdvancedMusicPlayer接口的方法来实现播放和停止播放音乐的功能。

最后,我们定义一个客户端类MusicPlayerClient,它包含一个MusicPlayer对象,并可以播放MP3、MP4、FLV、WMA等格式的音乐。

public class MusicPlayerClient {
    private MusicPlayer musicPlayer;

    public MusicPlayerClient(String audioType) {
        if (audioType.equalsIgnoreCase("MP3")) {
            musicPlayer = new MP3Player();
        } else {
            musicPlayer = new MediaAdapter(audioType);
        }
    }

    public void playMusic(String filePath) {
        musicPlayer.playMusic(filePath);
    }

    public void stopMusic() {
        musicPlayer.stopMusic();
    }
}

在客户端类中,我们在构造方法中根据传入的音频类型来创建不同的MusicPlayer对象,如果是MP3格式的音乐,就直接使用MP3Player对象;如果是其他格式的音乐,就使用适配器类MediaPlayerAdapter来将其适配成MP3Player对象,然后调用play()方法进行播放。

下面是客户端类的示例代码:

public class Client {
    public static void main(String[] args) {
        // 播放MP3格式音乐
        MusicPlayer player1 = new MP3Player();
        player1.play("music.mp3");

        // 播放其他格式音乐
        MusicPlayer player2 = new MediaPlayerAdapter();
        player2.play("music.wma");
    }
}

运行结果:

Playing music.mp3 with MP3Player.
Playing music.wma with MP3Player (via MediaPlayerAdapter).

可以看到,适配器模式使得客户端可以无需关心不同的音频类型,直接使用MP3Player进行播放,大大提高了代码的可复用性和可维护性。

猜你喜欢

转载自blog.csdn.net/weixin_42279822/article/details/130601424