设计模式(二)

单例模式

​ 单例对象的类必须保证只有一个实例存在。

​ 使用场景:

  1. 需要频繁的实例化然后销毁的对象

  2. 创建对象时耗时过多或者消耗资源过多,但又经常用的的对象。

  3. 有状态的工具类对象。

  4. 频繁访问数据库或文件的对象。

    有状态:就是有储存数据的功能,有状态对象,就是有实例变量的对象,可以保存数据,非线程安全

    无状态:就是一次操作,不保存数据,无状态对象,就是没有实例变量的对象,不保存数据,不变类,线程安全

    常见写法:

    1. 恶汉式

      public class Singleton {
          private static final Singleton singleton = new Singleton();
       private Singleton {}
          public static Singleton getInstance(){
              return singleton;
          }
      }
      
    2. 懒汉式

      public class Singleton {
          private static final Singleton singleton = null;
          private Singleton {}
          public static Singleton getInstance(){
              if(singleton == null){
                  singleton  = new Singleton();
              }
              return singleton;
          }
      }
      
    3. 双重检查锁

      volatitle 禁止指令重排,所有的写(write)操作都将发生在读(read)操作之前。实例化对象的过程,1. 分配内存空间,2.初始化对象,3.将对象指向刚分配的内存空间,编译器为了性能原因,将2和3重排序,

      public class Singleton {
          private volatitle static Singleton singleton = null;
          private Singleton {}
          public static Singleton getInstance(){
              if(singleton == null){   //当一次实例化结束之后,直接返回实例化对象,加快效率
                  synchronized(Singleton.class){
                      if(singleton == null){ //当线程a进入实例化完成之后,线程b进入之后判断,直接退出 ,当没有禁止重排的时候可能返回一个未初始化的对象。
                            singleton  = new Singleton();
                      }
                  }
              }
              return singleton;
          }
      }
      
    4. 匿名内部类

      jvm会保证一个类的初始化方法会在多线程环境中正确加锁,同步,如果多个线程去初始化同一个类,只有一个线程执行初始化方法,其他线程会阻塞,注意当一个线程执行完毕初始化方法后,其他线程被唤醒也不会执行初始化方法了,一个类只会初始化一次

      public class Singleton{
           private Singleton {}
           private static class Sinleton01 {
               private static final Singleton singleton = new Singleton();
           }
            public static Singleton getInstance(){
                return Sinleton01.sinleton;
            }
      }
      
    5. 枚举类

      public enum Singleton{
          INSATANCE;
      }
      

    注:可以通过反射破坏单利

    public class Client {
        public static void main(String[] args) throws Exception {
            Class<Singleton> clazz = (Class<Singleton>)Class.forName("com.course.design.singleton.Singleton");
            Constructor<Singleton> constructor = clazz.getDeclaredConstructor(null);
            constructor.setAccessible(true);//跳过权限验证
            Singleton singleton = constructor.newInstance();
            Singleton singleton1 = constructor.newInstance();
            System.out.println(singleton1);
            System.out.println(singleton);
        }
    }
    
    

    也可阻止,在私有的构造方法中加一个判断。

        private Singleton(){
            if (Singleton.singleton == null) {
                throw  new RuntimeException();
            }
        }
    

命令模式

  1. 包含角色

    • receiver 接收者角色

      干活的角色,命令传递到这里应该被执行的。

    • command 命令角色

      所需的所有命令在这里声明

    • invoker 调用者角色

      接收命令,并执行

  2. 代码

    /**
     * 抽象接收者,每个具体接收者都必须完成的命令
     */
    public abstract class Receiver {
        public abstract void doSomething();
    }
    
    public class ConcreteReceiver1 extends Receiver{
    //每个接收者都必须处理一定的业务逻辑
    public void doSomething(){
                System.out.println("接收者1 : 正在执行命令。。。。。。");
    
    }
    }
    public class ConcreteReceiver2 extends Receiver{
    //每个接收者都必须处理一定的业务逻辑
    public void doSomething(){
                System.out.println("接收者2 : 正在执行命令。。。。。。");
    
    }
    }
    
    
    public abstract class Command {
    //每个命令类都必须有一个执行命令的方法
    public abstract void execute();
    }
    
    
    public class ConcreteCommand1 extends Command {
    //对哪个Receiver类进行命令处理
    private Receiver receiver;
    //构造函数传递接收者
    public ConcreteCommand1(Receiver _receiver){
    this.receiver = _receiver;
    }
    //必须实现一个命令
    public void execute() {
    //业务处理
    this.receiver.doSomething();
    }
    }
    public class ConcreteCommand2 extends Command {
    //哪个Receiver类进行命令处理
    private Receiver receiver;
    //构造函数传递接收者
    public ConcreteCommand2(Receiver _receiver){
    this.receiver = _receiver;
    }
    //必须实现一个命令
    public void execute() {
    //业务处理
    this.receiver.doSomething();
    }
    }
    
    
    public class Invoker {
    private Command command;
    //受气包,接受命令
    public void setCommand(Command _command){
    this.command = _command;
    }
    //执行命令
    public void action(){
    this.command.execute();
    }
    }
    
    public class Client {
    public static void main(String[] args) {
    //首先声明调用者Invoker
    Invoker invoker = new Invoker();
    //定义接收者
    Receiver receiver = new ConcreteReciver1();
    //定义一个发送给接收者的命令
    Command command = new ConcreteCommand1(receiver);
    //把命令交给调用者去执行
    invoker.setCommand(command);
    invoker.action();
    }
    }
    
  3. 命令模式的优缺点

    1. 优点

      1. 类间解耦 :调用者角色与接收者之间没有任何依赖关系
      2. 可扩展性
    2. 缺点

      1. 当命令增加,导致命令类过多,臃肿
posted @ 2021-02-08 23:26  一个平凡的程序员  阅读(50)  评论(0)    收藏  举报