最困难的时候,也就是离成功不远的时候。—-拿破仑

常用设计模式 - 图1

单例模式

一个班级只能有一个班主任,所以就要保证一个班主任类只有一个实例,单例模式就是为了解决这种问题,保证一个类仅有一个实例
核心办法就是把构造函数设置为私有 private 的。这意味着除了 ClassMaster 自己,其他任何类都不能实例化 ClassMaster 对象。

  1. public class ClassMaster {
  2. private String id;
  3. // 班主任名称
  4. private String name;
  5. private String gender;
  6. //唯一实例 必须用static修饰符 ,否则会造成递归的严重的错误
  7. private static ClassMaster instance = new ClassMaster();
  8. //将构造函数设置为私有的
  9. private ClassMaster() {
  10. }
  11. }

我们需要增加一个方法允许其他类访问这个单例的实例,因为类 new 出一个实例的目的就是要给其他类使用的。

  1. public class ClassMaster {
  2. private String id;
  3. // 班主任名称
  4. private String name;
  5. private String gender;
  6. // 唯一实例
  7. private static ClassMaster instance = new ClassMaster();
  8. private ClassMaster() {
  9. }
  10. // 外部类可以通过这个方法访问唯一的实例
  11. public static ClassMaster getInstance() {
  12. return instance;
  13. }
  14. }

spring中的单例思想

有时候从技术角度出发为了节省系统资源,在Spring中类变量使用 @Autowried 注解,能够实现自动注入实例对象。任何 自动 注入实例对象,默认只有一个实例对象,是单例的。

  1. @Autowired
  2. private UsersService usersService;

Spring 会保证只生成一个 UsersServiceImpl 实例注入到多个 ServiceControl 中。

简单工厂模式

程序中的工厂就是生产 实例对象 的地方。

命名

一般来说,工厂类命名为 XXXXFactory ,以 Factory 作为后缀可以提高辨识度,易于理解这个类的作用。
使用简单工厂完成功能开发时,重点就是要明确 什么条件下 创建 什么实例对象 的需求逻辑。

例子

需求:
第二汽车制造厂可以生产“富康牌”轿车以及“爱丽舍”轿车。要求4S店告诉工厂汽车品牌名称即可提取相应的轿车。
分析:
我们需要将具体的汽车类抽象出一个接口 Car ,让具体的汽车类实现接口中的方法,让汽车工厂 CarFactory 生产实例对象就可以了。
类图:
常用设计模式 - 图2
注意类图中有一个抽象类 AbstractCar ,这是为了防止代码重复而创建,因为每个汽车的实现类都要写:

  1. private String brand;
  2. public String getBrand() {
  3. return brand;
  4. }
  5. public void setBrand(String brand) {
  6. this.brand = brand;
  7. }

所以使用抽象类可以避免每个轿车重复定义 brand 属性。轿车类不再直接实现接口,而是继承 AbstractCar ,这样的话

  • 抽象类存放公共属性和方法
  • 每个实现类存放自己特有的属性和方法

代码实现:

  1. public class CarShop {
  2. public static void main(String[] args) {
  3. CarFactory carFactory = new CarFactory();
  4. Car fukang = carFactory.makeCar("fukang");
  5. System.out.println(fukang.getBrand());
  6. Car elysee = carFactory.makeCar("elysee");
  7. System.out.println(elysee.getBrand());
  8. }
  9. }
  1. public class CarFactory {
  2. public Car makeCar(String brand) {
  3. Car car = null;
  4. if ("fukang".equals(brand)) {
  5. FuKang fukang = new FuKang();
  6. fukang.setBrand("富康");
  7. car = fukang;
  8. } else if ("elysee".equals(brand)) {
  9. Elysee elysee = new Elysee();
  10. elysee.setBrand("爱丽舍");
  11. car = elysee;
  12. }
  13. return car;
  14. }
  15. }
  1. public interface Car {
  2. public String getBrand();
  3. }
  1. public abstract class AbstractFood implements Food {
  2. private String Name;
  3. public void setName(String name) {
  4. this.name = name;
  5. }
  6. public String getName() {
  7. return name;
  8. }
  9. }
  1. public class Elysee extends AbstractFood{
  2. }
  1. public class fukang extends AbstractFood{
  2. }

抽象工厂模式

简单工厂是将多个产品抽象,使用一个工厂统一创建,抽象工厂的主要作用就是把多个工厂进一步抽象。
比如水果工厂只生产水果,而饮料工厂只生产饮料,但是超市既有水果也有饮料,这个时候就需要多个工厂搭配。

类图

