1、设计模式七大原则

1.1 单一职责

对类来说,一个类只负责一个职责;如果一个类A负责两个职责,那么更改职责1时可能会影响职责2,所以必须将类A分为两个类。

作用:

  • 降低类的复杂度
  • 提高类的可读性
  • 降低变更引起的风险

问题:

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. vehicle vehicle = new vehicle();
  4. vehicle.run("汽车");
  5. vehicle.run("摩托车");
  6. vehicle.run("飞机");
  7. }
  8. //该类就违反了单一职责问题,天上的交通工具和陆地的相混合
  9. static class vehicle{
  10. public void run(String vehicle){
  11. System.out.println(vehicle+"在公路上运行....");
  12. }
  13. }
  14. }

解决方案一:

  1. public class Test2 {
  2. /*
  3. 虽然遵守了单一职责原则,但这样开销会很大,需要将类分解
  4. */
  5. public static void main(String[] args) {
  6. roadVehicle roadVehicle = new roadVehicle();
  7. airVehicle airVehicle = new airVehicle();
  8. roadVehicle.run("汽车");
  9. roadVehicle.run("摩托车");
  10. airVehicle.run("飞机");
  11. }
  12. static class roadVehicle{
  13. public void run(String vehicle){
  14. System.out.println(vehicle+"在公路上运行....");
  15. }
  16. }
  17. static class airVehicle{
  18. public void run(String vehicle){
  19. System.out.println(vehicle+"在天空运行....");
  20. }
  21. }
  22. }

解决方案二:

  1. public class Test3 {
  2. /*
  3. 这种修改没有分解类,只是增加方法
  4. 类级别上没有遵守单一职责,但是方法上遵守了单一职责原则
  5. */
  6. public static void main(String[] args) {
  7. Vehicle2 vehicle2 = new Vehicle2();
  8. vehicle2.run("汽车");
  9. vehicle2.run("摩托车");
  10. vehicle2.fly("飞机");
  11. }
  12. static class Vehicle2{
  13. public void run(String vehicle){
  14. System.out.println(vehicle+"在公路上运行....");
  15. }
  16. public void fly(String vehicle){
  17. System.out.println(vehicle+"在天空上运行....");
  18. }
  19. }
  20. }

1.2 接口隔离

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上

问题:

这个图的意思是:类“犬科”依赖接口I中的方法:捕食(),行走(),奔跑(); 类“鸟类”依赖接口I中的方法捕食(),滑翔(),飞翔(); 宠物狗类与鸽子类分别是对类“犬科”与类“鸟类”依赖的实现。 对于具体的类:宠物狗与鸽子来说,虽然他们都存在着用不到的方法,但由于实现了接口1,所以也 必须要实现这些用不到的方法。

这样会导致接口过于臃肿,只要接口中出现的方法,不管对依赖于它们的类有没有用处,实现类中都必须去实现这些方法,这显然是不好的设计。如果将这个设计修改为符合接口隔离原则,就必须对接口I进拆分。在这里我们将原有的接口I拆分为三个接口,拆分后的设计如下所示:

改进:

1.3 依赖倒转

  • 高层模块不应该依赖底层模块,二者都应该依赖其抽象
  • 抽象不应该依赖细节,细节应该依赖抽象
  • 依赖倒转的中心思想是面向对象编程
  • 使用接口或抽象类的目的是指定规范,不设计任何具体的操作,把展现细节的任务交给他们的实现类去做。

问题:

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. Person person = new Person();
  4. person.receive(new Email());
  5. person.receiveWeiXin(new WeiXin());
  6. }
  7. //Person接收消息
  8. /*
  9. 方式一实现
  10. 这样虽然简单,容易实现
  11. 但是如果接收的信息不是电子邮件,是微信、QQ等,则就需要在添加类,Person中也要再添加对应的方法
  12. */
  13. static class Person{
  14. public void receive(Email email){
  15. System.out.println(email.getInfo());
  16. }
  17. //新增微信功能,需要新增方法 使代码冗余
  18. public void receiveWeiXin(WeiXin weiXin){
  19. System.out.println(weiXin.getInfo());
  20. }
  21. }
  22. static class Email{
  23. public String getInfo(){
  24. return "电子邮件信息:hello";
  25. }
  26. }
  27. static class WeiXin{
  28. public String getInfo(){
  29. return "微信消息来了!";
  30. }
  31. }
  32. }

改进:

  1. //依赖关系的传递方式
  2. //1.接口传递 2.构造方法传递 3.setter方法传递
  3. public class Test2 {
  4. public static void main(String[] args) {
  5. Person person = new Person();
  6. person.receive(new Email2());
  7. person.receive(new WeiXin());
  8. }
  9. /*
  10. 方式二:通过接口传递实现依赖,每次改动就不需要改变Person类的方法,只需要增加接口的实现类即可
  11. */
  12. static class Person{
  13. public void receive(IReceiver receiver){
  14. System.out.println(receiver.getInfo());
  15. }
  16. }
  17. //自定义接口
  18. interface IReceiver{
  19. public String getInfo();
  20. }
  21. static class Email2 implements IReceiver{
  22. public String getInfo(){
  23. return "电子邮件信息:hello";
  24. }
  25. }
  26. static class WeiXin implements IReceiver{
  27. @Override
  28. public String getInfo() {
  29. return "微信消息来了!";
  30. }
  31. }
  32. }

1.4 里氏替换

原则:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法

