设计模式

单例模式

简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()方法来获取它们的实例;

1.构造方法私有化;2.实例化的变量引用私有化;3.获取实例的方法公有

getInstance()的返回值是一个对象的引用,并不是一个新的实例,所以不要错误的理解成多个对象。

单例模式的实现:

  1. public class Singleton {
  2. private static Singleton singleton;
  3. private Singleton() {
  4. }
  5. public static Singleton getInstance(){
  6. if(singleton == null){
  7. singleton = new Singleton();
  8. }
  9. return singleton;
  10. }
  11. }

这是最基本的写法,也叫懒汉式写法(线程不安全),下面再介绍几种单例模式的实现方法:

懒汉式写法(线程安全)

  1. public class Singleton {
  2. private static Singleton instance;
  3. private Singleton() {
  4. }
  5. public static synchronized Singleton getInstance() {
  6. if (instance == null) {
  7. instance = new Singleton();
  8. }
  9. return instance;
  10. }
  11. }

饿汉式写法

  1. public class Singleton {
  2. private static Singleton instance = new Singleton();
  3. private Singleton() {
  4. }
  5. public static Singleton getInstance() {
  6. return instance;
  7. }
  8. }

静态内部类

  1. public class Singleton {
  2. private static class SingletonHolder {
  3. private static final Singleton INSTANCE = new Singleton();
  4. }
  5. private Singleton() {
  6. }
  7. public static final Singleton getInstance() {
  8. return SingletonHolder.INSTANCE;
  9. }
  10. }

枚举

  1. public enum Singleton {
  2. INSTANCE;
  3. public Singleton getInstance(){
  4. return INSTANCE;
  5. }
  6. }

这种方式是《Effective Java》作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,

单元素的枚举类型已经成为实现Singleton的最佳方法。

双重校验锁

  1. public class Singleton {
  2. private volatile static Singleton singleton;
  3. private Singleton() {
  4. }
  5. public static Singleton getInstance() {
  6. if(singleton == null) {
  7. synchronized(Singleton.class) {
  8. if(singleton == null) {
  9. singleton == new Singleton();
  10. }
  11. }
  12. }
  13. return singleton;
  14. }
  15. }

观察者模式

在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。

其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。

设计模式 - 图1

  • 抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
  • 抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
  • 具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。
  • 具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。

小结:

  • 这个模式是松偶合的。改变主题或观察者中的一方,另一方不会受到影像。
  • JDK中也有自带的观察者模式。但是被观察者是一个类而不是接口,限制了它的复用能力。
  • 在JavaBean和Swing中也可以看到观察者模式的影子。

装饰者模式

介绍:对已有的业务逻辑进一步的封装,使其增加额外的功能,Java中的IO流就是用了装饰者模式,用户在使用的时候,可以任意组装,达到自己想要的效果。

装饰者模式由四部分组成

  • 抽象组件:需要装饰的抽象对象,(接口或抽象类)
  • 具体组件:实现抽象组件,需要装饰的对象;
  • 抽象装饰类:实现抽象组件,包含了对抽象组件的引用,并声明装饰方法;
  • 具体装饰类:继承抽象装饰类,实现抽象方法,可以有多个;

