zoukankan      html  css  js  c++  java
  • 几个设计模式总结

    一、单例模式

    一般有八种写法,这里简单记录一下思路,首先单例需要构造器是private,然后一般有个static方法获得这个类的实例。

    饿汉式:

      所谓饿汉式其实应该是相对于懒汉式,懒汉式就是当你用到这个类的时候,才初始化实例并给你,所以这个饿汉式就是在类加载的时候就初始化了这个对象,所以有两种方法,一种是

    private final static Singleton INSTANCE = new Singleton();
    
    
     public static Singleton getInstance(){
            return INSTANCE;
        }

    还有就是利用static块,

    private static Singleton instance;
    
        static {
            instance = new Singleton();
        }
    
    
    public static Singleton getInstance() {
            return instance;
        }

    懒汉就是要用到的时候,才去初始化或者说new这个对象。

    这就有线程安全的问题了,所以懒汉这里有

      线程不安全的情况;

      线程安全getInstance同步方法的情况;

      采用同步块,只判断一次instance==null所以还是线程不安全的情况;

      采用同步块但双重检查的情况;

    然后还有两种分别是静态内部类和枚举的写法。

    静态内部类:

    public class Singleton {
    
        private Singleton() {}
    
        private static class SingletonInstance {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        public static Singleton getInstance() {
            return SingletonInstance.INSTANCE;
        }
    }
    View Code

    种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。

    类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

    枚举:

    public enum Singleton {
        INSTANCE;
        public void whateverMethod() {
    
        }
    }
    View Code

    单例模式的写法可以参考:https://www.cnblogs.com/zhaoyan001/p/6365064.html——《单例模式的八种写法比较 》

    二、工厂模式

    有三种,简单工厂模式,工厂模式,抽象工厂模式。

    简单工厂模式:

    1. 产品有个接口。

    2. 有一个工厂类,提供这个接口的产品。

    常见模式就是,工厂类一个switch块,有个方法根据你传进去的String值来返回相应的产品实例。

    public interface Product {
        
    }
    
    public class easyProductFactory {
        public static Product getProduct(String name) {
            switch(name) {
               case "xxx": return new ProductImplA();
               .......
            }
        }
    }

    但这种版本的话,如果你传进去的字符串出错了,就得不到正确的产品,所以有个改良版的,其实就是把switch改成直接调用一个getXXXProduct的方法

    public class EasyProductFactory {
        public Product getAProduct() {
             return new ProductAImpl();
        }  
    }
    
    
    //用的时候就easyProductFactory.getAProduct();

    还有个改版就:静态方法版的:

    public class EasyProductFactory {
        public static Product getProductA(){  
            return new productAImpl();  
        }  
    }    
    
    
    //用的时候不用new工厂类了,直接EasyProductFactory.getProductA();就好

    然后是工厂方法模式或者说是工厂模式

    1. 有个产品接口

    2. 有个工厂接口

    工厂方法模式是对简单工厂模式进一步的解耦,因为在工厂方法模式中是一个子类对应一个工厂类,而这些工厂类都实现于一个抽象接口。这相当于是把原本会因为业务代码而庞大的简单工厂类,拆分成了一个个的工厂类,这样代码就不会都耦合在同一个类里了。

    所以其实就从原来的单一的一个生产产品的固定类,变成了一个或者多个实现了Factory接口的工厂是实现类那样咯。

    抽象工厂方法

    1. 有大于等于一类个产品接口

    2. 有个工厂接口

    3. 工厂接口中提供不止一种产品

    就为什么要有这个抽象工厂呢,就之前的情况呢,一个Product接口是可以抽象出所有的产品的共性的,但如果不能的话,或者说我们需要多个产品的话,就要用这个模式了。

    比如说水果接口和玩具接口,这就是两个完全不同的产品了,你无法只用一个Product来抽象吧,那么这个时候的大概形式就是:

    public interface Fruit {
    
    }
    
    public interface Toy {
    
    }
    
    public interface ProductFactory {
        Fruit getFruit();
        Tou getToy();
    }
    
    //然后再分别有水果的实现类,玩具的实现类还有工厂的实现类,工厂的实现类需要提供两种产品。

    工厂模式的例子或者说是说明可以参考这两篇文章:

    https://www.cnblogs.com/zailushang1996/p/8601808.html——《java 三种工厂模式 》

    https://blog.csdn.net/u012156116/article/details/80857255——《简单工厂模式、工厂模式以及抽象工厂模式(具体)》这个可以只看它对抽象工厂模式的介绍

    三、模板方法模式

    一般就是有个抽象类,里面有很多可以重用的方法,你不想实现那么多次,就把它都放在抽象的父类中,然后留下几个protected的abstract方法或者是普通的方法给子类去实现。

    比如一个JDBC的实现,如果每个请求都完整走一遍的话,你会发现有很多的代码都是重复写的。

    我们先来看看使用JDBC操作数据库需要经过哪些步骤:

    1. 获取数据库连接

    2. 通过数据库连接得到Statement对象

    3. 使用Statement对象进行增删改查

    4. 处理异常

    5. 关闭连接释放资源

    我们再来区分一下这些步骤中,哪些是可变部分,哪些是不可变部分:

    1. 获取数据库连接(不可变)

    2. 通过数据库连接得到Statement对象(不可变)

    3. 使用Statement对象进行增删改查(可变)

    4. 处理异常(不可变)

    5. 关闭连接释放资源(不可变)

    我们可以看到,在5个步骤中,4个是不可变的,只有一个步骤是可变的

    所以我们其实可以用一个模板abstract类来封装这些不变的方法,然后JDBC具体的类就继承这个模板类,重写CRUD方法就好了。

    典型的模板方法设计模式还有:

    HttpServlet中的doGet、doPost方法;

    Lock的底层实现AQS也就是抽象队列同步器。

    四、观察者模式

    定义:

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

    说人话就是:

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

    这个模式的一个结构图:

    可以看到,该模式包含四个角色

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

     可以类比一个微信公众服务号,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息。

    具体的例子和代码见博客:https://www.cnblogs.com/luohanguo/p/7825656.html——《JAVA设计模式之观察者模式 》

    五、装饰器模式和代理模式

    先主要讲一下装饰器模式的思路吧。

    装饰器模式,顾名思义,就是对已经存在的某些类进行装饰,以此来扩展一些功能。

    一个很简单的例子就是,咖啡,牛奶咖啡,牛奶咖啡加冰块。

    一般的样子就是:

    • 有个接口,比如饮料接口Drink
    • 然后有个接口的实现类,比如Coffee
    • 然后有个抽象类也去实现Drink接口,里面维护着一个被装饰的类的对象,像Coffee coffee。
    • 然后是真正实现装饰的类,就上面那个抽象类的子类——这些类在普通的Coffee上装饰了些东西上去

    看完会发现,卧槽这东西和代理模式怎么这么像——代理模式,和被代理类实现同一个接口,然后就可以代替被代理类去接受用户的请求,而代理类里面是包装着真正类的,在调用真正类的某个请求方法的过程中,可以另外做点其他的事情。

    那么这两者有什么区别呢?其实我觉得大体上是差不多的了,要说区别就是思维还有用法,比如这里我们用两种设计模式去做同一件事情——在一个方法调用之前调用before(),调用只有调用after()。

    这是个很常见的前置增强和后置增强,所以代理模式显然就是在代理类中类似这样的代码

    @Override
        public void sayHello(String name) {
            before();
            greetingImpl.sayHello(name);
            after();
        }

    而如果是装饰者模式的话,就是这样的设计结构:

    然后用起来是这个样子的:

    public static void main(String[] args) {
            Greeting greeting = new GreetingAfter(new GreetingBefore(new GreetingImpl()));
            greeting.sayHello("Jack");
        }

    有没有像我们的IO模型的样子,就这样理解吧hh

    关于装饰器模式的介绍可以看——https://blog.csdn.net/smy_0114/article/details/80653127——《设计模式之-装饰器模式》

    关于装饰器模式和代理模式的差别,还有上面我的例子的详细代码可以看——https://blog.csdn.net/andong154564667/article/details/80258061——《代理模式和装饰器模式区别》

  • 相关阅读:
    English 2
    速算24点
    心理学1
    从微服务到函数式编程
    034 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 01 流程控制概述
    033 01 Android 零基础入门 01 Java基础语法 03 Java运算符 13 运算符和表达式知识点总结
    032 01 Android 零基础入门 01 Java基础语法 03 Java运算符 12 运算符和if-else条件语句的综合案例——闰年问题
    031 01 Android 零基础入门 01 Java基础语法 03 Java运算符 11 运算符的优先级
    030 01 Android 零基础入门 01 Java基础语法 03 Java运算符 10 条件运算符
    029 01 Android 零基础入门 01 Java基础语法 03 Java运算符 09 逻辑“非”运算符
  • 原文地址:https://www.cnblogs.com/wangshen31/p/10531610.html
Copyright © 2011-2022 走看看