问题:

  1. /*
  2. 几维鸟类在继承父类时,无意间重写了父类的方法setSpeed,这违背了里氏替换原则,致使flySpeed等于0了,导致发生错误
  3. */
  4. 运行结果:无法计算出几维鸟的飞行速度
  5. 如果飞行300公里:
  6. 燕子将飞行2.5小时.
  7. 几维鸟将飞行Infinity小时。
  1. public class Test1 {
  2. public static void main(String[] args) {
  3. Bird bird1 = new Swallow();
  4. Bird bird2 = new BrownKiwi();
  5. bird1.setSpeed(120);
  6. bird2.setSpeed(120);
  7. System.out.println("如果飞行300公里:");
  8. try {
  9. System.out.println("燕子将飞行" + bird1.getFlyTime(300) + "小时.");
  10. System.out.println("几维鸟将飞行" + bird2.getFlyTime(300) + "小时。");
  11. } catch (Exception err) {
  12. System.out.println("发生错误了!");
  13. }
  14. }
  15. }
  16. //鸟类 燕子类和几维鸟类都继承了该父类
  17. class Bird {
  18. double flySpeed;
  19. public void setSpeed(double speed) {
  20. flySpeed = speed;
  21. }
  22. public double getFlyTime(double distance) {
  23. return (distance / flySpeed);
  24. }
  25. }
  26. //燕子类
  27. class Swallow extends Bird {
  28. }
  29. //几维鸟类
  30. class BrownKiwi extends Bird {
  31. //重写了父类的方法
  32. public void setSpeed(double speed) {
  33. flySpeed = 0;
  34. }
  35. }

解决方法:

让原有的子类和父类都继承一个更通俗的基类;如取消几维鸟原来的继承关系,定义鸟类和几维鸟的父类,如动物类,它们都有奔跑的能力。几维鸟的飞行速度虽然为 0,但奔跑速度不为 0,可以计算出其奔跑 300 千米所要花费的时间。

1.5 开闭原则

  • 最基础、最重要的设计原则;对扩展开放(提供方),对修改关闭(使用方)
  • 当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求
  • 编程中开闭原则是面向对象程序设计的终极目标

问题:

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. GraphEditor graphEditor=new GraphEditor();
  4. graphEditor.drawShape(new Rectangle());
  5. graphEditor.drawShape(new Circle());
  6. }
  7. /*
  8. 该方式实现虽然比较好理解,操作简单,但是违反了ocp原则,
  9. 如果需要增加一个绘三角形的业务,那么不仅需要增加三角形子类,还会对绘图的类(使用方)进行修改,代码复杂
  10. */
  11. //绘图的类(使用方)
  12. static class GraphEditor{
  13. public void drawShape(Shape shape){
  14. if(shape.m_type==1){
  15. drawRectangle();
  16. }
  17. if(shape.m_type==2){
  18. drawCircle();
  19. }
  20. }
  21. private void drawCircle() {
  22. System.out.println("绘制圆形");
  23. }
  24. private void drawRectangle() {
  25. System.out.println("绘制矩形");
  26. }
  27. }
  28. //Shape类 基类
  29. static class Shape{
  30. int m_type;
  31. }
  32. //矩形子类
  33. static class Rectangle extends Shape{
  34. Rectangle(){
  35. super.m_type=1;
  36. }
  37. }
  38. //圆形子类
  39. static class Circle extends Shape{
  40. Circle(){
  41. super.m_type=2;
  42. }
  43. }
  44. }

改进:

  1. public class Test2 {
  2. public static void main(String[] args) {
  3. GraphEditor graphEditor = new GraphEditor();
  4. graphEditor.drawShape(new Rectangle());
  5. graphEditor.drawShape(new Circle());
  6. }
  7. //绘图的类(使用方)
  8. static class GraphEditor {
  9. public void drawShape(Shape shape) {
  10. shape.draw();
  11. }
  12. }
  13. /*
  14. 改进:
  15. 将基类抽象,增加一个抽象方法,所有子类都需实现这个抽象方法,之后增加新的业务时,就不会改动绘图的类(使用方)中的代码了
  16. */
  17. //Shape类 基类
  18. static abstract class Shape {
  19. int m_type;
  20. public abstract void draw();
  21. }
  22. //矩形子类
  23. static class Rectangle extends Shape {
  24. Rectangle() {
  25. super.m_type = 1;
  26. }
  27. @Override
  28. public void draw() {
  29. System.out.println("绘制矩形");
  30. }
  31. }
  32. //圆形子类
  33. static class Circle extends Shape {
  34. Circle() {
  35. super.m_type = 2;
  36. }
  37. @Override
  38. public void draw() {
  39. System.out.println("绘制圆形");
  40. }
  41. }
  42. //三角形子类
  43. //这里新增业务时只需要扩展一个子类就行,改动的代码少
  44. static class Triangle extends Shape {
  45. Triangle() {
  46. super.m_type = 2;
  47. }
  48. @Override
  49. public void draw() {
  50. System.out.println("绘制三角形");
  51. }
  52. }
  53. }

1.6 迪米特法则

  • 类与类之间的关系应该做到关联最少,耦合度最小
  • 最少知道原则:一个类对自己依赖的类知道的越少越好
  • 只与直接朋友进行通信。直接朋友:两个对象之间有耦合关系,耦合方式:依赖、关联、组合、聚合等
  • 陌生的类不要以局部变量的形式出现在类的内部

问题:

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. CompanyManager e = new CompanyManager();
  4. e.printAllEmployee(new SubCompanyManager());
  5. }
  6. //总公司员工类
  7. static class Employee {
  8. private String id;
  9. public void setId(String id) {
  10. this.id = id;
  11. }
  12. public String getId() {
  13. return id;
  14. }
  15. }
  16. //分公司员工类
  17. static class SubEmployee {
  18. private String id;
  19. public void setId(String id) {
  20. this.id = id;
  21. }
  22. public String getId() {
  23. return id;
  24. }
  25. }
  26. //分公司管理类
  27. static class SubCompanyManager {
  28. //返回分公司的所有员工
  29. public List<SubEmployee> getAllEmployee() {
  30. List<SubEmployee> list = new ArrayList<SubEmployee>();
  31. for (int i = 0; i < 10; i++) {
  32. SubEmployee emp = new SubEmployee();
  33. //为分公司人员按顺序分配一个ID
  34. emp.setId("分公司" + i);
  35. list.add(emp);
  36. }
  37. return list;
  38. }
  39. }
  40. //总公司管理类
  41. //CompanyManager的直接朋友:Employee、SubCompanyManager
  42. //发现问题:SubEmployee不是直接朋友,而是一个陌生类,违反了迪米特法则
  43. static class CompanyManager {
  44. //返回总公司的员工
  45. public List<Employee> getAllEmployee() {
  46. List<Employee> list = new ArrayList<>();
  47. for (int i = 0; i < 5; i++) {
  48. Employee emp = new Employee();
  49. //为总公司人员按顺序分配一个ID
  50. emp.setId("总公司" + i);
  51. list.add(emp);
  52. }
  53. return list;
  54. }
  55. //打印所有公司的员工信息
  56. public void printAllEmployee(SubCompanyManager sub) {
  57. //分公司
  58. List<SubEmployee> list1 = sub.getAllEmployee();
  59. for (SubEmployee e : list1) {
  60. System.out.println(e.getId());
  61. }
  62. //总公司
  63. List<Employee> list2 = this.getAllEmployee();
  64. for (Employee e : list2) {
  65. System.out.println(e.getId());
  66. }
  67. }
  68. }
  69. }