示例:

  1. /**
  2. * 装饰器设计模式:咖啡模拟,修饰器 牛奶、糖、
  3. * 1、抽象组件:需要装饰的抽象对象(接口或抽象父类) 饮品
  4. * 2、具体组件:需要装饰的对象 咖啡
  5. * 3、抽象装饰类:包含了对抽象组件的引用以及装饰着共有的方法
  6. * 4、具体装饰类:被装饰的对象 糖、牛奶
  7. * @author Tassel
  8. */
  9. public class DecorateTest {
  10. public static void main(String[] args) {
  11. Drink coffee = new Coffee(); //原味咖啡
  12. System.out.println(coffee.info() + coffee.price());
  13. Drink milkCoffee = new Milk(coffee); // 加牛奶
  14. System.out.println(milkCoffee.info() + milkCoffee.price());
  15. Drink sugerCoffee = new Suger(coffee); // 加糖
  16. System.out.println(sugerCoffee.info() + sugerCoffee.price());
  17. }
  18. }
  19. // 1.抽象组件:饮品
  20. interface Drink {
  21. double price(); //价格
  22. String info(); //说明
  23. }
  24. //2.具体组件: 咖啡
  25. class Coffee implements Drink {
  26. private String name = "原味咖啡";
  27. @Override
  28. public double price() {
  29. return 10;
  30. }
  31. @Override
  32. public String info() {
  33. return name;
  34. }
  35. }
  36. //3. 抽象装饰类
  37. abstract class DrinkDecorate implements Drink {
  38. //对抽象组件的引用
  39. private Drink drink;
  40. public DrinkDecorate (Drink drink) {
  41. this.drink = drink;
  42. }
  43. @Override
  44. public double price() {
  45. return this.drink.price();
  46. }
  47. @Override
  48. public String info() {
  49. return this.drink.info();
  50. }
  51. }
  52. //4.1 具体装饰类 : 牛奶
  53. class Milk extends DrinkDecorate {
  54. public Milk(Drink drink) {
  55. super(drink);
  56. }
  57. @Override
  58. public double price() {
  59. return super.price()*4;
  60. }
  61. @Override
  62. public String info() {
  63. return super.info() + "加入了牛奶装饰";
  64. }
  65. }
  66. //4.2 具体装饰类 : 糖
  67. class Suger extends DrinkDecorate {
  68. public Suger(Drink drink) {
  69. super(drink);
  70. }
  71. @Override
  72. public double price() {
  73. return super.price()*2;
  74. }
  75. @Override
  76. public String info() {
  77. return super.info() + "加入了糖装饰";
  78. }
  79. }

适配器模式

将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

角色:

Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类;

Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类时适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系;

Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

示例:类适配器

  1. //适配者类
  2. public class Adaptee {
  3. public void adapteeRequest() {
  4. System.out.println("被适配者的方法");
  5. }
  6. }
  1. //目标接口
  2. public interface Target {
  3. void request();
  4. }
  1. public class Adapter extends Adaptee implements Target{
  2. @Override
  3. public void request() {
  4. // 其他操作...
  5. super.adapteeRequest();
  6. // 其他操作...
  7. }
  8. }

示例:对象适配器

对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter 类即可将转变为对象适配器。

  1. public class Adapter implements Target{
  2. // 适配者是对象适配器的一个属性
  3. private Adaptee adaptee = new Adaptee();
  4. @Override
  5. public void request() {
  6. // 其他操作...
  7. adaptee.adapteeRequest();
  8. // 其他操作...
  9. }
  10. }

静态代理模式

  • RealSubject:真实对象,是实现抽象接口的类。
  • Proxy:代理对象,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
    • 静态代理:有一个类文件描述代理模式
    • 动态代理:在内存中形成代理类
  • Subject : 接口,是对象和它的代理共用的接口,让RealSubjectProxy具有一致性。

设计模式 - 图2

实现步骤:

  1. 代理对象和真实对象实现相同的接口;
  2. 代理对象 = Proxy.newProxyInstance(三个参数);[动态代理]
    参数:
    1. 类加载器:真实对象.getClass().getClassLoader()
    2. 接口数组:真实对象.getClass().getInterfaces()
    3. 处理器:new InvocationHndler()
  3. 使用代理对象调用方法;
  4. 增强方法;
  1. package com.tassel.thread;
  2. /**
  3. * 实现静态代理 公共接口: 1.真实角色(对象) 2.代理角色(对象)
  4. *
  5. */
  6. public class StaticProxy {
  7. public static void main(String[] args) {
  8. new WeddingCompany(new You()).happyMarry();
  9. // 类似多进程的
  10. // new Thread(线程对象).start();
  11. }
  12. }
  13. // 公共接口 结婚
  14. interface Marry {
  15. void happyMarry();
  16. }
  17. // 真实角色 你结婚
  18. class You implements Marry {
  19. @Override
  20. public void happyMarry() {
  21. System.out.println("真实角色:我要结婚");
  22. }
  23. }
  24. // 代理角色 婚庆公司
  25. class WeddingCompany implements Marry {
  26. private Marry target;
  27. public WeddingCompany(Marry target) {
  28. this.target = target;
  29. }
  30. @Override
  31. public void happyMarry() {
  32. System.out.println("代理角色:有人要结婚接个活");
  33. target.happyMarry();
  34. System.out.println("代理角色:人家结完婚了,咱该走了");
  35. }
  36. }

