前言

本文是参照 尚硅谷黑马程序员以及cyc2018 记录的个人学习笔记,仅供记录,不确保严谨性,部分实例代码是根据自己的理解抽象了代码,若不理解,更多具象实例可以参考其他网上实例资料,欢迎讨论学习。


设计模式

在软件工程中,设计模式是对软件设计中普遍存在(反复出现)的各种问题所提出的解决方案。

设计模式的目的

设计模式是为了让程序具有更好的

  • 代码重用性:即相同功能的代码不用多次编写
  • 可读性:即变成规范性,便于其他程序员的阅读和理解
  • 可扩展性/可维护:即需要增加新功能时,非常方便
  • 可靠性:即增加新功能后,对原功能没有影响
  • 使程序呈现高内聚,低耦合的特性

设计模式的七大原则

程序员在编程时,应当遵守的原则,也是各种设计模式的基础。分为:单一职责原则、接口隔离原则、依赖倒置原则、里氏替换原则、开闭原则、迪米特法则和合成复用原则

核心思想:

  1. 找出应用中可能需要变化之处,把它们独立除了,不要和那些不需要变化的代码混在一起
  2. 针对接口编程,而不是针对实现编程
  3. 为了交互对象之间的松耦合设计而努力

单一职责原则

  • 基本介绍

    ​ 对类来说,一个类应该只负责一项职责,如类A负责两个不同的职责1、职责2。当职责41需求变更而改变A时,可能造成职责2执行错误,使用要将类A分解为类A1、类A2。

  • 案例引入

    public class SingleResponsibility1 {public static void main(String[] args) {Vehicle vehicle = new Vehicle();vehicle.run("摩托车");vehicle.run("汽车");vehicle.run("飞机");}
    }
    // 交通工具类
    class Vehicle {public void run(String vehicle) {System.out.println(vehicle + " 在公路上运行....");}
    }
    

    该方法违反了单一职责原则,解决的方案非常的简单,根据交通工具运行方法不同,分解成不同类即可

    public class SingleResponsibility2 {public static void main(String[] args) {RoadVehicle roadVehicle = new RoadVehicle();roadVehicle.run("摩托车");roadVehicle.run("汽车");AirVehicle airVehicle = new AirVehicle();airVehicle.run("飞机");}
    }
    class RoadVehicle {public void run(String vehicle) {System.out.println(vehicle + "公路运行");}
    }
    class AirVehicle {public void run(String vehicle) {System.out.println(vehicle + "天空运行");}
    }
    class WaterVehicle {public void run(String vehicle) {System.out.println(vehicle + "水中运行");}
    }
    

    相比方式一,遵循了单一职责原则,但要修改主程序,改动很大。

    public class SingleResponsibility3 {public static void main(String[] args) {Vehicle2 vehicle2  = new Vehicle2();vehicle2.run("汽车");vehicle2.runWater("轮船");vehicle2.runAir("飞机");}
    }
    class Vehicle2 {public void run(String vehicle) {System.out.println(vehicle + " 在公路上运行....");}public void runAir(String vehicle) {System.out.println(vehicle + " 在天空上运行....");}public void runWater(String vehicle) {System.out.println(vehicle + " 在水中行....");}
    }
    

    这种修改方法没有对原来的类做大的修改,只是增加方法,虽然没有在类这个级别上遵守单一职责原则,但是在方法级别上,仍然是遵守单一职责

  • 注意事项和细节

    • 降低类的复杂度,一个类只负责一个职责
    • 提高类的可读性、可维护性
    • 降低变更引起的风险
    • 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类方法足够少,可以在方法级别保持单一职责原则