改进:

  1. public class Test2 {
  2. public static void main(String[] args) {
  3. Test1.CompanyManager e = new Test1.CompanyManager();
  4. e.printAllEmployee(new Test1.SubCompanyManager());
  5. }
  6. //总公司员工类
  7. static class Employee {
  8. private String id;
  9. public void setId(String id) {
  10. this.id = id;
  11. }
  12. public String getId() {
  13. return id;
  14. }
  15. }
  16. //分公司员工类
  17. static class SubEmployee {
  18. private String id;
  19. public void setId(String id) {
  20. this.id = id;
  21. }
  22. public String getId() {
  23. return id;
  24. }
  25. }
  26. //分公司管理类
  27. static class SubCompanyManager {
  28. //返回分公司的所有员工
  29. public List<SubEmployee> getAllEmployee() {
  30. List<SubEmployee> list = new ArrayList<>();
  31. for (int i = 0; i < 10; i++) {
  32. SubEmployee emp = new SubEmployee();
  33. //为分公司人员按顺序分配一个ID
  34. emp.setId("分公司" + i);
  35. list.add(emp);
  36. }
  37. return list;
  38. }
  39. //改进:直接将输出分公司员工的方法封装到自己的类中,降低耦合度
  40. //输出分公司人员信息
  41. public void printSubEmployee() {
  42. List<SubEmployee> list1 = getAllEmployee();
  43. for (SubEmployee e : list1) {
  44. System.out.println(e.getId());
  45. }
  46. }
  47. }
  48. //总公司管理类
  49. static class CompanyManager {
  50. //返回总公司的员工
  51. public List<Employee> getAllEmployee() {
  52. List<Employee> list = new ArrayList<>();
  53. for (int i = 0; i < 5; i++) {
  54. Employee emp = new Employee();
  55. //为总公司人员按顺序分配一个ID
  56. emp.setId("总公司" + i);
  57. list.add(emp);
  58. }
  59. return list;
  60. }
  61. //打印所有公司的员工信息
  62. public void printAllEmployee(SubCompanyManager sub) {
  63. //分公司
  64. sub.printSubEmployee();
  65. //总公司
  66. List<Employee> list2 = this.getAllEmployee();
  67. for (Employee e : list2) {
  68. System.out.println(e.getId());
  69. }
  70. }
  71. }
  72. }

1.7 合成复用

原则:尽量使用合成/聚合的方式,而不使用继承

2、UML类图

类之间的关系有继承关系,实现关系,依赖关系,关联关系,聚合关系,组合关系。

2.1 继承关系(泛化)

他是依赖关系的特例,如果A类继承了B类,我们就说A和B存在泛化关系

继承关系使用如下箭头:

由子类指向父类。

2.2 实现关系

实现关系使用如下箭头:

由实现类指向接口

2.3 依赖关系

依赖关系使用如下箭头:

由使用者指向被使用者。

如果A指向B,则说明A中使用了B,使用方式包括A类中有B类实例化对象的局部变量。A类中有方法把B类实例化对象当做了参数,A类中有方法调用了B类中的静态方法。

  1. //PersonServiceBean类中使用到了四个类。
  2. public class PersonServiceBean {
  3. private PersonDao personDao;
  4. public void save(Person person) {
  5. }
  6. public IDCard getIDCard(Integer personid) {
  7. return null;
  8. }
  9. public void modify() {
  10. Department department = new Department();
  11. }
  12. }
  13. public class PersonDao{}
  14. public class IDCard{}
  15. public class Person{}
  16. public class Department{}

UML类图如下:

2.4 关联关系

一种结构关系,指明事物的对象之间的联系:类与类之间的联系

关联关系使用如下箭头:

由拥有者指向被拥有者。如果A指向B,则说明A类中有B类的成员变量

用户拥有并使用计算机:

2.5 聚合关系

聚合关系使用如下箭头:

是整体与部分的关系,且部分可以离开整体而单独存在。;如果B指向A,则说明A类中有B类的成员变量,但是与关联关系不同,A类和B类有逻辑关系。A类是整体,B类是部分。A类由B类构成,同时B类即便不在A类中也可以单独存在。

大学由多个学院聚合而成:

2.6 组合关系

组合关系使用如下箭头:

是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门。

菜单、按钮共同组合成窗口,与窗口不可分离:

3、创建型模式

3.1 单例模式

3.1.1 饿汉式

步骤:

  • 构造器私有化(防止new)
  • 类的内部创建对象
  • 向外暴露一个静态的公共方法
  • 代码实现
  1. //方式一:静态变量
  2. public class Test1 {
  3. public static void main(String[] args) {
  4. //测试
  5. Singleton instance=Singleton.getInstance();
  6. Singleton instance2=Singleton.getInstance();
  7. System.out.println(instance==instance2); //true 单例模式下2次返回的实例是相同的
  8. }
  9. }
  10. class Singleton{
  11. //1.构造器私有化,外部不能new
  12. private Singleton(){
  13. }
  14. //2.内部创建一个对象实例
  15. private final static Singleton instance=new Singleton();
  16. //3.提供一个共有的静态方法,返回实例对象
  17. public static Singleton getInstance(){
  18. return instance;
  19. }
  20. }
  21. //方式二:静态代码块
  22. class Singleton2{
  23. //1.构造器私有化,外部不能new
  24. private Singleton2(){
  25. }
  26. //2.内部创建一个对象实例
  27. private static Singleton2 instance;
  28. //静态代码块中创建单例对象
  29. static {
  30. instance = new Singleton2();
  31. }
  32. //3.提供一个共有的静态方法,返回实例对象
  33. public static Singleton2 getInstance(){
  34. return instance;
  35. }
  36. }