动态代理模式

  • 特点:字节码随用随创建,随用随加载;
  • 作用:不修改方法代码的基础上对方法增强
  • 分类:
    • 基于接口的动态代理
    • 基于子类的动态代理

https://www.cnblogs.com/tuyang1129/p/12878549.html

基于接口的动态代理

  • 涉及的类:JDK官方提供的Proxy
  • 如何创建代理对象:使用Proxy类中的newProxyInstance方法
  • 创建代理对象的要求:被代理类最少实现一个接口,如果没有则不能使用
  • newProxyInstance方法的参数:
    • ClassLoader:类加载器,用于加载代理对象字节码的,即被代理对象的类加载器
    • Class[]:字节码数组,用于让代理对象和被代理对象有相同的方法。即被代理对象的接口
    • InvocationHandler:它是让我们写如何代理,一般都是写一个该接口的实现类,一般情况下是匿名内部类,非必然,此接口的实现类,谁用谁写。 ```java // 被代理对象 final Producer producer = new Producer();

IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler(){ /**

  1. * 作用:执行被代理对象的任何借口方法都会执行该方法;
  2. * @param proxy:代理对象的引用
  3. * @param method:当前执行的方法
  4. * @param args:当前执行方法所需的参数
  5. * @return:与被代理对象方法有相同的返回值
  6. * @throws Throwable
  7. */
  8. @Override
  9. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  10. // 提供增强的代码
  11. Object returnValue = null;
  12. // 获取方法执行的参数
  13. Float money = (Float) args[0];
  14. // 判断当前方法是不是销售
  15. if ("saleProduct".equals(method.getName())) {
  16. returnValue = method.invoke(producer, money * 0.8f);
  17. }
  18. return returnValue;
  19. }

});

  1. <a name="4de5d68b"></a>
  2. ### 基于子类的动态代理
  3. - **涉及的类**:第三方cglib库,需要导jar包
  4. - **如何创建代理对象**:使用Enhancer类中的create方法
  5. - **创建代理对象的要求**:被代理类不能是最终类
  6. - create方法的参数:
  7. - Class:字节码,用于指定被代理对象的字节码
  8. - Callback:用于提供增强的代码,它是让我们写如何代理。一般来实现该接口的子接口:MethodInterceptor
  9. ```java
  10. Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
  11. /**
  12. * 执行代理对象的任何方法都会执行该方法
  13. * @param proxy:代理对象的引用
  14. * @param method:当前执行的方法
  15. * @param args:当前执行方法所需的参数
  16. * @param
  17. * @return
  18. */
  19. @Override
  20. public Object intercept(Object proxy, Method method, Object[] orgs, MethodProxy methodProxy) throws Throwable {
  21. // 提供增强的代码
  22. Object returnValue = null;
  23. // 获取方法执行的参数
  24. Float money = (Float) args[0];
  25. // 判断当前方法是不是销售
  26. if ("saleProduct".equals(method.getName())) {
  27. returnValue = method.invoke(producer, money * 0.8f);
  28. }
  29. return returnValue;
  30. }
  31. });

工厂模式

简单工厂模式:一个抽象的接口,多个抽象接口的实现类,一个工厂类,用来实例化抽象的接口

  1. package com.tassel.designmodel;
  2. public class TestFactory {
  3. public static void main(String[] args) {
  4. Car car = Factory.getCarInstance("Benz");
  5. if (car != null) {
  6. car.run();
  7. car.stop();
  8. } else {
  9. System.out.println("不存在此品牌车辆!");
  10. }
  11. }
  12. }
  13. abstract interface Car {
  14. public void run();
  15. public void stop();
  16. }
  17. // 具体实现类
  18. class Benz implements Car {
  19. public void run() {
  20. System.out.println("Benz开始启动了。。。。。");
  21. }
  22. public void stop() {
  23. System.out.println("Benz停车了。。。。。");
  24. }
  25. }
  26. class Ford implements Car {
  27. public void run() {
  28. System.out.println("Ford开始启动了。。。");
  29. }
  30. public void stop() {
  31. System.out.println("Ford停车了。。。。");
  32. }
  33. }
  34. // 工厂类
  35. class Factory {
  36. public static Car getCarInstance(String type) {
  37. Car car = null;
  38. if ("Benz".equals(type)) {
  39. car = new Benz();
  40. }
  41. if ("Ford".equals(type)) {
  42. car = new Ford();
  43. }
  44. return car;
  45. }
  46. }

