设计模式

设计模式是前程序员总结出来的最佳实践,结果时间沉淀,形成的一套解决问题的方法。

通常,一个设计模式专注于解决一类问题,不会有万能的设计模式

单例模式

当数据对象存在 唯一 现象

如一个班级只有一个班主任,则使用单例模式,即保证一个类仅有一个实例

单例模式的核心办法是把构造函数设置为私有:

public class ClassMaster {
  private String id;
  // 班主任名称
  private String name;
  private String gender;

  // 唯一实例
  private static ClassMaster instance = new ClassMaster();

  private ClassMaster() {
  }
}

再在自己类的内部实例化自己,以此不允许其他类实例化这个类,只有自己能实例化一个唯一的自己。

此时,我们需要添加一个get方法来获取这个实例

 public static ClassMaster getInstance() {
    return instance;
  }

简单工厂模式

简单工厂模式解决的问题是:代码重复、耦合紧密

如以水果商店为例,为满足不同口味顾客,商店需要根据口味单独将单个水果实例化如(伪代码)

public class 餐馆 {
  public static void main(String[] args) {
    if (客人.get口味() == "甜") {
      西瓜 w = new 西瓜();
    } else if (客人.get口味() == "酸") {
      柠檬 l = new 柠檬();
    } else if (客人.get口味() == "臭") {
      榴莲 d = new 榴莲();
    }
  }
}

而这样的情况可以使用简单工厂模式代替

简单工厂模式例子:

public class FruitFactory {
    public static Fruit getFruit(Customer customer) {
        Fruit fruit = null;
        if ("sweet".equals(customer.getFlavor())) {
            fruit = new Watermelon();
        } else if ("acid".equals(customer.getFlavor())) {
            fruit = new Lemon();
        } else if ("smelly".equals(customer.getFlavor())) {
            fruit = new Durian();
        }

        return fruit;
    }
}

这样来完成代码,可以更有效完成解耦以及解决代码重复问题

工厂类为了提高辨识度,一般以 XXXFactory为名

抽象工程模式

简单工厂适合创建一种对象,但当情况复杂,需要创建一个系列、多种产品的时候,就需要抽象工厂模式。

如水果超市除了卖水果,还需要提供饮料,需要在“水果工厂”之外创建一个“饮料工厂”,我们可以多创建一个工厂抽象类把多个工厂进一步抽象。

其中由于水果工厂需要继承接口,需要添加getDrink()方法,此时返回null就行

SnacksFactoryBuilder 方法是生产工厂的工厂,如工厂是生产产品的工厂,其作用是创建工厂实例。

观察者模式

观察者模式的核心是要知道观察什么,什么对象发生了变化需要发出通知,如天气系统的订阅/通知功能。

如天气系统,我们需要观察的是天气的变化,我们抽象出天气的对象,

public class WeatherData extends Observable

让它继承Java提供的 Observable 类,表示其是核心的、需要被观察的类。被称为被观察者

public class WeatherData extends Observable {
    // 城市
    private String cityName;
    // 时间
    private String time;
    // 温度
    private String temp;

    // 城市固定了就不变了
    public WeatherData(String cityName) {
        this.cityName = cityName;
    }

    // 打印天气信息
    public String toString() {
        return cityName + "," + LocalDate.now().toString() + " " + time + ",气温:" + temp + "摄氏度。";
    }

    public String getCityName() {
        return cityName;
    }

    public String getTime() {
        return time;
    }

    public String getTemp() {
        return temp;
    }
}

然后我们需要在数据变化后发起通知:

public class WeatherData extends Observable {
    /**
     * 一个城市的气温在某个时刻发生了变化
     */
    public void changeTemp(String time, String temp) {
        if(time == null || temp == null) {
            // 输入数据为空是有问题的,不处理
            return;
        }

        // 与原数据不同,说明发生了变化
        if(!time.equals(this.time) || !temp.equals(this.temp)) {
            // 标记变化
            super.setChanged();
            this.time = time;
            this.temp = temp;
            // 发出通知,参数是额外的信息
            super.notifyObservers("温度变化已通知");
        }
    }
}

这里使用了父类方法 setChanged()notifyObservers()

setChanged() 是标记被观察者对象发生了变化

notifyObservers() 就是发出通知,如果需要发生额外(不在观察者对象内)的信息,在参数中传入信息对象即可。

存在被观察者,自然也要创建观察者:

public class WeatherObserver implements Observer {
    private String name;

    @Override
    public void update(Observable o, Object arg) {
        System.out.print(this.name + "观察到天气变化为:");
        System.out.print(o.toString());
        System.out.println(" " + arg);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

观察者需要实现 update() 方法其中,

方法第一个参数是被观察者对象(继承Observable)

方法第二个参数就是额外信息,具体就是调用 super.notifyObservers() 时传入的参数对象。

如果不想发生额外信息,就是 super.notifyObservers(null) 此时arg值就是null。

当我们在 main中调用时,被观察者需要添加观察者:

        // 城市天气数据
        WeatherData weatherData = new WeatherData("余杭");
        // 添加观察者
        weatherData.addObserver(w1);
        weatherData.addObserver(w2);