分析:

  • 写法简单,在类加载的时候就完成了实例化,避免了线程同步的问题
  • 缺点:在类加载时完成实例化,没有达到懒加载的效果;
  • 如果从始至终没有使用的话,容易造成内存浪费

3.1.2 懒汉式

步骤与饿汉式相似

  1. class Singleton3{
  2. //静态实例
  3. private static Singleton3 instance;
  4. //构造器私有化
  5. private Singleton3(){}
  6. //懒汉式:线程不安全 提供一个静态公有方法,当使用到该方法时,才去创建instance
  7. public static Singleton3 getInstance(){
  8. if(instance==null){
  9. instance=new Singleton3();
  10. }
  11. return instance;
  12. }
  13. }

分析:

  • 起到了懒加载的效果,但只能在单线程下才能使用
  • 多线程下,如果一个线程进入了if判断还未退出,另一个线程也通过这个if判断,就会产生多个实例,所以多线程下不能使用
  • 实际开发中,不要使用这种方式

加入同步代码解决线程不安全问题:

  1. class Singleton4 {
  2. //静态实例
  3. private static Singleton4 instance;
  4. //构造器私有化
  5. private Singleton4() {
  6. }
  7. //懒汉式:提供一个静态的共有方法,加入同步处理代码synchronized关键字,解决线程不安全问题
  8. public static synchronized Singleton4 getInstance() {
  9. if (instance == null) {
  10. instance = new Singleton4();
  11. }
  12. return instance;
  13. }
  14. }

分析:

  • 虽然解决了线程不安全问题,但效率太低了,不推荐使用
  • 每个线程想获得类的实例时,都需要执行getInstance()方法new一个实例对象,而单例模式中只需要new一次就行了,其他时候可以直接return

3.1.3 双重检查(推荐)

  1. class Singleton5 {
  2. //静态实例
  3. private static volatile Singleton5 instance;
  4. //构造器私有化
  5. private Singleton5() {
  6. }
  7. //提供公有静态方法,加入双重检查,解决线程安全问题,同时实现懒加载
  8. public static Singleton5 getInstance() {
  9. if (instance == null) {
  10. synchronized (Singleton5.class) {
  11. if (instance == null) {
  12. instance = new Singleton5();
  13. }
  14. }
  15. }
  16. return instance;
  17. }
  18. }

分析:

  • Double-Check中进行了2次if (instance == null)判断,这样就能保证线程的安全性
  • 且实例化代码只进行了一次,后续的线程在第一次if判断时就退出了,直接return
  • 延迟加载、效率高

3.1.4 静态内部类(推荐)

  1. class Singleton6 {
  2. //构造器私有化
  3. private Singleton6() {
  4. }
  5. //写一个静态内部类,该类中有一个静态属性
  6. private static class SingletonInstance{
  7. private static final Singleton6 INSTANCE=new Singleton6();
  8. }
  9. //提供共有静态方法,直接返回
  10. public static Singleton6 getInstance() {
  11. return SingletonInstance.INSTANCE;
  12. }
  13. }

分析:

  • 采用了类装载机制来保证初始化实例只有一个线程
  • 静态内部类方式在Singleton类加载时并不会立即实例化,而是需要在实例化时调用getInstance()方法完成实例化
  • JVM保证了线程安全,并实现了懒加载

3.1.5 枚举(推荐)

  1. public class Test7 {
  2. public static void main(String[] args) {
  3. Singleton7 instance =Singleton7.INSTANCE;
  4. Singleton7 instance2 = Singleton7.INSTANCE;
  5. System.out.println(instance == instance2); //true 2次返回的实例是相同的
  6. }
  7. }
  8. //使用枚举实现单例
  9. enum Singleton7 {
  10. INSTANCE;
  11. public void sayOK() {
  12. System.out.println("OK!");
  13. }
  14. }

能避免多线程同步问题,还能防止反序列化问题

3.1.6 jdk源码分析

java中的RunTime就使用了单例模式中的饿汉式

  1. public class Runtime {
  2. private static Runtime currentRuntime = new Runtime();
  3. /**
  4. * Returns the runtime object associated with the current Java application.
  5. * Most of the methods of class <code>Runtime</code> are instance
  6. * methods and must be invoked with respect to the current runtime object.
  7. *
  8. * @return the <code>Runtime</code> object associated with the current
  9. * Java application.
  10. */
  11. public static Runtime getRuntime() {
  12. return currentRuntime;
  13. }
  14. /** Don't let anyone else instantiate this class */
  15. private Runtime() {}
  16. }

3.2 工厂模式

3.2.1 简单工厂模式

定义了一个创建对象的类,由这个类来封装实例化对象的行为

需求:客户需要不同口味的披萨

代码实现:

