几个设计模式总结

一、单例模式

一般有八种写法,这里简单记录一下思路,首先单例需要构造器是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——《代理模式和装饰器模式区别》

 

posted @ 2019-03-14 17:20  汪神  阅读(170)  评论(0编辑  收藏  举报