常用设计模式 - 图3
进一步抽象出了工厂接口 SnacksFactory

  1. 工厂接口规定工厂应该提供的商品种类,所以包含所有工厂的方法。

    1. public class FruitFactory implements SnacksFactory {
    2. public Fruit getFruit(Customer customer) {
    3. Fruit fruit = null;
    4. if ("sweet".equals(customer.getFlavor())) {
    5. fruit = new Watermelon();
    6. } else if ("acid".equals(customer.getFlavor())) {
    7. fruit = new Lemon();
    8. } else if ("smelly".equals(customer.getFlavor())) {
    9. fruit = new Durian();
    10. }
    11. return fruit;
    12. }
    13. //水果工厂不提供饮料,水果工厂实现工厂接口后,必须实现getDrink()方法,直接返回null即可
    14. public Drink getDrink(Customer customer) {
    15. return null;
    16. }
    17. }
  2. SnacksFactroyBuilder 称之为 生产工厂的工厂 ,工厂用来生产产品实例, SnacksFactroyBuilder 用来生产工厂实例。

    1. public class SnacksFactoryBuilder {
    2. public SnacksFactory buildFactory(String choice) {
    3. if (choice.equalsIgnoreCase("fruit")) {
    4. return new FruitFactory();
    5. } else if (choice.equalsIgnoreCase("drink")) {
    6. return new DrinkFactory();
    7. }
    8. return null;
    9. }
    10. }

    扩展

    不提倡在工厂中定义 static 方法,

  3. 因为复杂场景下,实例方法可以被继承,扩展性较好

  4. 在使用 Spring 框架的时候,可以为 SnacksFactoryBuilder 加上 @Component 注解,可以让框架管理实例:

    1. @Component
    2. public class SnacksFactoryBuilder {
    3. public SnacksFactory buildFactory(String choice) {
    4. }
    5. }
    1. @Service
    2. public class XxxxxServiceImpl implements XxxxxService {
    3. @Autowired
    4. private SnacksFactoryBuilder snacksFactoryBuilder;
    5. }

    观察者模式

    例子

    1. 被观察类 - Observable

    比如天气变化的通知,核心是观察天气,先抽象出天气信息对象。

    1. import java.util.Observable;
    2. public class WeatherData extends Observable {
    3. // 城市
    4. private String cityName;
    5. // 时间
    6. private String time;
    7. // 温度
    8. private String temp;
    9. // 城市固定了就不变了
    10. public WeatherData(String cityName) {
    11. this.cityName = cityName;
    12. }
    13. // 打印天气信息
    14. public String toString() {
    15. return cityName + "," + LocalDate.now().toString() + " " + time + ",气温:" + temp + "摄氏度。";
    16. }
    17. public String getCityName() {
    18. return cityName;
    19. }
    20. public String getTime() {
    21. return time;
    22. }
    23. public String getTemp() {
    24. return temp;
    25. }
    26. }

    天气对象类继承了 Observable 类,这个类时Java提供的,继承后就表示是核心的、需要被观察的类。还需要注意的是,这个设计不太一样的地方是没有 setter 方法,因为一个 WeatherData 代表一个城市的天气,初始化后就不能改变了。

    2. 数据变化后发起通知

    什么时间的气温是多少是要被监听的重要信息,所以要在 WeatherData 中增加一个新的方法来专门处理 ```java import java.util.Observable;

public class WeatherData extends Observable { /**

  1. * 一个城市的气温在某个时刻发生了变化
  2. */
  3. public void changeTemp(String time, String temp) {
  4. if(time == null || temp == null) {
  5. // 输入数据为空是有问题的,不处理
  6. return;
  7. }
  8. // 与原数据不同,说明发生了变化
  9. if(!time.equals(this.time) || !temp.equals(this.temp)) {
  10. // 标记变化
  11. super.setChanged();
  12. this.time = time;
  13. this.temp = temp;
  14. // 发出通知 this表示把数据对象自己发送出去
  15. super.notifyObservers(this);
  16. }
  17. }

}

<a name="5DxqQ"></a>
#### 3. 谁接收通知 - Observer
`Observer` 是Java提供的观察者类,实现此接口表示作为观察者。并且要自己实现 `update()` 方法,方法签名是接口定义好的,属于固定写法。方法的作用就是接收通知。
```java
import java.util.Observable;
import java.util.Observer;

public class WeatherObserver implements Observer {
    private String name;

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

    public String getName() {
        return name;
    }

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

4. 调用

观察者可以有多个,观察者对象与被观察者对象谁先 new 出来都可以,但是必须调用 addObserver() 方法把 观察者对象实例 添加到 被观察者实例 中,然后再调用自定义的 changeTemp 方法变更天气,才能触发自动通知。

public class WeatherTest {
    public static void main(String[] args) {
        // 在天气变化后发邮件的观察者
        WeatherObserver w1 = new WeatherObserver();
        w1.setName("天气邮件观察者");

        // 在天气变化后发短信的观察者
        WeatherObserver w2 = new WeatherObserver();
        w2.setName("天气短信观察者");

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

        // 气温变化
        weatherData.changeTemp("11:08", "32.8");
        // 气温变化
        weatherData.changeTemp("14:46", "29.3");
    }
}

总结

跟工厂模式不同的是,观察者模式主要描述的是类的行为,而不是如何创建。
跟工厂模式相同的是,观察者模式让 观察者和被观察者双方的耦合度降到最低。