披萨基类:

  1. //披萨类
  2. public abstract class Pizza {
  3. protected String name;
  4. //准备制作,不同的披萨原材料不一样
  5. public abstract void prepare();
  6. //烘烤
  7. public void bake() {
  8. System.out.println(name + " baking;");
  9. }
  10. //切片
  11. public void cut() {
  12. System.out.println(name + " cutting;");
  13. }
  14. //打包
  15. public void box() {
  16. System.out.println(name + " boxing;");
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. }

披萨子类:略

简单工厂:

  1. //简单工厂类:当需要增加其他披萨业务时,只需要增加相应的披萨子类,并修改简单工厂的代码即可,并不会改动orderPizza中的代码
  2. public class SimpleFactory {
  3. Pizza pizza = null;
  4. //根据orderType返回Pizza对象
  5. public Pizza creatPizza(String orderType) {
  6. if (orderType.equals("greek")) {
  7. pizza = new GreekPizza();
  8. } else if (orderType.equals("cheese")) {
  9. pizza = new CheesePizza();
  10. }
  11. return pizza;
  12. }
  13. }

模拟客户端披萨订购:

  1. public class Test1 {
  2. //模拟客户端
  3. public static void main(String[] args) {
  4. new orderPizza(new SimpleFactory());
  5. }
  6. //订购披萨,实际业务中,订购披萨的orderPizza类会有多个,所以能不修改其代码就不修改
  7. static class orderPizza {
  8. //定义一个简单工厂对象
  9. SimpleFactory simpleFactory;
  10. //构造器
  11. public orderPizza(SimpleFactory simpleFactory) {
  12. setFactory(simpleFactory);
  13. }
  14. public void setFactory(SimpleFactory simpleFactory) {
  15. String orderType = "";
  16. this.simpleFactory = simpleFactory;
  17. orderType = getType();
  18. Pizza pizza = this.simpleFactory.creatPizza(orderType);
  19. if (pizza != null) {
  20. pizza.prepare();
  21. pizza.bake();
  22. pizza.cut();
  23. pizza.box();
  24. } else {
  25. System.out.println("订购失败!");
  26. }
  27. }
  28. //动态获取用户订购的披萨种类
  29. private String getType() {
  30. try {
  31. BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
  32. System.out.println("input pizza type:");
  33. String str = strin.readLine();
  34. return str;
  35. } catch (IOException e) {
  36. e.printStackTrace();
  37. return "";
  38. }
  39. }
  40. }
  41. }

3.2.2 工厂方法模式

定义一个创建对象的抽象方法,由子类决定要实例化的类,把对象的实例化推迟到子类实现

项目需求:客户在点披萨时不仅有不同口味需求,位置也不一样,如北京奶酪、北京胡椒、伦敦奶酪、伦敦胡椒

实现:将披萨项目的实例化功能(creatPizza方法)抽象为抽象方法

更改orderPizza:

  1. public abstract class OrderPizza {
  2. public OrderPizza() {
  3. Pizza pizza = null;
  4. String ordertype;
  5. do {
  6. ordertype = gettype();
  7. pizza = createPizza(ordertype);
  8. if (pizza != null) {
  9. pizza.prepare();
  10. pizza.bake();
  11. pizza.cut();
  12. pizza.box();
  13. } else {
  14. break;
  15. }
  16. } while (true);
  17. }
  18. abstract Pizza createPizza(String ordertype);
  19. }

抽象creatPizza方法:

  1. //先根据位置不同,调用对应的OrderPizza的继承子类,再判断口味
  2. public class BJOrderPizza extends OrderPizza {
  3. @Override
  4. Pizza createPizza(String type) {
  5. Pizza pizza = null;
  6. if (type.equals("cheese")) {
  7. //在子类中进行对象的实例化
  8. pizza = new BJCheesePizza();
  9. } else if (type.equals("pepper")) {
  10. pizza = new BJPepperPizza();
  11. }
  12. return pizza;
  13. }
  14. }
  15. public class LDOrderPizza extends OrderPizza {....}

3.2.3 抽象工厂

定义:定义了一个interface用于创建相关或有依赖的对象簇,不需指明具体的类,是简单工厂和工厂方法的结合

  1. //声明AbsFactory接口
  2. public interface AbsFactory {
  3. //让不同的工厂类来实现createPizza方法
  4. public Pizza createPizza(String type);
  5. }
  1. //北京披萨工厂
  2. public class BJFactory implements AbsFactory {
  3. @Override
  4. public Pizza createPizza(String type) {
  5. Pizza pizza = null;
  6. if (type.equals("cheese")) {
  7. pizza = new BJCheesePizza();
  8. } else if (type.equals("pepper")) {
  9. pizza = new BJPepperPizza();
  10. }
  11. return pizza;
  12. }
  13. }
  14. //伦敦披萨工厂
  15. public class LDFactory implements AbsFactory {...}

orderPizza:

  1. public class OrderPizza {
  2. AbsFactory factory;
  3. //构造器
  4. public OrderPizza(AbsFactory factory) {
  5. setFactory(factory);
  6. }
  7. public void setFactory(AbsFactory factory) {
  8. Pizza pizza = null;
  9. String ordertype;
  10. this.factory = factory;
  11. do {
  12. ordertype = gettype();
  13. pizza = factory.createPizza(ordertype);
  14. if (pizza != null) {
  15. pizza.prepare();
  16. pizza.bake();
  17. pizza.cut();
  18. pizza.box();
  19. } else {
  20. break;
  21. }
  22. } while (true);
  23. }
  24. }

3.2.4 jdk源码分析

  1. //java中的Calendar类使用了工厂模式
  2. Calendar calendar=Calendar.getInstance();
  1. public static Calendar getInstance(TimeZone zone,Locale aLocale)
  2. {
  3. return createCalendar(zone, aLocale);
  4. }
  5. private static Calendar createCalendar(TimeZone zone,Locale aLocale)
  6. {
  7. CalendarProvider provider =LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale).getCalendarProvider();
  8. if (provider != null) {
  9. try {
  10. return provider.getInstance(zone, aLocale);
  11. } catch (IllegalArgumentException iae) {
  12. // fall back to the default instantiation
  13. }
  14. }
  15. Calendar cal = null;
  16. if (aLocale.hasExtensions()) {
  17. String caltype = aLocale.getUnicodeLocaleType("ca");
  18. if (caltype != null) {
  19. switch (caltype) {
  20. case "buddhist":
  21. cal = new BuddhistCalendar(zone, aLocale);
  22. break;
  23. case "japanese":
  24. cal = new JapaneseImperialCalendar(zone, aLocale);
  25. break;
  26. case "gregory":
  27. cal = new GregorianCalendar(zone, aLocale);
  28. break;
  29. }
  30. }
  31. }
  32. return cal;
  33. }

3.3 原型模式

  • 用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象 对象.clone()
  • 原型模式是一种创建型模式,允许一个对象再创建另外一个可定制的对象,无需知道创建的细节

3.3.1 问题引出

需要创建和tom羊一模一样的5只羊

