面向对象六大原则
-
单一职责 Single Responsibility Principle
单一职责的定义就是一个类应该只有一个引起它变化的原因,也就是一个类应该是一组相关性很高的函数、数据的封装。划分界限不是死的,大都依靠经验而定。
-
开闭原则 Open Close Principle
定义:软件中的对象(类、模板、函数等)应该对应扩展是开放的,对于修改是封闭的。主要通过继承和接口来实现。开闭原则可以使程序更加灵活和稳定。完全符合开闭原则是理想化的状态。
-
里式替换 Liskov Substitution Principle
第一定义:如果对每一个类型为S的对象O1,都有类型为T的对象O2,使得以T定义的所有程序P在所有对象O1都代换成O2时,程序P的行为没有发生变化,那么S就是类型T的子类。咋一看有点不好理解。那看第二定义:所有引用基类的地方必须能透明的使用其子类的对象。也就是说,只要父类能出现的地方,子类就能出现,而且替换为子类也不会产生任何错误或异常。实现里式替换的核心原理是抽象。抽象的实现又依赖于继承。
继承的优缺点都很明显:
优点:
代码重用,减少创建类的成本~~(少写些代码?)~~,每个子类都拥有父类的方法和属性;
子类与父类基本相似,但又与父类有所区别;
提高代码的可扩展性。
缺点:
继承是侵入式的,只要继承就必须拥有父类的所有属性和方法;
可能造成子类代码冗余、灵活性降低
-
依赖倒置 Dependence Inversion Principle
依赖倒置指代了一种特定的解耦形式,使得高层次的模块不依赖于低层次模块的实现细节(?)。
关键点:
高层模板不应该依赖底层模板,应该依赖其抽象;
抽象不应该依赖细节;
细节应该依赖抽象。
依赖倒置在Java中的表现:模板间的依赖通过抽象产生,实现类之间不发生直接的依赖关系。
-
接口隔离 Interface Segregation Principle
第一定义:客户端不应该依赖它不需要的接口。第二定义:类间的依赖关系应该建立在最小的接口上。
-
迪米特原则 Law of Demeter
也被称为最少知识原则,其定义为:一个对象应该对其他对象有最少的了解。这样可以降低耦合度,将当一个类发生改变的时候对其他类的影响降至最小。
备忘录模式
定义:在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之处保存这个状态,这样,以后就可将该对象恢复到原先保存的状态。
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。