工厂方法模式:有四个角色,抽象工厂模式,具体工厂模式,抽象产品模式,具体产品模式。不再是由一个工厂类去实例化具体的产品,而是由抽象工厂的子类去实例化产品;

  1. // 抽象产品角色
  2. public interface Moveable {
  3. void run();
  4. }
  5. // 具体产品角色
  6. public class Plane implements Moveable {
  7. @Override
  8. public void run() {
  9. System.out.println("plane....");
  10. }
  11. }
  12. public class Broom implements Moveable {
  13. @Override
  14. public void run() {
  15. System.out.println("broom.....");
  16. }
  17. }
  18. // 抽象工厂
  19. public abstract class VehicleFactory {
  20. abstract Moveable create();
  21. }
  22. // 具体工厂
  23. public class PlaneFactory extends VehicleFactory {
  24. public Moveable create() {
  25. return new Plane();
  26. }
  27. }
  28. public class BroomFactory extends VehicleFactory {
  29. public Moveable create() {
  30. return new Broom();
  31. }
  32. }
  33. // 测试类
  34. public class Test {
  35. public static void main(String[] args) {
  36. VehicleFactory factory = new BroomFactory();
  37. Moveable m = factory.create();
  38. m.run();
  39. }
  40. }

抽象工厂模式:与工厂方法模式不同的是,工厂方法模式中的工厂只生产单一的产品,而抽象工厂模式中的工厂生产多个产品

  1. // 抽象工厂类
  2. public abstract class AbstractFactory {
  3. public abstract Vehicle createVehicle();
  4. public abstract Weapon createWeapon();
  5. public abstract Food createFood();
  6. }
  7. // 具体工厂类,其中Food,Vehicle,Weapon是抽象类,
  8. public class DefaultFactory extends AbstractFactory {
  9. @Override
  10. public Food createFood() {
  11. return new Apple();
  12. }
  13. @Override
  14. public Vehicle createVehicle() {
  15. return new Car();
  16. }
  17. @Override
  18. public Weapon createWeapon() {
  19. return new AK47();
  20. }
  21. }
  22. // 测试类
  23. public class Test {
  24. public static void main(String[] args) {
  25. AbstractFactory f = new DefaultFactory();
  26. Vehicle v = f.createVehicle();
  27. v.run();
  28. Weapon w = f.createWeapon();
  29. w.shoot();
  30. Food a = f.createFood();
  31. a.printName();
  32. }
  33. }

简单工厂和抽象工厂的区别

简单工厂模式:

这个模式本身很简单而且使用在业务较简单的情况下。一般用于小项目或者具体产品很少扩展的情况(这样工厂类才不用经常更改)。

它由三种角色组成:

  • 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,根据逻辑不同,产生具体的工厂产品。如例子中的Driver类。
  • 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。如例中的Car接口。
  • 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现,如例子中的Benz、Bmw类。

来用类图来清晰的表示下的它们之间的关系:

设计模式 - 图3

抽象工厂模式:

先来认识下什么是产品族: 位于不同产品等级结构中,功能相关联的产品组成的家族。

设计模式 - 图4

图中的ProductA和ProductB就是两个产品树(产品层次结构);而如图所示的ProductA1和ProductB1就是一个产品族。他们都可以放到Factory1中,因此功能有所关联。同理PorductA2和ProductB2也是一个产品族。

可以这么说,它和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象。

而且使用抽象工厂模式还要满足一下条件:

  1. 系统中有多个产品族,而系统一次只可能消费其中一族产品
  2. 同属于同一个产品族的产品以其使用。
    来看看抽象工厂模式的各个角色(和工厂方法的如出一辙):

角色:

  • 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
  • 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
  • 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
  • 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。