sheep类:

  1. public class Sheep {
  2. private String name;
  3. private Integer age;
  4. private String color;
  5. //set、get方法省略
  6. public Sheep(String name, Integer age, String color) {
  7. this.name = name;
  8. this.age = age;
  9. this.color = color;
  10. }
  11. @Override
  12. public String toString() {
  13. return "Sheep{" +
  14. "name='" + name + '\'' +
  15. ", age=" + age +
  16. ", color='" + color + '\'' +
  17. '}';
  18. }
  19. }

客户端:

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. Sheep sheep1 = new Sheep("tom", 3, "白色");
  4. Sheep sheep2 = new Sheep(sheep1.getName(),sheep1.getAge(),sheep1.getColor());
  5. Sheep sheep3 = new Sheep(sheep1.getName(),sheep1.getAge(),sheep1.getColor());
  6. Sheep sheep4 = new Sheep(sheep1.getName(),sheep1.getAge(),sheep1.getColor());
  7. }
  8. }

分析:

  • 简单易操作
  • 在创建新对象时,总是需要获取原始对象的属性,创建效率低、不灵活

3.3.2 使用原型模式改进

  1. //实现Cloneable接口,使用clone方法
  2. public class Sheep implements Cloneable{
  3. private String name;
  4. private Integer age;
  5. private String color;
  6. //...
  7. //克隆实例,使用默认clone方法
  8. @Override
  9. protected Object clone() throws CloneNotSupportedException {
  10. return super.clone();
  11. }
  12. }
  1. public class Test1 {
  2. public static void main(String[] args) throws CloneNotSupportedException {
  3. //原型模式完成克隆
  4. Sheep sheep = new Sheep("tom", 3, "白色");
  5. Sheep sheep2 = (Sheep) sheep.clone();
  6. Sheep sheep3 = (Sheep) sheep.clone();
  7. Sheep sheep4 = (Sheep) sheep.clone();
  8. Sheep sheep5 = (Sheep) sheep.clone();
  9. //克隆只是属性相同,并不是同一个对象
  10. System.out.println(sheep==sheep2); //false
  11. }
  12. }

spring的bean配置就使用了原型模式

  1. <bean id="id01" class="com.atguigu.spring.bean.Monster" scope="prototype"/>

3.3.3 浅拷贝

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象
  • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
  • 上面的克隆羊就是浅拷贝
  • 浅拷贝是使用默认的 clone()方法来实现

3.3.4 深拷贝

  • 复制对象的所有基本数据类型的成员变量值
  • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
  • 深拷贝实现方式1:重写clone方法来实现深拷贝
  • 深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)

4、结构性模式

4.1 装饰模式

定义:动态的将新功能附加到对象上,在对象功能扩展方面,它比基础更有弹性,装饰着模式也体现了开闭原则

4.1.1 问题引出

解决项目:

星巴克咖啡订单项目(咖啡馆):

  1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)

  2. 调料:Milk、Soy(豆浆)、Chocolate

  3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便

  4. 使用OO的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。

问题:Espress && Milk 就是单品咖啡+调料, 这种组合很多;会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

4.1.2 装饰者模式解决

Drink抽象类:

  1. public abstract class Drink {
  2. public String des; //描述
  3. private float price=0.0f; //价格
  4. //set、get方法省略
  5. //计算费用的抽象方法,由子类实现
  6. public abstract float cost();
  7. }

Coffee类:

  1. //Coffee基类
  2. public class Coffee extends Drink{
  3. public float cost() {
  4. return super.getPrice();
  5. }
  6. }
  7. //咖啡1 继承Coffee
  8. public class Coffee1 extends Coffee{
  9. public Coffee1(){
  10. setDes("咖啡1");
  11. setPrice(6.0f);
  12. }
  13. }
  14. //咖啡2 继承Coffee
  15. public class Coffee2 extends Coffee{
  16. public Coffee2(){
  17. setDes("咖啡2");
  18. setPrice(5.0f);
  19. }
  20. }

装饰者:

  1. //装饰者模式的装饰者 给咖啡加调料
  2. public class Decorator extends Drink {
  3. private Drink obj;
  4. //构造器
  5. public Decorator(Drink obj){
  6. this.obj=obj;
  7. }
  8. //getPrice()获得调料的价格,再加上原咖啡的价格
  9. @Override
  10. public float cost() {
  11. return super.getPrice()+obj.cost();
  12. }
  13. @Override
  14. public String getDes() {
  15. //调料的描述、价格、原咖啡的描述
  16. return super.des+":"+super.getPrice()+"--"+obj.getDes();
  17. }
  18. }
  19. //巧克力调料
  20. public class Chocolate extends Decorator {
  21. public Chocolate(Drink obj) {
  22. super(obj);
  23. setDes("巧克力");
  24. setPrice(1.0f);
  25. }
  26. }
  27. //牛奶调料
  28. public class Milk extends Decorator{
  29. public Milk(Drink obj) {
  30. super(obj);
  31. setDes("牛奶");
  32. setPrice(2.0f);
  33. }
  34. }

测试:

  1. //模拟客户端下订单
  2. public class Test {
  3. public static void main(String[] args) {
  4. //装饰者模式下订单:coffee1+2份巧克力+一份牛奶
  5. //1. 点coffee1
  6. Drink order=new Coffee1();
  7. //2.加一份牛奶
  8. order=new Milk(order);
  9. System.out.println(order.getDes()+" 总价:"+order.cost());
  10. //3.加两份巧克力
  11. order=new Chocolate(order);
  12. order=new Chocolate(order);
  13. System.out.println(order.getDes()+" 总价:"+order.cost());
  14. }
  15. }
  16. //结果:
  17. /*
  18. 牛奶:2.0--咖啡1 总价:8.0
  19. 巧克力:1.0--巧克力:1.0--牛奶:2.0--咖啡1 总价:10.0
  20. */

4.1.3 jdk源码分析

Java的IO结构,FilterInputStream就是一个装饰者

  1. public abstract class InputStream implements Closeable {} //InputStream是一个抽象类,即Component 类似Drink
  2. class FileInputStream extends InputStream{} //FileInputStream是InputStream是一个子类,类似coffee1、coffee2
  3. public class FilterInputStream extends InputStream {} //是InputStream是一个抽象类的子类,一个装饰者类 类似Decorator
  4. class DataInputStream extends FilterInputStream implements DataInput {} //FilterInputStream 子类,具体的修饰者,类似Milk、Chocolate
  5. protected volatile InputStream in; //被装饰的对象

