前言:设计模式简述
设计模式是一种编程思想,它实际就是面向对象思想的实际运用。它体现了复用性、低耦合性、高扩张性等等面向对象的思想,能大大提高我们的项目质量。
总共有23种设计模式,这些模型在大多时候是混合在一起使用的,它们各自有优点和缺点,应结合实际正确使用。
-
创建型模式
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式 -
结构式模式
设配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式 -
行为型模式
模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、访问者模式、职责链模式
创建者模式
单例模式
顾名思义,单例模式就是一个类在整个运行时期,只有一个实例对象。为什么会出现单例模式呢?比如对于某些类,它的创建和销毁过程过于复杂,且它可以复用,如果对它进行频繁的创建和销毁,就会浪费大量资源。所以单例模式应运而生。
- 优点:可以节省不必要浪费的资源
- 特点
- 类在整个运行期间都只有一个实例
- 构造器私有,有公开的构造实例的静态方法
- 有一个静态的类实例对象,表示唯一存在的实例对象
下面来看代码,单例模式有两种实现方式,分别是饿汉式和懒汉式:
饿汉式
public class Test {
private Test() {
}
private static final Test test = new Test();
public static Test getInstance(){
return test;
}
}
懒汉式
public class Test {
private Test() {
}
private static Test test;
public static Test getInstance(){
if(test == null){
test = new Test();
}
return test;
}
}
顾名思义,饿汉式就是加载的时候就去创建实例对象,懒汉式就是在第一次使用的时候再去创建实例对象,毫无疑问,肯定是懒汉式比较优秀。不过懒汉式也有缺点,就是它是线程不安全的。
懒汉式不是线程安全的,那怎么解决呢?加锁嘛,我们可以用synchronized锁住就好。但是如果直接在getInstance()
方法上加锁又对性能影响过大,所以我们可以考虑一下方法。
public class Test {
private Test() {
}
private static Test test;
public static synchronized Test getInstance(){
if(test == null){
synchronized (Test.class){
test = new Test();
}
}
return test;
}
}
如上面,使用类锁便可解决,但真的解决了吗?并没有,这个代码还是有问题的,因为假如已经有多个线程进入if语句里,就会造成创建多个实例对象,从而破坏了单例模式,所以需要再加一个判断,也就是我们的双重检测锁模式的懒汉式,也叫DLC懒汉式。
public class Test {
private Test() {
}
private static Test test;
public static synchronized Test getInstance(){
if(test == null){
synchronized (Test.class){
if(test == null){
test = new Test();
}
}
}
return test;
}
}
还没完,如果你对多线程熟悉,你会发现test = new Test();
这一步并不是原子操作,它分为3步操作,所以可能会出现指令重排,造成错误,所以应该将test对象使用volatile修饰。所以完整的懒加载且线程安全的写法如下:
public class Test {
private Test() {
}
private static volatile Test test;
public static synchronized Test getInstance(){
if(test == null){
synchronized (Test.class){
if(test == null){
test = new Test();
}
}
}
return test;
}
}
但是但是,还是没完,以上的写法其实都是可以通过反射来破坏单例模式的,对此我们也有对策,就是采用枚举类型,枚举类型是无法通过反射来破坏的,它会报出异常。但是枚举类型又不能用于懒汉式,它的原理和饿汉式相同,类加载的时候就已经构建好实例对象了。
所以当前暂时没有同时满足懒汉式、线程安全、反射不能破坏的写法,只能满足其中的两种,这就是单例模式,如果有兴趣也可以看下以下两个视频:
https://www.bilibili.com/video/BV1pt4y1X7kt?from=search&seid=16397970082293022209
https://www.bilibili.com/video/BV1K54y197iS?from=search&seid=16397970082293022209
工厂模式
还是先说说为什么要有工厂模式吧。工厂模式是为了解耦:把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。
其次,工厂模式可以降低代码重复。如果创建对象B的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。我们可以这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的创建过程的修改维护。
同理由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。
下面有一篇博客,个人觉得写得很不错,对于了解工厂模式的作用有很大帮助:
https://blog.csdn.net/kocscs123/article/details/53243847?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159594280519724811852397%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=159594280519724811852397&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v3~pc_rank_v2-1-53243847.first_rank_ecpm_v3_pc_rank_v2&utm_term=%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%E4%BD%9C%E7%94%A8&spm=1018.2118.3001.4187
工厂模式分为两种:简单工厂模式和工厂方法模式,它们的核心本质就是实例化对象不直接new出来,而是使用工厂方法创建,正如我们熟知的一些类有Factory,如线程池。这样实现了创建者和调用者的分离。
- 优点:降低了耦合度,使创建者和使用者分离
- 分类
- 简单工厂模式:用来生产同一等级结构中的任意产品,代码量较小,但增加新产品需要修改已有代码
- 工厂方法模式:用来生产同一等级结构中的固定产品,代码量较大,但支持增加新产品,只需在原有基础上扩展即可
下面举例来演示下这两种方法,用买车的例子。假如有两种车,分别为AB种,它们实现了car这个接口,现在有用户来买车:
简单工厂模式
public interface Car {
void name();
}
class A implements Car{
@Override
public void name() {
System.out.println("A种车");
}
}
class B implements Car{
@Override
public void name() {
System.out.println("B种车");
}
}
class Factory{
public static A getA(){
return new A();
}
public static B getB(){
return new B();
}
}
class Customer{
public static void main(String[] args) {
//不直接new对象,而是通过工厂创建实例对象
A a = Factory.getA();
a.name();
B b = Factory.getB();
b.name();
}
}
上面便是简单工厂模式实现,但是如果我们新增一种车,则必定要改动工厂的代码,不符合我们的面向对象思想。下面我们来看下工厂方法模式:
工厂方法模式
public interface Car {
void name();
}
class A implements Car{
@Override
public void name() {
System.out.println("A种车");
}
}
class B implements Car{
@Override
public void name() {
System.out.println("B种车");
}
}
interface CarFactory{
Car getInstance();
}
class ACarFactory implements CarFactory{
@Override
public Car getInstance() {
return new A();
}
}
class BCarFactory implements CarFactory{
@Override
public Car getInstance() {
return new B();
}
}
class Customer{
public static void main(String[] args) {
//不直接new对象,而是通过工厂创建实例对象
A a = (A)new ACarFactory().getInstance();
a.name();
B b = (B)new BCarFactory().getInstance();
b.name();
}
}
从上面我们可以很清楚的看到,如果我们有了一种新的车,我们只需要新建一个新的工厂然后实现工厂接口就可以了,且各种车的工厂是独立的,我们也可以在各种车工厂里提供特有方法,但是弊端也很明显,就是代码量大大增加,每多一种车就要多出一个工厂。所以我们要根据实际情况决定。