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——《代理模式和装饰器模式区别》

  • 相关阅读:
    Servlet学习总结
    Tomcat学习总结1
    第44周星期日反思
    第44周星期一Tomcat学习2
    第44周星期五忙碌文档的一天
    第44周星期六好文章摘录
    laravel 5.6接入微信第三方授权登陆的主要步骤
    laravel多表登录出现路由调用错误
    cURL error 60: SSL certificate problem...
    传说中Python最难理解的点|看这完篇就够了(装饰器)
  • 原文地址:https://www.cnblogs.com/wangshen31/p/10531610.html
Copyright © 2011-2022 走看看