4.2 代理模式

  • 为一个对象提供一个替身,以控制这个对象的访问。通过代理对象访问目标对象
  • 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
  • 主要有三种静态代理动态代理 (JDK代理、接口代理)、 Cglib代理(可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴。
  • 在AOP编程中如何选择代理模式:

    • 目标对象需要实现接口,用JDK代理
    • 目标对象不需要实现接口,用Cglib代理

4.2.1 静态代理

  • 在不修改目标对象的前提下,能通过代理对象对目标功能扩展
  • 缺点:代理对象和目标对象需要实现一样的接口,所以会有很多代理类,一但接口方法增加,需要维护的代码很多
  1. //业务:代理角色代理老师授课
  2. //被代理对象 真实角色的接口
  3. public interface ITeacherDao {
  4. void teach();
  5. }
  6. //被代理对象的实现类
  7. public class TeacherDao implements ITeacherDao{
  8. @Override
  9. public void teach() {
  10. System.out.println("老师授课!");
  11. }
  12. }
  13. //静态代理类 代理角色
  14. public class TeacherDaoProxy implements ITeacherDao {
  15. //被代理的对象 目标对象
  16. private ITeacherDao target;
  17. public TeacherDaoProxy(ITeacherDao target) {
  18. this.target = target;
  19. }
  20. //代理对象代理真实对象执行动作
  21. @Override
  22. public void teach() {
  23. System.out.println("代理开始");
  24. target.teach();
  25. System.out.println("代理结束");
  26. }
  27. }
  28. //测试
  29. //模拟客户端调度
  30. public class Test {
  31. public static void main(String[] args) {
  32. //创建目标对象TeacherDao和代理对象TeacherDaoProxy
  33. TeacherDaoProxy tp=new TeacherDaoProxy(new TeacherDao());
  34. //执行代理对象的方法,但本质还是执行的被代理对象的teach方法
  35. tp.teach();
  36. }
  37. }

4.2.2 动态代理

  • 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
  • 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
  • 动态代理也叫做:JDK代理、接口代理
  1. //被代理对象 真实角色的接口
  2. public interface ITeacherDao {
  3. void teach();
  4. }
  5. //被代理对象的实现类
  6. public class TeacherDao implements ITeacherDao{
  7. @Override
  8. public void teach() {
  9. System.out.println("老师授课!");
  10. }
  11. }
  12. //动态代理对象
  13. public class ProxyFactory {
  14. //维护目标对象
  15. private Object target;
  16. //构造器
  17. public ProxyFactory(Object target) {
  18. this.target = target;
  19. }
  20. //给目标对象生成一个代理对象
  21. /*
  22. public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
  23. newProxyInstance的三个参数:
  24. ClassLoader loader:指定当前目标对象的类加载器
  25. Class<?>[] interfaces:目标对象实现的接口类型,使用泛型确认类型
  26. InvocationHandler h:事件处理,执行目标对象方法时,触发
  27. */
  28. public Object getProxyInstance() {
  29. return Proxy.newProxyInstance(target.getClass().getClassLoader(),
  30. target.getClass().getInterfaces(), new InvocationHandler() {
  31. @Override
  32. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  33. System.out.println("JDK代理开始!");
  34. Object invoke = method.invoke(target, args);
  35. System.out.println("JDK代理结束");
  36. return invoke;
  37. }
  38. });
  39. }
  40. }
  41. //测试:
  42. //模拟动态代理客户端
  43. public class Test2 {
  44. public static void main(String[] args) {
  45. //目标对象
  46. TeacherDao target=new TeacherDao();
  47. //给目标对象创建代理对象,强转类型
  48. ITeacherDao proxyFactory = (ITeacherDao) new ProxyFactory(target).getProxyInstance();
  49. proxyFactory.teach();
  50. }
  51. }

4.4.3 Cglib代理

  • 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理
  • Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, Cglib代理也归属到动态代理。
  • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

实现步骤:

  • 需要引入cglib的jar文件

  • 在内存中动态构建子类,注意代理的类不能为final,否则报错java.lang.IllegalArgumentException:

  • 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

  1. //被代理对象 目标对象
  2. public class TeacherDao {
  3. public void teach() {
  4. System.out.println("cglib代理 老师授课!");
  5. }
  6. }
  7. //cglib代理工厂
  8. public class ProxyFactory implements MethodInterceptor {
  9. //维护一个目标对象
  10. private Object target;
  11. //构造器,传入一个被代理的对象
  12. public ProxyFactory(Object target) {
  13. this.target = target;
  14. }
  15. //返回一个代理对象: 是 target 对象的代理对象
  16. public Object getProxyInstance() {
  17. //1. 创建一个工具类
  18. Enhancer enhancer = new Enhancer();
  19. //2. 设置父类
  20. enhancer.setSuperclass(target.getClass());
  21. //3. 设置回调函数
  22. enhancer.setCallback(this);
  23. //4. 创建子类对象,即代理对象
  24. return enhancer.create();
  25. }
  26. //重写 intercept 方法,会调用目标对象的方法
  27. @Override
  28. public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
  29. // TODO Auto-generated method stub
  30. System.out.println("Cglib代理模式 开始");
  31. Object returnVal = method.invoke(target, args);
  32. System.out.println("Cglib代理模 结束");
  33. return returnVal;
  34. }
  35. }
  36. //模拟客户端
  37. public class Test3 {
  38. public static void main(String[] args) {
  39. //创建目标对象
  40. TeacherDao target = new TeacherDao();
  41. //获取到代理对象,并且将目标对象传递给代理对象
  42. TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();
  43. //执行代理对象的方法,触发intecept 方法,从而实现 对目标对象的调用
  44. String res = proxyInstance.teach();
  45. System.out.println("res=" + res);
  46. }
  47. }

5、行为型模式

5.1 观察者模式

5.1.1 问题引出

项目需求:

  • 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)
  • 需要设计开放型API,便于其他第三方也能接入气象站获取数据
  • 提供温度、气压和湿度的接口
  • 测量数据更新时,要能实时的通知给第三方

