Skip to content

设计模式

Posted on:November 7, 2020 at 11:01:48 GMT+8

面向对象六大原则

备忘录模式

定义:在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之处保存这个状态,这样,以后就可将该对象恢复到原先保存的状态。

UMLofMemento

package memento;

public class Game {
    private int mCheckpoint = 1;
    private int mLifeValue = 100;
    private String mWeapon = "Lightsaber";

    void play() {
        System.out.println("playing " + String.format("level%d", mCheckpoint));
        mLifeValue -= 10;
        System.out.println("next level");
        mCheckpoint++;
        System.out.println("reach " + String.format("level%d", mCheckpoint));
    }

    void quit() {
        System.out.println("attribute:"+this.toString());
        System.out.println("quit");
    }

    Memento createMemento() {
        Memento memento = new Memento();
        memento.mCheckpoint=mCheckpoint;
        memento.mLifeValue = mLifeValue;
        memento.mWeapon=mWeapon;
        return memento;
    }

    void restore(Memento memento) {
        this.mCheckpoint=memento.mCheckpoint;
        this.mLifeValue =memento.mLifeValue;
        this.mWeapon=memento.mWeapon;
        System.out.println("restores attribute:"+this.toString());
    }

    @Override
    public String toString() {
        return "Checkpoint " + mCheckpoint + " LifeValue " + mLifeValue + " Weapon " + mWeapon;
    }
}

单例模式

单例:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

public class LogUtil {

	private static LogUtil sLogUtil;
	public final int DEGUB = 0;
	public final int INFO = 1;
	public final int ERROR = 2;
	public final int NOTHING = 3;
	public int level = DEGUB;

	private LogUtil() {
	}

    // 双重锁定(DCL)(Double-Check Locking)
	public static LogUtil getInstance() {
		if (sLogUtil == null) {
			synchronized (LogUtil.class) {
				if (sLogUtil == null) {
					sLogUtil = new LogUtil();
				}
			}
		}
		return sLogUtil;
	}

	public void debug(String msg) {
		if (DEGUB >= level) {
			System.out.println(msg);
		}
	}

	public void info(String msg) {
		if (INFO >= level) {
			System.out.println(msg);
		}
	}

	public void error(String msg) {
		if (ERROR >= level) {
			System.out.println(msg);
		}
	}

}

只有在sLogUtil还没被初始化的时候才会进入到第3行,然后加上同步锁。等sLogUtil一但初始化完成了,就再也走不到第3行了,这样执行getInstance()也不会再受到同步锁的影响,效率上会有一定的提升。

private static volatile Singleton instance;

private Singleton() {}

// 双重锁定(Double-Check Locking)
public static Singleton getInstance() {
	if (instance == null) {
		synchronized (Singleton.class) {
			if (instance == null) {
				instance = new Singleton();
			}
		}
	}
	return instance;
}

更多创建方法

第 1 种方式:

publci 静态成员是 final 域

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {}
}

风险是可以通过反射调用私有构造方法,要抵御可以在构造方法中判断,创建第二个实例时抛出异常。

第 2 种方式:

public 的是静态工厂方法

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

有同样的风险。

同时这两种方法还有反序列化的问题。解决需要将所有实例域声明为 transient,并提供 readResolve 方法:

private Object readResolve() {
    return INSTANCE;
}

上面两种又称为饿汉模式。

第 3 种,通过枚举实现单例:

public enum Elvis {
    INSTANCE;
    public void someMethod() {

    }
}

没有反射和序列化风险。

第 4 种,懒汉模式

class Singleton {
    private static final Singleton INSTANCE;
    private Singleton() {}
    public static Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCW = new Singleton();
        }
        return INSTANCE;
    }
}

线程不安全。

第 5 种,懒汉模式、线程安全

class Singleton {
    private static final Singleton INSTANCE;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }
}

效率低。

第 6 种,双重检查 DCL

class Singleton {
    private static final Singleton INSTANCE;
    private Singleton() {}
    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized(Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

如果单例已经创建,则不再进行同步。

存在 DCL 失效的问题。

INSTANCE = new Singleton();

可以分为三个步骤:

给 Singleton 实例分配内存空间;

调用构造方法,初始化成员变量;

将 INSTANCE 对象执行分配的内存空间(此时 INSTANCE 就非 null 了)。

因为 JVM 的指令重排序,可以 1 - 3 - 2 这样执行,就可能造成线程读取到的是一个还未初始化的实例,造成 DCL 失效。

解决是加上 volatile 关键字。

更多方式

观察者模式

代理模式

为其他对象提供一种代理以控制对这个对象的访问。[DP]

静态代理

提供一个代理对象,代理对象持有对真实对象的一个引用,在代理对象的方法中调用真实对象的方法实现代理。

动态代理

需要实现 InvocationHandler 接口。

动态代理可以实现 AOP,可以在不改动已有代码结构的情况下增强或控制对象的行为。

享元模式

运用共享技术有效地支持大量细粒度的对象。

Android 中的 Message。