接口隔离原则

  • 基本介绍

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

  • 案例引入

    interface Interface1 {void operation1();void operation2();void operation3();void operation4();void operation5();
    }
    class B implements Interface1 {public void operation1() {System.out.println("B 实现了 operation1");}public void operation2() {System.out.println("B 实现了 operation2");}public void operation3() {System.out.println("B 实现了 operation3");}public void operation4() {System.out.println("B 实现了 operation4");}public void operation5() {System.out.println("B 实现了 operation5");}
    }
    class D implements Interface1 {public void operation1() {System.out.println("D 实现了 operation1");}public void operation2() {System.out.println("D 实现了 operation2");}public void operation3() {System.out.println("D 实现了 operation3");}public void operation4() {System.out.println("D 实现了 operation4");}public void operation5() {System.out.println("D 实现了 operation5");}
    }
    class A { //A 类通过接口Interface1 依赖(使用) B类,但是只会用到1,2,3方法public void depend1(Interface1 i) {i.operation1();}public void depend2(Interface1 i) {i.operation2();}public void depend3(Interface1 i) {i.operation3();}
    }
    class C { //C 类通过接口Interface1 依赖(使用) D类,但是只会用到1,4,5方法public void depend1(Interface1 i) {i.operation1();}public void depend4(Interface1 i) {i.operation4();}public void depend5(Interface1 i) {i.operation5();}
    }
    

    类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口 Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不需要的方法。

    按接口隔离原则,应该将接口分解成多个接口,分别让A、C去与所需接口建立联系。改进如下:

  public class Segregation1 {public static void main(String[] args) {A a = new A();a.depend1(new B()); // A类通过接口去依赖B类a.depend2(new B());a.depend3(new B());C c = new C();c.depend1(new D()); // C类通过接口去依赖(使用)D类c.depend4(new D());c.depend5(new D());}}// 接口1interface Interface1 {void operation1();}// 接口2interface Interface2 {void operation2();void operation3();}// 接口3interface Interface3 {void operation4();void operation5();}class B implements Interface1, Interface2 {public void operation1() {System.out.println("B 实现了 operation1");}public void operation2() {System.out.println("B 实现了 operation2");}public void operation3() {System.out.println("B 实现了 operation3");}}class D implements Interface1, Interface3 {public void operation1() {System.out.println("D 实现了 operation1");}public void operation4() {System.out.println("D 实现了 operation4");}public void operation5() {System.out.println("D 实现了 operation5");}}class A { // A 类通过接口Interface1,Interface2 依赖(使用) B类,但是只会用到1,2,3方法public void depend1(Interface1 i) {i.operation1();}public void depend2(Interface2 i) {i.operation2();}public void depend3(Interface2 i) {i.operation3();}}class C { // C 类通过接口Interface1,Interface3 依赖(使用) D类,但是只会用到1,4,5方法public void depend1(Interface1 i) {i.operation1();}public void depend4(Interface3 i) {i.operation4();}public void depend5(Interface3 i) {i.operation5();}}

依赖倒转/倒置原则

  • 基本介绍

    • 高层模块不应该依赖底层模块,二者都应该依赖其抽象
    • 抽象不应该依赖细节,细节应该依赖抽象
    • 依赖倒置的中心思想是面向接口编程
    • 设计理念是:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的框架比以细节为基础的架构要稳定的多。在Java中,抽象是指接口或抽象类,细节是指具体实现类
    • 使用接口或抽象类的目的是规定好规范而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
  • 案例引入

    public class DependencyInversion {public static void main(String[] args) {Person person = new Person();person.receive(new Email());}
    }
    class Email {public String getInfo() {return "电子邮件信息: hello,world";}
    }
    class Person {public void receive(Email email ) {System.out.println(email.getInfo());}
    }
    

    对于person获取对象太过单调,只能获Email类对象,所以应该引入一个抽象的接口IReceiver, 表示接收者, 这样Person类与接口IReceiver发生依赖

    public class DependencyInversion {public static void main(String[] args) {//客户端无需改变Person person = new Person();person.receive(new Email());person.receive(new WeiXin());}
    }
    //定义接口
    interface IReceiver {public String getInfo();
    }
    class Email implements IReceiver {public String getInfo() {return "电子邮件信息: hello,world";}
    }
    //增加微信
    class WeiXin implements IReceiver {public String getInfo() {return "微信信息: hello,ok";}
    }
    class Person {//这里我们是对接口的依赖public void receive(IReceiver receiver ) {System.out.println(receiver.getInfo());}
    }
    
  • 关系传递的三种方式

    • 接口传递

      // 开关的接口
      interface IOpenAndClose {public void open(ITV tv); //抽象方法,接收接口
      }
      interface ITV { //ITV接口public void play();
      }class ChangHong implements ITV {public void play() {System.out.println("长虹电视机,打开");}
      }
      // 实现接口
      class OpenAndClose implements IOpenAndClose{public void open(ITV tv){tv.play();}
      }==================测试方法==========================
      ChangHong changHong = new ChangHong();
      OpenAndClose openAndClose = new OpenAndClose();
      openAndClose.open(changHong);
      
    • 构造器传递

      interface IOpenAndClose {public void open(); //抽象方法
      }
      interface ITV { //ITV接口public void play();
      }
      class OpenAndClose implements IOpenAndClose{public ITV tv; //成员public OpenAndClose(ITV tv){ //构造器this.tv = tv;}public void open(){this.tv.play();}
      }==================测试方法==========================
      ChangHong changHong = new ChangHong();
      OpenAndClose openAndClose = new OpenAndClose(changHong);
      openAndClose.open();
      
    • setter方法传递

      interface IOpenAndClose {public void open(); // 抽象方法public void setTv(ITV tv);
      }
      interface ITV { // ITV接口public void play();
      }
      class OpenAndClose implements IOpenAndClose {private ITV tv;public void setTv(ITV tv) {this.tv = tv;}public void open() {this.tv.play();}
      }
      class ChangHong implements ITV {public void play() {System.out.println("长虹电视机,打开");}
      }==================测试方法==========================
      ChangHong changHong = new ChangHong();
      OpenAndClose openAndClose = new OpenAndClose();
      openAndClose.setTv(changHong);
      openAndClose.open();
      
  • 注意事项和细节

    • 低层模块尽量都要有抽象类或接口,或两者都有,程序稳定性更好
    • 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
    • 继承时遵循里氏替换原则

里氏替换原则

  • 基本介绍

    • 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。所有引用基类的地方必须能透明地使用其子类的对象
    • 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
    • 继承实际上让两个类耦合性增强了,在适当情况下,可以通过聚合,组合,依赖来解决问题
  • 案例引入

    public class Liskov {public static void main(String[] args) {A a = new A();System.out.println("11-3=" + a.func1(11, 3));System.out.println("1-8=" + a.func1(1, 8));System.out.println("-----------------------");B b = new B();System.out.println("11-3=" + b.func1(11, 3));//这里本意是求出11-3System.out.println("1-8=" + b.func1(1, 8));// 1-8System.out.println("11+3+9=" + b.func2(11, 3));}
    }
    // A类
    class A {// 返回两个数的差public int func1(int num1, int num2) {return num1 - num2;}
    }
    // B类继承了A
    // 增加了一个新功能:完成两个数相加,然后和9求和
    class B extends A {//这里,重写了A类的方法, 可能是无意识public int func1(int a, int b) {return a + b;}public int func2(int a, int b) {return func1(a, b) + 9;}
    }
    

    调用B方法时,想使用父类A的求差,可无意识的把A类的方法给重写覆盖了,所以导致出错。可以通过原来的父类和子类都继承一个更通俗的基类,去除原有的继承关系,采用依赖、聚合、组合等关系代替来解决。这就是里氏替换

    public class Liskov {public static void main(String[] args) {A a = new A();System.out.println("11-3=" + a.func1(11, 3));System.out.println("1-8=" + a.func1(1, 8));System.out.println("-----------------------");B b = new B();//因为B类不再继承A类,因此调用者,不会再func1是求减法//调用完成的功能就会很明确System.out.println("11+3=" + b.func1(11, 3));//这里本意是求出11+3System.out.println("1+8=" + b.func1(1, 8));// 1+8System.out.println("11+3+9=" + b.func2(11, 3));//使用组合仍然可以使用到A类相关方法System.out.println("11-3=" + b.func3(11, 3));// 这里本意是求出11-3}
    }
    //创建一个更加基础的基类
    class Base {//把更加基础的方法和成员写到Base类
    }
    // A类
    class A extends Base {// 返回两个数的差public int func1(int num1, int num2) {return num1 - num2;}
    }
    // B类继承了A
    // 增加了一个新功能:完成两个数相加,然后和9求和
    class B extends Base {//如果B需要使用A类的方法,使用组合关系private A a = new A();//这里,重写了A类的方法, 可能是无意识public int func1(int a, int b) {return a + b;}public int func2(int a, int b) {return func1(a, b) + 9;}//我们仍然想使用A的方法public int func3(int a, int b) {return this.a.func1(a, b);}
    }
    

开闭原则

  • 基本介绍

    • 编程中最基础、最重要的设计原则
    • 一个软件实体 如类,模块和函数应该对扩展开放【设计方】,对修改关闭【使用方】。用抽象构建框架,用实现扩展细节
    • 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化
    • 编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则
  • 案件引入

public class Ocp {public static void main(String[] args) {GraphicEditor graphicEditor = new GraphicEditor();graphicEditor.drawShape(new Rectangle());graphicEditor.drawShape(new Circle());graphicEditor.drawShape(new Triangle());}
}
//这是一个用于绘图的类 [使用方]
class GraphicEditor {//接收Shape对象,然后根据type,来绘制不同的图形public void drawShape(Shape s) {if (s.m_type == 1)drawRectangle(s);else if (s.m_type == 2)drawCircle(s);else if (s.m_type == 3)drawTriangle(s);}//绘制矩形public void drawRectangle(Shape r) {System.out.println(" 绘制矩形 ");}//绘制圆形public void drawCircle(Shape r) {System.out.println(" 绘制圆形 ");}//绘制三角形public void drawTriangle(Shape r) {System.out.println(" 绘制三角形 ");}
}
//Shape类,基类
class Shape {int m_type;
}
class Rectangle extends Shape {Rectangle() {super.m_type = 1;}
}
class Circle extends Shape {Circle() {super.m_type = 2;}
}
//新增画三角形
class Triangle extends Shape {Triangle() {super.m_type = 3;}
}

以上代码违反了设计模式的ocp(开闭)原则。当我们给类新增功能时,应该不修改代码或尽量少修改代码。但上述修改代码过多,所以应该把创建Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现。

public class Ocp {public static void main(String[] args) {GraphicEditor graphicEditor = new GraphicEditor();graphicEditor.drawShape(new Rectangle());graphicEditor.drawShape(new Circle());graphicEditor.drawShape(new Triangle());graphicEditor.drawShape(new OtherGraphic());}
}
//这是一个用于绘图的类 [使用方]
class GraphicEditor {//接收Shape对象,调用draw方法public void drawShape(Shape s) {s.draw();}
}
//Shape类,基类
abstract class Shape {int m_type;public abstract void draw();//抽象方法
}
class Rectangle extends Shape {Rectangle() {super.m_type = 1;}public void draw() {System.out.println(" 绘制矩形 ");}
}
class Circle extends Shape {Circle() {super.m_type = 2;}public void draw() {System.out.println(" 绘制圆形 ");}
}
class Triangle extends Shape {Triangle() {super.m_type = 3;}public void draw() {System.out.println(" 绘制三角形 ");}
}
class OtherGraphic extends Shape {OtherGraphic() {super.m_type = 4;}public void draw() {System.out.println(" 绘制其它图形 ");}
}

迪米特法则

  • 基本介绍

    • 一个对象应该对其他对象保持最少的了解
    • 迪米特法则又称最少知道法则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么负责,都尽量将逻辑封装在类的内部,对外除了public方法之外,不提供任何信息
    • 出现在成员变量、方法参数、方法返回值中的类为朋友类
  • 案例引用

    //客户端
    public class Demeter1 {public static void main(String[] args) {//创建了一个 SchoolManager对象SchoolManager schoolManager = new SchoolManager();//输出学院的员工id 和学校总部的员工信息schoolManager.printAllEmployee(new CollegeManager());}
    }
    //学校总部员工类
    class Employee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
    }
    //学院的员工类
    class CollegeEmployee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
    }
    //管理学院员工的管理类
    class CollegeManager {//返回学院的所有员工public List<CollegeEmployee> getAllEmployee() {List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();for (int i = 0; i < 10; i++) { //增加了10个员工到 list,模拟有10个学院员工CollegeEmployee emp = new CollegeEmployee();emp.setId("学院员工id= " + i);list.add(emp);}return list;}
    }
    //学校管理类
    class SchoolManager {//返回学校总部的员工public List<Employee> getAllEmployee() {List<Employee> list = new ArrayList<Employee>();for (int i = 0; i < 5; i++) { //增加了5个员工到 list 模拟了共有5个学校员工Employee emp = new Employee();emp.setId("学校总部员工id= " + i);list.add(emp);}return list;}//该方法完成输出学校总部和学院员工信息(id)void printAllEmployee(CollegeManager sub) {//获取到学院员工List<CollegeEmployee> list1 = sub.getAllEmployee();System.out.println("------------学院员工------------");for (CollegeEmployee e : list1) {System.out.println(e.getId());}//获取到学校总部员工List<Employee> list2 = this.getAllEmployee();System.out.println("------------学校总部员工------------");for (Employee e : list2) {System.out.println(e.getId());}}
    }
    

    对于 SchoolManager 类的朋友有 EmployeeCollegeManager ,但在printAllEmployee()方法中,CollegeEmployee不是朋友类而是一个陌生类,是以局部变量的方式出现在SchoolManager中,这样违背了迪米特法则。

    将输出 学院的员工 的方法封装到CollegeManager

    //客户端
    public class Demeter1 {public static void main(String[] args) {System.out.println("使用迪米特法则的改进::");//创建了一个 SchoolManager 对象SchoolManager schoolManager = new SchoolManager();//输出学院的员工id 和  学校总部的员工信息schoolManager.printAllEmployee(new CollegeManager());}
    }
    //学校总部员工类
    class Employee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
    }
    //学院的员工类
    class CollegeEmployee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
    }
    //管理学院员工的管理类
    class CollegeManager {//返回学院的所有员工public List<CollegeEmployee> getAllEmployee() {List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 listCollegeEmployee emp = new CollegeEmployee();emp.setId("学院员工id= " + i);list.add(emp);}return list;}//输出学院员工的信息public void printEmployee() {//获取到学院员工List<CollegeEmployee> list1 = getAllEmployee();System.out.println("------------学院员工------------");for (CollegeEmployee e : list1) {System.out.println(e.getId());}}
    }
    //学校管理类
    class SchoolManager {//返回学校总部的员工public List<Employee> getAllEmployee() {List<Employee> list = new ArrayList<Employee>();for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 listEmployee emp = new Employee();emp.setId("学校总部员工id= " + i);list.add(emp);}return list;}//该方法完成输出学校总部和学院员工信息(id)void printAllEmployee(CollegeManager sub) {//将输出学院的员工方法,封装到CollegeManagersub.printEmployee();//获取到学校总部员工List<Employee> list2 = this.getAllEmployee();System.out.println("------------学校总部员工------------");for (Employee e : list2) {System.out.println(e.getId());}}
    }
    
  • 注意事项和细节

    迪米特法则的核心是降低类之间的耦合。但是注意,由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类之间耦合关系,并不是要求完全没有依赖关系

合成复用原则

  • 基本介绍

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

设计模式的分类

(根据《设计模式》,将设计模式分为三种类型共23种)

  1. 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式

  2. 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

  3. 行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式

    创建型模式 --> 对象怎么来

    结构型模式 --> 对象和谁有关

    行为型模式 --> 对象与对象在干嘛

单例模式

采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态的)。

单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高性能。如果想实例化一个单例类,应该调用它的获取对象的方法而不是直接new出。

单例模式应用在:需要频繁的进行创建和销毁对象、创建对象时耗时过多或耗费资源过多,但又经常用到的对象类、工具对象、频繁访问数据库或文件的对象。

  • 实现步骤

    1. 构造器私有化(防止 new出)

    2. 类的内部创建对象,并用静态方法保存

    3. 向外暴露一个静态的公共方法 getInstance()

  • JDK底层使用该设计模式:

    • java.lang.Runtime#getRuntime()
    • java.awt.Desktop#getDesktop()
    • java.lang.System#getSecurityManager()

饿汉式

  • 代码实例

    //饿汉式(静态常量)
    class Singleton { //1. 构造器私有化, 外部不能newprivate Singleton() {}//2.本类内部创建对象实例private final static Singleton instance = new Singleton();//或共有化直接调用//public final static Singleton INSTANCE = new Singleton();//3. 提供一个公有的静态方法,返回实例对象public static Singleton getInstance() {return instance;}
    }
    //饿汉式(静态代码块)
    //2.本类内部创建对象实例。常用于创建实例时,需要其他的属性参数,可在静态代码块中一同加载private  static Singleton instance; static { // 在静态代码块中,创建单例对象instance = new Singleton();}
    
    public class SingletonTest01 {public static void main(String[] args) {//测试Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2); // trueSystem.out.println("instance.hashCode=" + instance.hashCode());System.out.println("instance2.hashCode=" + instance2.hashCode());}
    }
    
  • 优缺点分析

    • 优点:在类装载的时候就完成了实例化,避免了线程同步问题
    • 缺点:在类装载的时候完成实例化,没达到懒加载的效果,可能会造成内存浪费

懒汉式

  • 代码实例

    //懒汉式(线程不安全)
    class Singleton {private static Singleton instance;private Singleton() {}//提供一个静态的公有方法,当使用到该方法时,才去创建new instance,即懒汉式public static Singleton getInstance() {if(instance == null) {instance = new Singleton();}return instance;}
    }
    
  • 优缺点分析

    起到了懒加载效果,但只能在单线程下使用。若在多线程下,一个线程进入了if判断语句块,还没来得及往下执行,另一个线程也进入了这个判断语句,就会产生多个实例

  • 线程安全解决

    getInstance()方法加上synchronized修饰,使创建时启用同步处理代码,解决同步问题,但此方法效率太低:每个线程获取实例时都要调用该方法,每次都需要同步,所以推荐使用同步代码块的方式实现

    if (instance == null) {synchronized (Singleton.class) {instance = new Singleton();}
    }
    

    考虑上述的实现,也就是只使用了一个 if 语句。在 instance== null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 uniqueInstance = new Singleton(); 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句:第一个 if 语句用来避免 instance已经被实例化之后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出现 instance== null 时两个线程同时进行实例化操作。

双重检查

  • 代码实例

    public class SingletonTest06 {public static void main(String[] args) {System.out.println("双重检查");Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2); // trueSystem.out.println("instance.hashCode=" + instance.hashCode());System.out.println("instance2.hashCode=" + instance2.hashCode());}
    }// 懒汉式(线程安全,同步方法)
    class Singleton {private static volatile Singleton instance;private Singleton() {}//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题//同时保证了效率, 推荐使用public static synchronized Singleton getInstance() {if(instance == null) {synchronized (Singleton.class) {if(instance == null) {instance = new Singleton();}}}return instance;}
    }
    

    instance采用 volatile 关键字修饰也是很有必要的instance = new Singleton(); 这段代码其实是分为三步执行:

    1. 为 instance分配内存空间
    2. 初始化 instance
    3. 将 instance指向分配的内存地址

    但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用getInstance()后发现 instance不为空,因此返回 instance,但此时 instance还未被初始化。

    使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

  • 优缺点分析

    双重检查既确保了线程安全,实例化代码只能执行一次,后面再次访问,就会直接过滤,又避免反复进行代码同步。线程安全,延迟加载,效率较高,推荐使用的单例设计模式

静态内部类

  • 代码实例

    // 静态内部类完成, 推荐使用
    class Singleton {//构造器私有化private Singleton() {}//写一个静态内部类,该类中有一个静态属性 Singletonprivate static class SingletonInstance {private static final Singleton INSTANCE = new Singleton(); }//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCEpublic static Singleton getInstance() {return SingletonInstance.INSTANCE;}
    }
    
  • 优缺点分析

    采用类装载的机制保证初始化实例时只有一个线程。静态内部类方式在Singleton类被装载时不会立即实例化,而是需要在静态实例化时,调用getInstance()方法才会装载。类的静态属性确保它只会在第一次加载类的时候初始化,保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。效率高,推荐使用。

枚举

  • 代码实例

    //使用枚举,可以实现单例,是饿汉式的 推荐
    enum Singleton {INSTANCE; //属性
    }
    
  • 优缺点分析

    不仅能避免多线程同步问题,而且能防止反射攻击和反序列化重写创建新的对象,推荐使用。

工厂模式

将实例化对象的代码提取处理,放到一个类统一管理和维护,达到和主项目依赖关系的解耦,从而提高项目的扩展和维护性。

创建对象实例时,不是直接new出,而是把new这个动作放在一个工厂里并返回。不要让类继承具体类而是继承抽象类或实现接口。不要覆盖基类中实现的方法。

简单工厂

  • 基本介绍

    简单工厂不是一个设计模式,反而像是一种编程习惯。**简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。**简单工厂模式是工厂模式里最简单的使用模式。在软件开发中,当我们会用到大量的创建某种类或某批对象时,就会使用。

    简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为,让简单工厂类来决定应该用哪个具体子类来实例化。

  • 代码实例

    /**
    * 多个对象继承其父对象/实现接口
    */public interface Product {}public class ConcreteProduct implements Product {}public class ConcreteProduct1 implements Product {}public class ConcreteProduct2 implements Product {}
    

    Client 类包含了实例化的代码,这是一种错误的实现。如果在客户类中存在这种实例化代码,就需要考虑将代码放到简单工厂中。

    代码违反了OCP原则,若新增一个继承/实现的具体类,则要修改客户端代码进行添加

    public class Client {public static void main(String[] args) {int type = 1;Product product;if (type == 1) {product = new ConcreteProduct1();} else if (type == 2) {product = new ConcreteProduct2();} else {product = new ConcreteProduct();}// do something with the product}
    }
    

    以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。

    public class SimpleFactory {public Product createProduct(int type) {if (type == 1) {return new ConcreteProduct1();} else if (type == 2) {return new ConcreteProduct2();}return new ConcreteProduct();}
    }
    
    public class Client {public static void main(String[] args) {SimpleFactory simpleFactory = new SimpleFactory();Product product = simpleFactory.createProduct(1);// do something with the product}
    }
    

    当然,在开发中也有人习惯将工厂类中的创建对象的功能定义为静态的,这就是静态工厂。静态工厂在客户端中,不需要创建工厂对象,直接调用createProduct就可以创建对象。

    public class SimpleFactory {public static Product createProduct(int type) {if (type == 1) {return new ConcreteProduct1();} else if (type == 2) {return new ConcreteProduct2();}return new ConcreteProduct();}
    }
    

工厂方法

  • 基本介绍

    在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象的,定义了一个创建对象的抽象方法,由子类确定要实例化的类

  • 代码实例

    /**
    *  多个工厂类继承抽象工厂类
    */public abstract class Factory {abstract public Product factoryMethod();public void doSomething() {Product product = factoryMethod();// do something with the product}
    }public class ConcreteFactory extends Factory {public Product factoryMethod() {return new ConcreteProduct();}
    }public class ConcreteFactory1 extends Factory {public Product factoryMethod() {return new ConcreteProduct1();}
    }public class ConcreteFactory2 extends Factory {public Product factoryMethod() {return new ConcreteProduct2();}
    }
    
  • JDK底层使用该设计模式:

    • java.util.Calendar
    • java.util.ResourceBundle
    • java.text.NumberFormat
    • java.nio.charset.Charset
    • java.net.URLStreamHandlerFactory
    • java.util.EnumSet
    • javax.xml.bind.JAXBContext

抽象工厂模式

  • 基本介绍

    定义了一个接口用于创建相关或由依赖关系的对象族,并且这些对象是相关的,是必须一起创建出来的。抽象工厂可以将简单工厂模式和工厂方法模式进行整合,是对简单工厂的改进。

  • 代码实例

    public abstract class AbstractProductA {}
    
    public abstract class AbstractProductB {}
    
    public class ProductA1 extends AbstractProductA {}
    
    public class ProductA2 extends AbstractProductA {}
    
    public class ProductB1 extends AbstractProductB {}
    
    public class ProductB2 extends AbstractProductB {}
    
    public inter class AbstractFactory {AbstractProductA createProductA();AbstractProductB createProductB();
    }
    
    public class ConcreteFactory1 extends AbstractFactory {AbstractProductA createProductA() {return new ProductA1();}AbstractProductB createProductB() {return new ProductB1();}
    }
    
    public class ConcreteFactory2 extends AbstractFactory {AbstractProductA createProductA() {return new ProductA2();}AbstractProductB createProductB() {return new ProductB2();}
    }
    
    public class Client {public static void main(String[] args) {AbstractFactory abstractFactory = new ConcreteFactory1();AbstractProductA productA = abstractFactory.createProductA();AbstractProductB productB = abstractFactory.createProductB();// do something with productA and productB}
    }
    
  • JDK底层使用该设计模式:

    • javax.xml.parsers.DocumentBuilderFactory
    • javax.xml.transform.TransformerFactory
    • javax.xml.xpath.XPathFactory

原型模式

Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个Cloneable接口,该接口表示该类能够复制且具有复制能力

  • 基本介绍

    原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。工作原理是通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()

  • 代码实例

    方法一:自定义Prototype抽象类或接口,ConcretePrototype类继承或实现

    public abstract class Prototype {abstract Prototype myClone();
    }
    
    public class ConcretePrototype extends Prototype {private String filed;public ConcretePrototype(String filed) {this.filed = filed;}Prototype myClone() {return new ConcretePrototype(filed);}public String toString() {return filed;}
    }
    
    public class Client {public static void main(String[] args) {Prototype prototype = new ConcretePrototype("abc");Prototype clone = prototype.myClone();System.out.println(clone.toString());}
    }
    

    方法二:ConcretePrototype实现Cloneable接口

    public class ConcretePrototype implements Cloneable {//克隆该实例,使用默认的clone方法来完成@Overrideprotected Object clone()  {ConcretePrototype prototype = null;try {prototype = (ConcretePrototype)super.clone();} catch (Exception e) {System.out.println(e.getMessage());}return prototype;}
    }
    
  • JDK底层使用该设计模式:

    • java.lang.Object#clone()
    • Spring的bean生成对象,scope="prototype"是使用的原型模式
  • 深拷贝与浅拷贝

    浅拷贝 深拷贝
    对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性复制一份给新的对象 复制对象的所有基本数据类型的成员变量值
    对于数据类型是引用数据类型的成员变量,如数组、类对象等,浅拷贝会进行引用传递,也就是只是将该成员变量的引用值赋值一份给新的对象。实际上两个对象的成员变量指向同一个实例。在这种情况下,修改一个对象的成员变量会影响到另一个对象的成员变量。 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象,也就是说,对象进行深拷贝要对整个对象进行拷贝
    默认的clone方法实现浅拷贝 1. 重写clone方法实现深拷贝
    2. 通过对象序列化实现深拷贝
    //深拷贝 - 方式2 通过对象的序列化实现 (推荐)
    public Object deepClone() {//创建流对象ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try {//序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this); //当前这个对象以对象流的方式输出//反序列化bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);// 创建深拷贝对象,从liu'zhDeepProtoType copyObj = (DeepProtoType)ois.readObject();return copyObj;} catch (Exception e) {e.printStackTrace();return null;} finally {//关闭流try {if (oos != null)oos.close();} catch (Exception e) {System.out.println(e.getMessage());}try {if (ois != null)ois.close();} catch (Exception e) {System.out.println(e.getMessage());}}
    }
    
  • 优缺点分析

    创建新的对象比较复杂时,可以利用原型模式简化对象创建过程,提高效率。不用重新初始化对象,是动态获取对象运行时的状态,如果原始对象发生变化,其克隆对象也会发生变化。但对已有的类进行改造,会需要修改其源代码,违背了ocp原则。

建造者模式

  • 基本介绍

    建造者模式又称生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

    建造者模式是一步一步建造一个复杂的对象,用户只需要通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体细节。

    建造者模式中有四个角色:

    • Product(产品角色):一个具体的产品对象。

    • Builder(抽象建造者):创建一个Product对象的各个部件指定的接口或抽象类。

    • ConcreteBuilder(具体建造者):实现接口或抽象类,构建和组装部件。

    • Director(指挥者):构建一个使用Builder接口或抽象类的对象。主要是用于建造一个复杂对象

      作用:1. 隔离了客户与对象的生产过程

      ​ 2. 负责控制产品对象的生产过程

  • 代码实例

    public class Product{//一些属性和getter、setter方法
    }
    
    public abstract class Builder{protected Product product = new Product();public abstract void buildPart();public Product getResult(){return Product;}}
    
    public class ConcreteBuilder extends Builder{//实现抽象方法
    }
    
    public class Director{private Builder builder = null;//构造器方法传入Builderpublic Director(Builder builder){super();this.builder = builder;}//通过setter方法传入public void setBuilder(Builder builder){this.builder = builder;}//指挥者处理具体流程public Product constructProduct(){builder.buildPart();return builder.getResult();}}
    
    public class Client{public static void main(String[] args){ConcreteBuilder concreteBuilder = new ConcreteBuilder();Director director = new Director(concreteBuilder);Product product = director.getResult();}
    }
    
  • JDK底层使用该设计模式:

    • java.lang.StringBuilder

    • java.nio.ByteBuffer

    • java.lang.StringBuffer

    • java.lang.Appendable

    • Apache Camel builders

  • 优缺点分析

    客户端不需要知道产品内部细节,将产品本身与产品创建过程解耦,使得相同的创建过程可以创建不同的产品对象。可以更加精细的控制产品的创建过程,更加清晰、方便。增加新的具体创建者无需修改原有类库代码,符合开闭原则。

    建造者模式所创建的产品一般具有较多的共同点,如果产品之间差异大,则不适合建造者模式。

  • 建造者模式 VS 抽象工厂模式

    抽象工厂模式实现对产品家族的创建,一个产品家族是这样一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关系构建过程,只关心什么产品是由什么工厂生产的即可。而建造者模式则是要求按照指定的蓝图建造产品,它组要目的是通过组装零配件而产生一个新的产品

适配器模式

  • 基本介绍

    适配器模式将某个类的接口转换成客户端期望的另一个接口表示,主要目的是为了兼容,让原本因接口不匹配不能一起工作的两个类可以协同工作。别名为包装器

    分为:类适配器模式、对象适配器模式和接口适配器模式

    工作原理:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容,从用户角度看不到适配者,是解耦的过程。用户调用适配器转换出来的目标接口方法,适配器在调用被适配者的相互接口方法,用户最终收到反馈结果这是感觉是和目标接口交互。

    适配器主要角色:

    Target(目标接口):当前系统业务所期待的接口,可以是抽象类或接口。

    Adaptee(适配者类):它是被访问和适配的现存组件库中的组件接口。

    Adapter(适配器类):它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

  • JDK底层使用该设计模式:

    • java.util.Arrays#asList()
    • java.util.Collections#list()
    • java.util.Collections#enumeration()
    • javax.xml.bind.annotation.adapters.XMLAdapter
    • springmvc的HandlerAdapter

类适配器

定义一个适配器类来实现当前系统的业务接口,同时继承现有组件已经存在的组件

  • 代码实例

    public class Adaptee {public void specificRequest() {}
    }
    
    public interface Target {public void request();
    }
    
    public class Adapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest();//通过一系列方法将specificRequest适配为request返回}
    }
    
    public class User {public void use(Target target) {target.request();}
    }
    
    public class Client {public static void main(String[] args) {User user = new User();user.use(new Adapter());}
    }
    
  • 优缺点分析

    Java是单继承多接口,类适配器需要继承被适配者,会存在一定的局限性。而且被适配者在适配器中都会暴露出来,增加了成本。耦合度较高,要求程序员了解现有组件库中的相关组件的内部结构。但它可以根据需求重新被适配者类方法,更加灵活。

对象适配器

根据合成复用原则,尽量用关联关系替代继承关系。对象适配器将继承Adaptee类转变为将现有组件库中已实现的组件引入适配器类中,实现业务接口

  • 代码实例

    public class Adapter implements Target {private Adaptee adaptee;public Adapter(Adaptee adaptee){this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();//通过一系列方法将specificRequest适配为request返回}
    }
    
    public class Client {public static void main(String[] args) {User user = new User();user.use(new Adapter(new Adaptee()));}
    }
    
  • 优缺点分析

    对象适配器解决了必须继承适配者类的局限性问题。使用成本更低,更灵活。

接口适配器

当不需要全部实现接口提供的方法时,可以先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现,那么该抽象类的子类可以有选择地覆盖父类中的某些方法来实现需求。

桥接模式

  • 基本介绍

    桥接模式是指将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。它基于类的最小设计原则,通过使用封装、聚合和继承等行为让不同的类承担不同的职责。它的主要特点是把抽象与行为实现分开,从而可以保持各部分的独立性以及应对他们的功能扩展。

    桥接模式的主要角色:

    Abstraction(抽象化角色):定义抽象类,并包含一个实现化对象的引用。充当桥接类。

    RefinedAbstraction(扩展抽象化角色):是抽象化角色的子类,实现父类的业务方法,并通过组合关系调用实现化角色中的业务方法。

    Implementor(实现化角色):定义实现化角色的接口,供扩展抽象化角色调用。

    ConcreteImplementor(具体实现化角色):给出实现化角色接口的具体实现。

  • 代码实例

    public interface Implementor {void operationImpl();
    }
    
    public class ConcreteImplenmentorA implements Implementor {@Overridepublic void operationImpl() {//具体实现}
    }public class ConcreteImplenmentorB implements Implementor {public void operationImpl() { }
    }
    
    public abstract class Abstraction {private Implementor impl;//构造器public Abstraction(Implementor impl) {super();this.impl = impl;}protected void operation() {impl.operationImpl();}}
    
    public class RefinedAbstraction1 extends Abstraction {//构造器public RefinedAbstraction1(Implementor impl) {super(impl);}public void operation() {super.operation();}}public class RefinedAbstraction2 extends Abstraction {public RefinedAbstraction2(Implementor impl) {super(impl);}public void operation() {super.operation();}
    }
    
    public class Client {public static void main(String[] args) {Implementor impl1 = new RefinedAbstraction1(new ConcreteImplenmentorA());impl1.operation();Implementor impl2 = new RefinedAbstraction1(new ConcreteImplenmentorB());impl2.operation();Implementor impl3 = new RefinedAbstraction2(new ConcreteImplenmentorA());impl3.operation();Implementor impl4 = new RefinedAbstraction2(new ConcreteImplenmentorB());impl4.operation();}
    }
    
  • 优缺点分析

    实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,有助于系统进行分层设计,从而产生更好的结构化系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它部分由具体业务完成。桥接模式替代多层继承方案,可以减少子类个数,降低系统的管理和维护成本

    桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程,适用范围存在一定的局限性。

  • JDK底层使用该设计模式:

    • AWT (It provides an abstraction layer which maps onto the native OS the windowing support.)
    • JDBC

装饰者模式

  • 基本介绍

    装饰者模式动态的将新功能附加到对象上,在对象功能扩展方面,它比继承更有弹性。

    装饰者模式的角色

    Component(抽象构件角色):定义一个抽象接口以规范准备接收附加责任的对象。

    ConcreteComponent(具体构件角色):实现抽象构件,通过装饰角色为其添加一些职责。

    Decorator(抽象装饰角色):继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。

    ConcreteDecorator(具体装饰类角色):实现抽象装饰的相关方法,并具体构件对象添加附加的责任。

  • 代码实例

    public interface Component {void doOperation();
    }
    
    public class ConcreteComponentA implements Component {public void doOperation() {}
    }public class ConcreteComponentB implements Component {public void doOperation() {}
    }
    
    public class Decorator implements Component {private Component component;public Decorator(Component component) { //组合this.component = component;}@Overridepublic void doOperation() {component.otherOperation();component.doOperation();}}
    
    public class ConcreteDecorator1 extends Decorator {public Decorator(Component component) {super(component);}
    }public class ConcreteDecorator2 extends Decorator {public Decorator(Component component) {super(component);}
    }
    
    public class Client {public static void main(String[] args) {ComponentA componentA = new ConcreteComponentA();component.doOperation();componentA = new ConcreteDecorator1(componentA);componentA.doOperation();componentA = new ConcreteDecorator2(componentA);componentA.doOperation();System.out.println("===========================");Component componentB = new ConcreteComponentB();componentB.doOperation();componentB = new ConcreteDecorator1(componentB);componentB.doOperation();componentB = new ConcreteDecorator2(componentB);componentB.doOperation();}
    }
    
  • 优缺点分析

    可以带来比继承更加灵活的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化结果。比继承更具扩展性,是动态的添加装饰者与被装饰者可以独立发展,不会相互耦合

  • JDK底层使用该设计模式

    • java.io.BufferedInputStream(InputStream)
    • java.io.DataInputStream(InputStream)
    • java.io.BufferedOutputStream(OutputStream)
    • java.util.zip.ZipOutputStream(OutputStream)
    • java.util.Collections#checked List|Map|Set|SortedSet|SortedMap

组合模式

  • 基本介绍

    组合模式,又称部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次。组合模式使得用户对单个对象和组合对象的访问具有一致性,即组合能让客户以一致的方式处理个别对象及组合对象

    组合模式的角色:

    Component(抽象根节点):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。

    Composite(树枝节点):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。

    Leaf(叶子节点):叶子节点对象,其再无分支,是系统层次遍历的最小单位。

  • 代码实例

    public abstract class Component {protected String name;public Component(String name) {this.name = name;}public void print() {print(0);}abstract void print(int level);abstract public void add(Component component);abstract public void remove(Component component);
    }
    
    public class Composite extends Component {private List<Component> child;public Composite(String name) {super(name);child = new ArrayList<>();}@Overridevoid print(int level) {for (int i = 0; i < level; i++) {System.out.print("--");}System.out.println("Composite:" + name);for (Component component : child) {component.print(level + 1);}}@Overridepublic void add(Component component) {child.add(component);}@Overridepublic void remove(Component component) {child.remove(component);}
    }
    
    public class Leaf extends Component {public Leaf(String name) {super(name);}@Overridevoid print(int level) {for (int i = 0; i < level; i++) {System.out.print("--");}System.out.println("left:" + name);}@Overridepublic void add(Component component) {// 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点throw new UnsupportedOperationException(); }@Overridepublic void remove(Component component) {throw new UnsupportedOperationException();}
    }
    
    public class Client {public static void main(String[] args) {Composite root = new Composite("root");Component node1 = new Leaf("1");Component node2 = new Composite("2");Component node3 = new Leaf("3");root.add(node1);root.add(node2);root.add(node3);Component node21 = new Leaf("21");Component node22 = new Composite("22");node2.add(node21);node2.add(node22);Component node221 = new Leaf("221");node22.add(node221);root.print();}
    }
    
    Composite:root
    --left:1
    --Composite:2
    ----left:21
    ----Composite:22
    ------left:221
    --left:3
    
  • 优缺点分析

    简化客户端操作,具有较强的扩展性,方便创建出复杂的层次结构。要求较高的抽象性,若节点和叶子有很大差异的化,就不适合使用组合模式。

  • JDK底层使用该设计模式

    • javax.swing.JComponent#add(Component)
    • java.awt.Container#add(Component)
    • java.util.Map#putAll(Map)
    • java.util.List#addAll(Collection)
    • java.util.Set#addAll(Collection)

外观模式

  • 基本介绍

    外观模式又称过程模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需要跟这个接口发生调用,而无需关心这个子系统的内部细节。

    外观模式的主要角色

    Facade(外观):为多个子系统对外提供一个共同的接口

    Sub System(子系统):实现系统的部分功能,客户通过外观角色访问它

  • 代码实例

    public class SubSystem1{//子系统1功能
    }public class SubSystem2{//子系统2功能
    }public class SubSystem3{//子系统3功能
    }
    
    public class Facade{SubSystem1 sbs1 = new SubSystem1();    SubSystem2 sbs2 = new SubSystem2();SubSystem3 sbs3 = new SubSystem3();public void work1(){//将子系统的功能分配到外观类的方法中}public void work2(){}public void shutdown(){}//...
    }
    
    public class Client{public static void main(String[] args){Facade facade = new facade();facade.work1();facade.work2();facade.shutdown();}
    }
    
  • 优缺点分析

    降低了客户端对子系统的耦合度,降低了使用的复杂性,让子系统内部的模块更容易维护和扩展。但外观类不易维护,且不符合开闭原则。对于分层结构系统构建时,可以简化依赖关系。

享元模式

  • 基本介绍

    运用共享技术有效地支持大量细粒度的对象,它通过共享已存在的对象来大幅度减少需要创建的对象数量,避免大量相似的对象开销,常用于底层开发,提高系统资源使用率,解决性能问题。

    享元模式存在两种状态:

    内部状态:即不会随着环境的改变而改变的可共享部分。

    外部状态:指随着环境改变而改变的不可以共享的部分。

    享元模式的主要角色:

    Flyweight(抽象享元):抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据,同时也可以通过这些方法来设置外部数据。

    ConcreteFlyweight(具体享元):实现了抽象享元类,在具体享元类中为内部状态提供了存储空间,通常结合单例模式来设计具体享元类,提供唯一的享元对象

    UnsharableFlyweight(非享元):不能被共享的子类

    FlyweightFactory(享元工厂):负责创建和管理享元角色。享元工厂可以检测系统中是否存在符合要求的享元对象,使其提供或新建给用户。

  • 代码实例

    public interface Flyweight {void doOperation(String extrinsicState);
    }
    
    public class ConcreteFlyweight implements Flyweight {private String intrinsicState;    // 定义内部状态public ConcreteFlyweight(String intrinsicState) {this.intrinsicState = intrinsicState;}@Overridepublic void doOperation(String extrinsicState) { // 传入执行外部状态(extrinsicState)System.out.println("Object address: " + System.identityHashCode(this));System.out.println("IntrinsicState: " + intrinsicState);System.out.println("ExtrinsicState: " + extrinsicState);}
    }
    
    public class FlyweightFactory {// 集合,充当池的作用private HashMap<String, Flyweight> flyweights = new HashMap<>();// 根据参数返回对象Flyweight getFlyweight(String intrinsicState) {if (!flyweights.containsKey(intrinsicState)) {// 如果没有就创建并入池Flyweight flyweight = new ConcreteFlyweight(intrinsicState);flyweights.put(intrinsicState, flyweight);}// 如果创建了就直接获取return flyweights.get(intrinsicState);}
    }
    
    public class Client {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight flyweight1 = factory.getFlyweight("aa");Flyweight flyweight2 = factory.getFlyweight("aa");flyweight1.doOperation("x");flyweight2.doOperation("y");}
    }
    
    Object address: 1163157884
    IntrinsicState: aa
    ExtrinsicState: x
    Object address: 1163157884
    IntrinsicState: aa
    ExtrinsicState: y
    
  • 优缺点分析

    极大减少了内存中相似或相同对象数量,节约系统资源,提供性能。外部状态相互独立,且不影响内部状态。但它分离了内部状态和外部状态,使程序逻辑变得复杂。

  • JDK底层使用该设计模式

    • java.lang.Integer#valueOf(int)
    • java.lang.Boolean#valueOf(boolean)
    • java.lang.Byte#valueOf(byte)
    • java.lang.Character#valueOf(char)

代理模式

  • 基本介绍

    访问对象不适合或不能直接引用目标对象时,代理对象为一个访问对象提供一个替身,以控制对这个对象的访问。

    代理模式的主要角色

    Subject(抽象主题):通过接口或抽象类声明真实主题和代理对象实现的业务方法。

    RealSubject(真实主题):实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终引用的对象

    Proxy(代理):提供与真实主题相同的接口,其内部含有对真实主题的引用,可以访问、控制或扩展真实主题的功能

  • JDK底层使用该设计模式

    • java.lang.reflect.Proxy
    • RMI

静态代理

代理类在编译期生成。使用静态代理时,需要定义抽象类或接口,被代理对象(目标对象)与代理对象一起实现相同接口或继承相同抽象类

  • 代码实例

    public interface Subject {void doOperation();
    }
    
    public class RealSubject implements Subject {@Overridepublic void doOperation() {  }
    }
    
    public class Proxy implements Subject{private Subject target; // 目标对象,通过接口来聚合  //构造器public Proxy(Subject target) {this.target = target;}@Overridepublic void doOperation() {System.out.println("开始代理  完成某些操作。。。。。 ");target.doOperation();}}
    
    public class Client {public static void main(String[] args) {//创建目标对象(被代理对象)RealSubject realSubject = new RealSubject();//创建代理对象, 同时将被代理对象传递给代理对象Proxy proxy = new Proxy(realSubject);//通过代理对象,调用到被代理对象的方法//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法 proxy.doOperation();}
    }
    
  • 优缺点分析

    在不修改目标对象的前提下,通过代理对象对目标功能扩展。但因为代理对象需要与目标方法实现同一个接口或继承同一个类,当接口或抽象类发生改变,就需要同时维护代理类和目标方法。

JDK代理

Java运行时动态生成,代理对象不需要实现接口,但目标对象还是需要。代理对象的生成是利用JDK的API,动态的在内存中构建代理对象。

Java中提供了一个动态代理类Proxy,Proxy提供了一个创建代理对象的静态方法(newProxyInstance)来获取代理对象

public class ProxyFactory {//维护一个目标对象 , Objectprivate Object target;//构造器 , 对target 进行初始化public ProxyFactory(Object target) {this.target = target;} //给目标对象 生成一个代理对象public Object getProxyInstance() {/***  *  ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定*  Class<?>[] interfaces: 目标对象实现的接口类型*  InvocationHandler h : 代理对象的处理程序*/return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {      //抽象内部类/***  Object proxy:代理对象,在invoke方法基本用不上*  Method method:对接口中的方法进行封装的method对象*  Object[] args:调用方法的实际参数*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//反射机制调用目标对象的方法Object returnVal = method.invoke(target, args);return returnVal;}}); }
}

CGLib代理

静态代理和JDK代理都需要实际代理对象实现接口,而CGLib不需要实现接口,为JDK代理提供了很好的补充

前提:导入CGLib相关的jar包

public class ProxyFactory implements MethodInterceptor {//维护一个目标对象private Object target;//构造器,传入一个被代理的对象public ProxyFactory(Object target) {this.target = target;}//返回一个代理对象:  是 target 对象的代理对象public Object getProxyInstance() {//1. 创建一个工具类Enhancer enhancer = new Enhancer();//2. 设置父类enhancer.setSuperclass(target.getClass());//3. 设置回调函数enhancer.setCallback(this);//4. 创建子类对象,即代理对象return enhancer.create();}//重写  intercept 方法,调用目标对象的方法会触发该方法@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object returnVal = method.invoke(target, args);return returnVal;}
}

模板方法模式

  • 基本介绍

    只知道算法所需关键步骤,但具体实现未知。模板方法模式就是在一个抽象类中公开定义了执行它的方法的模板,它的子类可以按需要重写方法实现,但调用将以抽象类定义的方法进行。将步骤延迟到子类,使子类可以不改变该算法结构的情况下重写定义该算法某些特定步骤。

    模板方法模式的主要角色

    抽象类:负责给出一个算法的轮廓和骨架。由一个模板方法和若干个基本方法构成。模板方法定义了算法的骨架,按顺序调用基本方法。基本方法是实现算法各个步骤方法,它可以分为:抽象方法(由子类实现),具体方法(子类继承或重写)和钩子方法(一般为判断的逻辑方法)

    具体子类:实现抽象方法和钩子方法

  • 代码实例

    public abstract class AbstractClass {//模板方法用final修饰不让子类去覆盖final void templateMethod() {primitiveOperationA();primitiveOperationB();if(primitiveOperationC()){// doSth...}}// 具体方法,子类可以复写void primitiveOperationA() {System.out.println(" AbstractClass-primitiveOperationA  ");}// 抽象方法,子类具体实现abstract void primitiveOperationB();// 钩子方法,规定方法是否执行,可在子类复写设置boolean primitiveOperationC(){return true;}
    }
    
    public class ConcreteClass1 extends AbstractClass {@Overridevoid primitiveOperationB() {System.out.println(" ConcreteClass1 ");}
    }public class ConcreteClass2 extends AbstractClass {@Overridevoid primitiveOperationB() {System.out.println(" ConcreteClass2 ");}boolean primitiveOperationC(){return false;}
    }
    
    public class Client {public static void main(String[] args) {AbstractClass concreteClass1 = new ConcreteClass1();concreteClass1.templateMethod();AbstractClass concreteClass2 = new ConcreteClass2();concreteClass2.templateMethod();}
    }
    
  • 优缺点分析

    提高了代码的复用性,符合开闭原则。但对于每个不同的实现都需要定义一个子类,导致系统庞大,提高了代码阅读难度。

  • JDK底层使用该设计模式

    • java.util.Collections#sort()
    • java.io.InputStream#skip()
    • java.io.InputStream#read()
    • java.util.AbstractList#indexOf()

命令模式

  • 基本介绍

    将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开,两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理

    命令模式的主要角色:

    Command(抽象命令):定义命令的接口,声明执行的方法

    ConcreteCommand(具体命令):具体的命令,实现命令的接口。通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

    Receiver(实现者/接收者):真正执行命令的对象,实现命令要求实现的功能。

    Invoker(调用者/请求者):要求命令执行请求,通常会持有命令对象。是客户端真正触发命令并要求命令执行相应操作的地方,也就是命令对象的入口。

  • 代码实例

    public interface Command {public void execute();   //执行动作
    }
    
    public class Receiver {public void doOperation1() {   }public void doOperation2() {   }
    }
    
    public class ConcreteCommand1 implements Command {Receiver receiver;//构造器public ConcreteCommand1(Receiver receiver) {super();this.receiver = receiver;}@Overridepublic void execute() {//调用接收者的方法receiver.doOperation1();}}public class ConcreteCommand2 implements Command {Receiver receiver;//构造器public ConcreteCommand2(Receiver receiver) {super();this.receiver = receiver;}@Overridepublic void execute() {//调用接收者的方法receiver.doOperation2();}}
    
    public class Invoker {// 命令数组Command[] concreteCommand1s;Command[] concreteCommand2s;// 构造器,完成初始化public Invoker() {//长度按需求可变concreteCommand1s = new Command[5];concreteCommand2s = new Command[5];}// 设置所需要的命令public void setCommand1(int no, Command command) {concreteCommand1s[no] = command;}public void setCommand2(int no, Command command) {concreteCommand2s[no] = command;}// 执行命令1public void doCommand1(int no) { // no 0// 找到命令, 并调用对应方法concreteCommand1s[no].execute();}// 执行命令2public void doCommand2(int no) { // no 0// 找到命令, 并调用对应方法concreteCommand2s[no].execute();}}
    
    public class Client {public static void main(String[] args) {Invoker invoker = new Invoker();Receiver receiver = new Receiver();Command concreteCommand1 = new ConcreteCommand1(receiver);Command concreteCommand2 = new ConcreteCommand2(receiver);invoker.setCommand1(concreteCommand1, 0);invoker.setCommand2(concreteCommand2, 0);invoker.doCommand1(0);invoker.doCommand2(0);}
    }
    
  • 优缺点分析

    降低了系统的耦合度。增加或删除命令方便,满足开闭有原则,扩展比较灵活。方便实现undo、redo操作。但命令模式可能导致系统有过多的具体命令类,系统结构更复杂。

  • JDK底层使用该设计模式

    • java.lang.Runnable
    • Netflix Hystrix
    • javax.swing.Action
    • Spring JdbcTemplate

访问者模式

  • 基本介绍

    封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

    访问者模式的主要角色

    Visitor(抽象访问者):定义了对每个元素访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来说与元素类个数一致,访问者模式要求元素类的个数不能改变。

    ConcreteVisitor(具体访问者):给出对每一个元素类访问时所参数的具体行为。

    Element(抽象元素):定义了一个接受访问者的方法,每一个元素都要可以被访问者访问。

    ConcreteElement(具体元素):提供接受访问方法的具体实现,而这个具体实现通常情况下是使用访问者提供访问该元素的方法。

    ObjectStructure(对象结构):定义对象结构,可以理解为一个具有容器性质或符合对象特性的类,它会含有一组元素,并且可以迭代这些元素,供访问者访问。

  • 代码实例

    public interface Visitor {void visitA(ConcreteElementA concreteElementA);void visitB(ConcreteElementB concreteElementB);
    }public Visitor1 implements Visitor{public void visitA(ConcreteElementA concreteElementA){System.out.println("visitor1 visit concreteElementA");}pubilc void visitB(ConcreteElementB concreteElementB){System.out.println("visitor1 visit concreteElementB");}
    }public Visitor2 implements Visitor{public void visitA(ConcreteElementA concreteElementA){System.out.println("visitor2 visit concreteElementA");}public void visitB(ConcreteElementB concreteElementB){System.out.println("visitor2 visit concreteElementB");}
    }
    
    public interface Element {void accept(Visitor visitor);
    }public ConcreteElementA implements Element{void accept(Visitor visitor){System.out.println("Hello,I'm ConcreteElementA");visitor.visitA(this)}
    }public ConcreteElementB implements Element{void accept(Visitor visitor){System.out.println("Hello,I'm ConcreteElementB");visitor.visitB(this)}
    }
    
    public class ObjectStructure {//维护了一个集合private List<Element> elements = new LinkedList<>();//增加到listpublic void add(Element e) {elements.add(e);}//显示测评情况public void action(Visitor visitor) {for(Element e: elements) {e.accept(visitor);}}
    }
    
    public class Client {public static void main(String[] args) {//创建ObjectStructureObjectStructure objectStructure = new ObjectStructure();objectStructure.add(new ConcreteElementA());objectStructure.add(new ConcreteElementB());Visitor1 visitor1 = new Visitor1();objectStructure.action(visitor1);System.out.println("=================");Visitor2 visitor2 = new Visitor2();objectStructure.action(visitor2);}
    }
    
    Hello,I'm ConcreteElementA
    visitor1 visit concreteElementA
    Hello,I'm ConcreteElementB
    visitor1 visit concreteElementB
    =================
    Hello,I'm ConcreteElementA
    visitor2 visit concreteElementA
    Hello,I'm ConcreteElementB
    visitor2 visit concreteElementB
    
  • 优缺点分析

    扩展性好、复用性好,分离无关行为。但会使结构变得很困难,违反了开闭原则和依赖倒置原则。

  • JDK底层使用该设计模式

    • javax.lang.model.element.Element and javax.lang.model.element.ElementVisitor
    • javax.lang.model.type.TypeMirror and javax.lang.model.type.TypeVisitor

迭代器模式

  • 基本介绍

    迭代器模式提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,不暴露其内部结构。

    迭代器模式的主要角色:

    Aggregate(抽象聚合):定义存储、添加、删除聚合元素以及创建迭代器对象的接口

    ConcreteAggregate(具体聚合):实现抽象聚合类,返回一个具体迭代器的实例

    Iterator(抽象迭代器):定义访问和遍历聚合元素的接口,通常包含hasNext()、next()等方法

    Concretelterator(具体迭代器):实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

  • 代码实例

    public interface Aggregate {Iterator createIterator();
    }
    
    public class ConcreteAggregate implements Aggregate {private Integer[] items;public ConcreteAggregate() {      //固定聚合对象的元素,可定义add、remove等方法对聚合对象元素进行增删items = new Integer[10];for (int i = 0; i < items.length; i++) {items[i] = i;}}@Overridepublic Iterator createIterator() {return new ConcreteIterator<Integer>(items);}
    }
    
    public interface Iterator<Item> {Item next();boolean hasNext();
    }
    
    public class ConcreteIterator<Item> implements Iterator {private Item[] items;private int position = 0;public ConcreteIterator(Item[] items) {this.items = items;}@Overridepublic Object next() {return items[position++];}@Overridepublic boolean hasNext() {return position < items.length;}
    }
    
    public class Client {public static void main(String[] args) {Aggregate aggregate = new ConcreteAggregate();Iterator<Integer> iterator = aggregate.createIterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}
    }
    
  • 优缺点分析

    支持不同方式遍历一个聚合对象,可以使用不同的迭代器来替换原来迭代器遍历的算法。简化了聚合类,新增聚合类和迭代器都很方便,满足了开闭原则。

  • JDK底层使用该设计模式

    • java.util.Iterator
    • java.util.Enumeration

观察者模式

  • 基本介绍

    对象之间一对多依赖的一种设计方案,又称发布-订阅模式,让多个观察者对象同时建通某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

    观察者模式的主要角色:

    Subject(抽象主题/抽象被观察者):抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

    ConcreteSubject(具体主题):该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生变化时,给所有注册过的观察者发送通知。

    Observer(抽象观察者):抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。

    ConcreteObserver(具体观察者):实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身状态。

  • 代码实例

    public interface Subject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObservers();
    }
    
    public class ConcreteSubject implements Subject {private List<Observer> observers;// 具体主题包含的属性参数 private String a;public ConcreteSubject() {observers = new ArrayList<>();}public void setAttribute(/* 具体主题包含的参数 String a */) {// this.a = a;notifyObserver();       //设置最新属性后直接通知,也可以提取出来另写方法调用}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {int i = observers.indexOf(o);if (i >= 0) {observers.remove(i);}}@Overridepublic void notifyObserver() {for (Observer o : observers) {o.update(/* 具体主题包含的参数 a */);}}
    }
    
    public interface Observer {void update(/* 传入主题中的所需属性,需要更新的属性 String a */);
    }
    
    public class ConcreteObserver implements Observer {public ConcreteObserver(Subject subject) {subject.registerObserver(this);}@Overridepublic void update(/* 传入主题中的所需属性,需要更新的属性 String a */) {// 重写update方法 System.out.println(a);}
    }
    
    public class Client {public static void main(String[] args) {ConcreteSubject concreteSubject = new ConcreteSubject();ConcreteObserver concreteObserver = new ConcreteObserver(concreteSubject);concreteSubject.setAttribute("test");concreteSubject.setAttribute("update");}
    }
    
  • 优缺点分析

    降低了目标与观察者之间的耦合关系,被观察者发送通知,所有注册的观察者都会收到信息。但如果观察者非常多的话,所有观察者收到发送的通知会很耗时。

  • JDK底层使用该设计模式

    • java.util.Observer
    • java.util.EventListener
    • javax.servlet.http.HttpSessionBindingListener
    • RxJava

中介模式

  • 基本介绍

    用一个中介对象封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,且可以独立地改变它们之间的交互。在MVC模式中,C就是M和V的中介者。

    中介者模式的主要角色:

    Mediator(抽象中介者):中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法

    ConcreteMediator(具体中介者):实现抽象接口,定义一个list来管理同事对象,协调各个同事间角色的交互,依赖于同事角色

    Colleague(抽象同事):同事类接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相关影响的同事类的公共功能

    ConcreteColleague(具体同事):实现抽象同事类,当需要与其他同事对象交互时,由中介者对象负责后续交互

  • 代码实例

    public abstract class Mediator {public abstract void doEvent(String doWhat);
    }
    
    public abstract class Colleague {public abstract void onMediator(Mediator mediator);   //将中介者传入同事类
    }
    
    public class ConcreteColleague1 extends Colleague {@Overridepublic void onMediator(Mediator mediator) {mediator.doEvent("ConcreteColleague1");}public void sayHello(){  //定义同事类其他方法System.out.println("Hello");}
    }public class ConcreteColleague2 extends Colleague {@Overridepublic void onMediator(Mediator mediator) {mediator.doEvent("ConcreteColleague2");}public void sayBye(){    //定义同事类其他方法System.out.println("Bye");}
    }...
    
    public class ConcreteMediator extends Mediator {private ConcreteColleague1 concreteColleague1;private ConcreteColleague2 concreteColleague2;public ConcreteMediator(ConcreteColleague1 concreteColleague1, ConcreteColleague2 concreteColleague2) {this.concreteColleague1 = concreteColleague1;this.concreteColleague2 = concreteColleague2;}@Overridepublic void doEvent(String doWhat) {switch (doWhat) {case "concreteColleague1":doEventFor1();break;case "concreteColleague2":doEventFor2();break;default:System.out.println("event can not found.");}}public void doEventFor1() {//调用同事类的各种方法 包括同一个同事或不同的同事,代表同事间的依赖关系concreteColleague1.sayHello();concreteColleague2.sayBye();}public void doEventFor2() {// ...}
    }
    
    public class Client {public static void main(String[] args) {ConcreteColleague1 concreteColleague1 = new ConcreteColleague1();ConcreteColleague2 concreteColleague2 = new ConcreteColleague2();Mediator mediator = new ConcreteMediator(concreteColleague1, concreteColleague2);// 闹钟事件到达,调用中介者就可以操作相关对象concreteColleague1.onMediator(mediator);}
    }
    
  • 优缺点分析

    松散耦合,集中控制交互,一对多关联转变为一对一关联

  • JDK底层使用该设计模式

    • All scheduleXXX() methods of java.util.Timer
    • java.util.concurrent.Executor#execute()
    • submit() and invokeXXX() methods of java.util.concurrent.ExecutorService
    • scheduleXXX() methods of java.util.concurrent.ScheduledExecutorService
    • java.lang.reflect.Method#invoke()

备忘录模式

  • 基本介绍

    备忘录模式提供了一种状态恢复的机制,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。类似于撤销,或游戏的存档都是状态恢复机制。

    备忘录模式的主要角色

    Originator(发起人):记录当前时刻内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息

    Menento(备忘录):负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人

    Caretaker(管理者):对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改

  • 代码实例

    public class Memento {private String state;public Memento(String state) {super();this.state = state;}public String getState() {return state;}
    }
    
    public class Originator {private String state;public String getState() {return state;}public void setState(String state) {this.state = state;}//保存一个状态对象Mementopublic Memento saveStateMemento() {return new Memento(state);}//通过备忘录对象,恢复状态public void getStateFromMemento(Memento memento) {state = memento.getState();}
    }
    
    public class Caretaker {private Memento memento = new Memento;                            //管理一个发起人的一种状态private List<Memento> mementoList = new ArrayList<Memento>();    //管理一个发起人的多种状态private Map<String, List<Memento>> map = new HashMap<>();      //管理多个发起人的多个状态public void add(Memento memento) {mementoList.add(memento);}public Memento get(int index) {return mementoList.get(index);}
    }
    
    public class Client {public static void main(String[] args) {Originator originator = new Originator();Caretaker caretaker = new Caretaker();originator.setState(" 状态#1 ");caretaker.add(originator.saveStateMemento()); //保存了当前的状态originator.setState(" 状态#2 ");caretaker.add(originator.saveStateMemento());originator.getStateFromMemento(caretaker.get(0));System.out.println("当前的状态是 =" + originator.getState());}
    }
    

    以上方法将备忘录属性对外暴露,容易发生入侵。可以利用私有内部类实现只对当前发起人的私有。

    将Memento定义成无任何属性的抽象接口,在发起人中继承并构建自己的备忘录

    public class Originator {private class MyMemento implements Memento{//...}
    }
    
  • 优缺点分析

    提供了一种状态恢复机制,实现了内部状态的封装,用户不需要关系内部细节,简化了发起人类,发起人不需要管理各个备份,符合了单一职责原则。但资源消耗大,内部消息过多或保存频繁会容易消耗较大的内存资源。

  • JDK底层使用该设计模式

    • java.io.Serializable

解释器模式

  • 基本介绍

    将需要解决的问题,提取规则,抽象成为一种“语言”。

    解释器模式的主要角色

    AbstractExpression(抽象表达式):定义解释器的接口,约定解释器的解释操作,包含解释方法 interpret()

    TerminalExpression(终结符表达式):是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之对应

    NonTerminalExpression(非终结符表达式):如上

    Context(环境 / 上下文):通过包含各个解释器需要的数据或是公共的功能,一般同类传递所有解释器共享的数据,后面的解释器可以从这里获取值

    Client(客户端):将需要分析的句子或表达式转化成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,也可以通过环境角色间接访问解释器的解释方法

  • 代码实例

    public abstract class AbstractExpression {public abstract int interpreter(HashMap<String, Integer> var);      //或者用Context对象封装数据
    }
    
    public class VarExpression extends AbstractExpression {private String key;public VarExpression(String key) {this.key = key;}@Overridepublic int interpreter(HashMap<String, Integer> var) {return var.get(this.key);}
    }
    
    public class NonTerminalExpression extends AbstractExpression {protected AbstractExpression left;protected AbstractExpression right;public NonTerminalExpression(AbstractExpression left, AbstractExpression right) {this.left = left;this.right = right;}@Overridepublic int interpreter(HashMap<String, Integer> var) {return 0;}
    }
    
    public class AddExpression extends NonTerminalExpression  {    //加法解释器public AddExpression(AbstractExpression left, AbstractExpression right) {super(left, right);}public int interpreter(HashMap<String, Integer> var) {return super.left.interpreter(var) + super.right.interpreter(var);}
    }public class SubExpression extends NonTerminalExpression { //减法解释器public SubExpression(AbstractExpression left, AbstractExpression right) {super(left, right);}public int interpreter(HashMap<String, Integer> var) {return super.left.interpreter(var) - super.right.interpreter(var);}
    }
    
    public class TerminalExpression extends Expression {//调用解释器并利用算法来解释表达式并完成解释
    }
    
  • 优缺点分析

    易于改变和扩展文法,较为容易的实现文法,新增解释器表达式更方便。但对于复杂的文法难以维护,要自己写算法,并且使用了大量的循环和递归,效率低。

  • JDK底层使用该设计模式

    • java.util.Pattern
    • java.text.Normalizer
    • All subclasses of java.text.Format
    • javax.el.ELResolver

状态模式

  • 基本介绍

    主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换,当一个对象的内在状态改变时,允许改变其行为

    状态模式的主要角色:

    Context(环境):定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理

    State(抽象状态):定义一个接口,用以封装环境对象中的特定状态所对应的行为

    ConcreteState(具体状态):实现抽象状态所对应的行为

  • 代码实例

    public interface State {void doHandle1();void doHandle2();}
    
    public class Context {private State concreteStateA;private State concreteStateB;private State state;private int now;public Context(int number) {now = number;concreteStateA = new ConcreteStateA(this);concreteStateB = new ConcreteStateB(this);if (now == 1) {state = concreteStateA;} else {state = concreteStateB;}}public void doHandle1() {state.doHandle1();}public void doHandle2() {state.doHandle2();}public void setState(State state) {this.state = state;}public State getConcreteStateA() {return concreteStateA;}public State getConcreteStateB() {return concreteStateB;}
    }
    public class ConcreteStateA implements State {private Context context;public ConcreteStateA(Context context) {this.context = context;}@Overridepublic void doHandle1() {System.out.println(" A:操作1 ");}@Overridepublic void doHandle2() {System.out.println(" A:操作2 --> B ");context.setState(context.getConcreteStateB());}}public class ConcreteStateB implements State {private Context context;public ConcreteStateB(Context context) {this.context = context;}@Overridepublic void doHandle1() {System.out.println(" B:操作1 ");}@Overridepublic void doHandle2() {System.out.println(" B:操作2 --> A ");context.setState(context.getConcreteStateA());}}
    
    public class Client {public static void main(String[] args) {Context context = new Context(1);context.doHandle1();context.doHandle2();context.doHandle1();context.doHandle2();context.doHandle1();}
    }
    
     A:操作1 A:操作2 --> B B:操作1 B:操作2 --> A A:操作1
    
  • 优缺点分析

    将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,允许状态转换逻辑与状态对象合成一体而不是某个巨大的条件语块,方便维护。但会增加系统类和对象的个数,使得结构和实现都较为复杂,容易代码混乱,对开闭原则的支持不太好。

策略模式

  • 基本介绍

    策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。

    策略模式的主要角色

    Strategy(抽象策略):给出所有的具体策略类所需的接口

    ConcreteStrategy(具体策略):提供具体的算法实现或行为

    Context(环境):持有一个策略类的引用,最终给客户端调用

  • 代码实例

    public interface Stategy {void behavior();
    }
    
    public class ConcreteStategyA implements Stategy {@Overridepublic void behavior() {System.out.println("behavior of A");}
    }public class ConcreteStategyB implements Stategy {@Overridepublic void behavior() {System.out.println("behavior of B");}
    }
    
    public class Context {private Stategy stategy;public void showBehavior() {if (stategy != null) {stategy.quack();}}public void setStategy(Stategy stategy) {this.stategy = stategy;}
    }
    
    public class Client {public static void main(String[] args) {Context context = new Context();context.setStategy(new ConcreteStategyA());context.showBehavior();context.setStategy(new ConcreteStategyB());context.showBehavior();}
    }
    
  • 优缺点分析

    策略类之间可以自由切换,且易于扩展,符合开闭原则;简化算法,符合面向对象的设计思想。但客户端必须知道所有策略类才能选用策略,而且会生成很多策略类,可以使用享元模式减少策略对象的生成。

  • JDK底层使用该设计模式

    • java.util.Comparator#compare()
    • javax.servlet.http.HttpServlet
    • javax.servlet.Filter#doFilter()

职责链模式

  • 基本介绍

    为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

    职责链模式的主要角色

    Handler(抽象处理者):定义一个处理请求的接口,包含抽象处理方法和一个后继连接

    ConcreteHandler(具体处理者):实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将请求传递给后继

    Request(请求):含有很多属性,表示一个请求

    Client(客户类):创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程

  • 代码实例

    public abstract class Handler {protected Handler successor;public Handler(Handler successor) {this.successor = successor;}protected abstract void handleRequest(Request request);
    }
    
    public class ConcreteHandler1 extends Handler {public ConcreteHandler1(Handler successor) {super(successor);}@Overrideprotected void handleRequest(Request request) {if (request.getType() == RequestType.TYPE1) {System.out.println(request.getName() + " is handle by ConcreteHandler1");return;}if (successor != null) {successor.handleRequest(request);}}
    }public class ConcreteHandler2 extends Handler {public ConcreteHandler2(Handler successor) {super(successor);}@Overrideprotected void handleRequest(Request request) {if (request.getType() == RequestType.TYPE2) {System.out.println(request.getName() + " is handle by ConcreteHandler2");return;}if (successor != null) {successor.handleRequest(request);}}
    }
    
    public class Request {private RequestType type;private String name;public Request(RequestType type, String name) {this.type = type;this.name = name;}public RequestType getType() {return type;}public String getName() {return name;}
    }
    
    public enum RequestType {TYPE1, TYPE2
    }
    
    public class Client {public static void main(String[] args) {Handler handler1 = new ConcreteHandler1(null);Handler handler2 = new ConcreteHandler2(handler1);Request request1 = new Request(RequestType.TYPE1, "request1");handler2.handleRequest(request1);Request request2 = new Request(RequestType.TYPE2, "request2");handler2.handleRequest(request2);}
    }
    
    request1 is handle by ConcreteHandler1
    request2 is handle by ConcreteHandler2
    
  • 优缺点分析

    降低了对象之间的耦合度;增强了系统的可扩展性;增强了给对象指派职责的灵活性;责任链简化了对象之间的连接;责任分担。但不能确保每一个请求一定被处理;若责任链较长,一定情况下会影响效率;增加了客户端的复杂性。

  • JDK底层使用该设计模式

    • java.util.logging.Logger#log()
    • Apache Commons Chain
    • javax.servlet.Filter#doFilter()

设计模式--类图、实例代码相关推荐

  1. java类图与代码实例

    在 Java编程中,类图是一个非常重要的概念.类图的作用是用来展示类的结构以及类之间的关系.通过类图,可以很方便地展示出对象之间的关系.下面我将使用实例来演示一下我在学习 Java时的类图. 首先我们 ...

  2. Java设计模式之模板方法模式(UML类图分析+代码详解)

    大家好,我是一名在算法之路上不断前进的小小程序猿!体会算法之美,领悟算法的智慧~ 希望各位博友走过路过可以给我点个免费的赞,你们的支持是我不断前进的动力!! 加油吧!未来可期!! 本文将介绍java设 ...

  3. Java设计模式之中介者模式(UML类图分析+代码详解)

    大家好,我是一名在算法之路上不断前进的小小程序猿!体会算法之美,领悟算法的智慧~ 希望各位博友走过路过可以给我点个免费的赞,你们的支持是我不断前进的动力!! 加油吧!未来可期!! 本文将介绍java设 ...

  4. Java设计模式之策略模式(UML类图分析+代码详解)

    大家好,我是一名在算法之路上不断前进的小小程序猿!体会算法之美,领悟算法的智慧~ 希望各位博友走过路过可以给我点个免费的赞,你们的支持是我不断前进的动力!! 加油吧!未来可期!! 本文将介绍java设 ...

  5. Java设计模式之享元模式(UML类图分析+代码详解)

    大家好,我是一名在算法之路上不断前进的小小程序猿!体会算法之美,领悟算法的智慧~ 希望各位博友走过路过可以给我点个免费的赞,你们的支持是我不断前进的动力!! 加油吧!未来可期!! 本文将介绍java设 ...

  6. java设计模式之建造者模式(UML类图分析+代码详解)

    大家好,我是一名在算法之路上不断前进的小小程序猿!体会算法之美,领悟算法的智慧~ 希望各位博友走过路过可以给我点个免费的赞,你们的支持是我不断前进的动力!! 加油吧!未来可期!! 本文将介绍java设 ...

  7. Java设计模式之组合模式(UML类图分析+代码详解)

    大家好,我是一名在算法之路上不断前进的小小程序猿!体会算法之美,领悟算法的智慧~ 希望各位博友走过路过可以给我点个免费的赞,你们的支持是我不断前进的动力!! 加油吧!未来可期!! 本文将介绍java设 ...

  8. 23种设计模式【全】 包含:模式定义 使用场景 实现步骤 优缺点 模式区别 UML类图 示例代码 注意项等

    23种设计模式(全) ***项目地址***:[GitHub](https://github.com/yjhroot/design-pattern) 声明 模式分类(3大类) 创建型模式(共5种) 结构 ...

  9. 【设计模式】装饰器模式类图和代码

    文章目录 1 概述 2 类图 3 代码 1 概述 2 类图 3 代码 package lixiang;public class Test {public static void main(String ...

  10. 最全Pycharm教程(43)——Pycharm扩展功能之UML类图使用 代码结构

    版权声明:本文为博主原创文章,转载时麻烦注明源文章链接,谢谢合作 https://blog.csdn.net/u013088062/article/details/50353202 1.什么是UML ...

最新文章

  1. mysql 错误:1166 解决办法
  2. Linux对I/O端口资源的管理〔1〕
  3. 文献记录(part46)--Building Outlier Detection Ensembles by Selective Parameterization of ...
  4. 图论--2-SAT--详解
  5. 20181220 Oracle程序包基本开发逻辑
  6. html中怎么远程控制小车,利用ESP8266远程控制小车 求大佬帮忙加段程序
  7. pd怎么转成mysql_powerdesigner中实现PDM到MYSQl数据库的转换《转》
  8. python电脑下载-python
  9. 火灾原来离我们那么近
  10. 【遥感专题系列】微波遥感(二、合成孔径雷达SAR基础)
  11. linux权限不够【操作方案】
  12. flutter大小单位:dp
  13. 【转帖】map,set,list,等JAVA中集合解析 - Java - cjw的资料
  14. product相关函数(excel)
  15. 华为硬件工程师社招机考题库_【华为硬件开发工程师面试】第一轮机考,在华为南研所-看准网...
  16. 从零开始:微信小程序新手入门宝典
  17. new Proxy()代理
  18. myBase Desktop 6.x 用户手册
  19. 优秀案例 | 长江鲲鹏中地数码:打造智慧城市“数字底座”
  20. 【172. 阶乘后的零】

热门文章

  1. 山东省计算机信息职业学院,山东信息职业技术学院
  2. 成佩涛-站酷(ZCOOL) 刷评论漏洞
  3. 输入一个数字n,计算n的阶乘
  4. eclipse java使用_Java基础--Eclipse使用
  5. 音频编辑大师 3.3 注册名称 许可证
  6. java花开程序_java代码的运行流程
  7. c语言求最大公约数(c语言求最大公约数和最小公倍数代码)
  8. html打印无法打印背景图片,Word/wps怎么打印背景图片?Word背景打印不出来的三种解决办法...
  9. SpringBoot + Vue实现在线考试系统
  10. python模拟Get请求保存网易歌曲的url