普通实现方案:

  1. //天气信息的主类,当有数据更新时,就调用currentConditions的update方法
  2. public class WeatherData {
  3. private float temperature;
  4. private float pressure;
  5. private float humidity;
  6. //信息接入方
  7. private CurrentConditions currentConditions;
  8. public WeatherData(CurrentConditions currentConditions) {
  9. this.currentConditions = currentConditions;
  10. }
  11. //get方法省略
  12. public void dataChange() {
  13. //接入方1
  14. currentConditions.update(getTemperature(), getPressure(), getHumidity());
  15. //接入方2
  16. //接入方3
  17. }
  18. public void setData(float temperature, float pressure, float humidity) {
  19. this.temperature = temperature;
  20. this.pressure = pressure;
  21. this.humidity = humidity;
  22. dataChange();
  23. }
  24. }
  25. //第三方公告板:接入方显示当前天气情况
  26. public class CurrentConditions {
  27. //温度、气压、湿度
  28. private float temperature;
  29. private float pressure;
  30. private float humidity;
  31. //更新天气
  32. public void update(float temperature, float pressure, float humidity) {
  33. this.temperature = temperature;
  34. this.pressure = pressure;
  35. this.humidity = humidity;
  36. display();
  37. }
  38. //展示
  39. public void display() {
  40. System.out.println("***Today mTemperature: " + temperature + "***");
  41. System.out.println("***Today mPressure: " + pressure + "***");
  42. System.out.println("***Today mHumidity: " + humidity + "***");
  43. }
  44. }
  45. //测试
  46. //模拟客户端
  47. public class Test1 {
  48. public static void main(String[] args) {
  49. CurrentConditions currentConditions = new CurrentConditions();
  50. WeatherData weatherData = new WeatherData(currentConditions);
  51. weatherData.setData(30, 150, 40);
  52. }
  53. }

分析:

  • 其他第三方接入气象站获取数据的问题
  • 无法在运行时动态的添加其他第三方网站
  • 违反ocp原则:每次修改时都需要修改WeatherData类中的dataChange方法,且还需创建一个对应的第三方公告板

5.1.2 使用观察者模式解决

第三方接入口不在需要依赖weatherDate,当增加第三方时就不用修改weatherData中的代码,直接在Observer下增加对应实现类就行了

  1. //接口 WeatherData实现
  2. public interface Subject {
  3. public void register(Observer observer);
  4. public void removeObserver(Observer observer);
  5. public void notifyObservers();
  6. }
  7. //天气信息处理类,含观察者集合,当数据更新时,直接通知接入方更新信息
  8. public class WeatherData implements Subject {
  9. private float temperature;
  10. private float pressure;
  11. private float humidity;
  12. //信息接入方 含多个
  13. private ArrayList<Observer> observers;
  14. public WeatherData() {
  15. observers=new ArrayList<>();
  16. }
  17. public void dataChange(){
  18. notifyObservers();
  19. }
  20. public void setData(float temperature, float pressure, float humidity) {
  21. this.temperature = temperature;
  22. this.pressure = pressure;
  23. this.humidity = humidity;
  24. dataChange();
  25. }
  26. public void register(Observer observer) {
  27. //注册一个观察者
  28. observers.add(observer);
  29. }
  30. public void removeObserver(Observer observer) {
  31. //移除一个观察者
  32. observers.remove(observer);
  33. }
  34. public void notifyObservers() {
  35. for (Observer tmp : observers) {
  36. tmp.update(this.temperature,this.pressure,this.humidity);
  37. }
  38. }
  39. }
  1. //观察者接口
  2. public interface Observer {
  3. public void update(float temperature,float pressure,float humidity);
  4. }
  5. //CurrentCondition观察者 实现Observer接口
  6. public class CurrentCondition implements Observer{
  7. //温度、气压、湿度
  8. private float temperature;
  9. private float pressure;
  10. private float humidity;
  11. //更新天气
  12. public void update(float temperature, float pressure, float humidity) {
  13. this.temperature = temperature;
  14. this.pressure = pressure;
  15. this.humidity = humidity;
  16. display();
  17. }
  18. //展示
  19. public void display() {
  20. System.out.println("***CurrentCondition mTemperature: " + temperature + "***");
  21. System.out.println("***CurrentCondition mPressure: " + pressure + "***");
  22. System.out.println("***CurrentCondition mHumidity: " + humidity + "***");
  23. }
  24. }
  25. //其他观察者...
  1. //测试:
  2. //模拟观察者模式客户端
  3. public class Test2 {
  4. public static void main(String[] args) {
  5. //创建weatherData消息中心
  6. WeatherData weatherData = new WeatherData();
  7. //创建currentCondition观察者
  8. CurrentCondition currentCondition = new CurrentCondition();
  9. //注册currentCondition观察者
  10. weatherData.register(currentCondition);
  11. weatherData.setData(10, 200, 10);
  12. }
  13. }

5.1.3 jdk源码分析

Jdk的Observable类就使用了观察者模式

  1. //Observable类似subject,只是一个是类一个是接口
  2. public class Observable {
  3. private boolean changed = false;
  4. private Vector<Observer> obs;
  5. /** Construct an Observable with zero Observers. */
  6. public Observable() {
  7. obs = new Vector<>();
  8. }
  9. public synchronized void addObserver(Observer o) {
  10. if (o == null)
  11. throw new NullPointerException();
  12. if (!obs.contains(o)) {
  13. obs.addElement(o);
  14. }
  15. }
  16. public synchronized void deleteObserver(Observer o) {
  17. obs.removeElement(o);
  18. }
  19. public void notifyObservers() {
  20. notifyObservers(null);
  21. }
  22. public void notifyObservers(Object arg) {
  23. Object[] arrLocal;
  24. synchronized (this) {
  25. if (!changed)
  26. return;
  27. arrLocal = obs.toArray();
  28. clearChanged();
  29. }
  30. for (int i = arrLocal.length-1; i>=0; i--)
  31. ((Observer)arrLocal[i]).update(this, arg);
  32. }
  33. protected synchronized void setChanged() {
  34. changed = true;
  35. }