设计模式

设计原则 S.O.L.I.D

1. 单一责任原则

The Single Responsibility Principle

修改一个类的原因应该只有一个。

换句话说就是让一个类只负责一件事,当这个类需要做过多事情的时候,就需要分解这个类。

如果一个类承担的职责过多,就等于把这些职责耦合在了一起,一个职责的变化可能会削弱这个类完成其它职责的能力。

2. 开放封闭原则

The Open Closed Principle

类应该对扩展开放,对修改关闭。

扩展就是添加新功能的意思,因此该原则要求在添加新功能时不需要修改代码。

符合开闭原则最典型的设计模式是装饰者模式,它可以动态地将责任附加到对象上,而不用去修改类的代码。

3. 里氏替换原则

The Liskov Substitution Principle

子类对象必须能够替换掉所有父类对象。

继承是一种 IS-A 关系,子类需要能够当成父类来使用,并且需要比父类更特殊。

如果不满足这个原则,那么各个子类的行为上就会有很大差异,增加继承体系的复杂度。

4. 接口分离原则

The Interface Segregation Principle

不应该强迫客户依赖于它们不用的方法。

因此使用多个专门的接口比使用单一的总接口要好。

5. 依赖倒置原则

The Dependency Inversion Principle

高层模块不应该依赖于低层模块,二者都应该依赖于抽象; 抽象不应该依赖于细节,细节应该依赖于抽象。

高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于低层模块,那么低层模块的改动就会直接影响到高层模块,从而迫使高层模块也需要改动。

单例

确保一个类只有一个实例,并提供该实例的全局访问点。

使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。

懒汉式 - 非线程安全

public class Singleton1 {
    private static Singleton1 uniqueInstance;

    private Singleton1() {}

    public static Singleton1 getInstance() {
        if (uniqueInstance == null)
            uniqueInstance = new Singleton1();
        return uniqueInstance;
    }
}

懒汉式 - 线程安全

public class Singleton2 {
    private static Singleton2 uniqueInstance;

    private Singleton2() {}

    // 使用同步方法,避免多线程下创建多个实例
    public static synchronized Singleton2 getInstance() {
        if (uniqueInstance == null)
            uniqueInstance = new Singleton2();
        return uniqueInstance;
    }
}

双重校验锁

public class Singleton3 {
    // volatile确保当uniqueInstance被实例化时,其他线程能感知到
    private static volatile Singleton3 uniqueInstance;

    private Singleton3() {}

    // 双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁
    public static Singleton3 getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton3.class) {
                // 进行第二次校验
                if (uniqueInstance == null)
                    uniqueInstance = new Singleton3();
            }
        }
        return uniqueInstance;
    }
}

静态内部类

当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。

这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

public class Singleton4 {

    private Singleton4() {}

    private static class SingletonHolder {
        private static final Singleton4 INSTANCE = new Singleton4();
    }

    public static Singleton4 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

简单工厂

在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。

简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。

这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。

// pizza接口
public interface Pizza {
    void prepare();
    void bake();
    void cut();
    void box();
}
// 各种Pizza实现类,以CheesePizza为例
public class CheesePizza implements Pizza {
    public void prepare() { System.out.println("Preparing cheese pizza...") }
    public void bake() { System.out.println("Baking cheese pizza...") }
    public void cut() { System.out.println("Cutting cheese pizza...") }
    public void box() { System.out.println("Boxing cheese pizza...") }
}
// 简单pizza工厂,帮助客户创建pizza
public class SimplePizzaFactory {
    // 所有的客户都使用这个方法来实例化新对象
    public Pizza createPizza(String type) {
        Pizza pizza = null;

        // 创建不同类型的pizza
        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("clam")) {
            pizza = new ClamPizza();  
        }
        return null;
    }
}
// 客户类
public class PizzaStore {
    // 保存一个对SimplePizzaFactory的引用
    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza;

        // 通过传入订单类型来使用工厂创建pizza
        pizza = factory.createPizza(type);

        return pizza;
    }
}

工厂

定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。

在简单工厂中,是由一个对象负责所有具体类的实例化;而在工厂中,是由一群子类来负责实例化。

// 抽象pizza工厂
public abstract class PizzaStore {
    public void orderPizza(String type) {
        Pizza pizza;

        // PizzaStore的子类在createPizza()方法中处理对象的实例化
        pizza = createPizza(type);

        return pizza;
    }

    // 实例化pizza的责任被移到一个方法中,这个方法就如同是一个工厂
    protected abstract Pizza createPizza(String type);
}
// 负责创建pizza的具体子类,以NewYorkPizzaStore为例
public class NYPizzaStore extends PizzaStore {
    public Pizza createPizza(String type) {
        Pizza pizza;
        // 创建不同类型的NYStylePizza
        if (type.equals("cheese")) {
            pizza = new NYStyleCheesePizza();
        } else if (type.equals("clam")) {
            pizza = new NYStyleClamPizza();  
        }
        return pizza;
    }
}

抽象工厂

迭代器

观察者

EventListener、消息队列常用的订阅-发布模型

代理

扩展阅读:动态代理与静态代理

由代理对象组合基础对象,控制对基础对象的访问,扩展新的功能。

代理对象 (Proxy) 通过组合的方式持有基础对象 (RealSubject) 的引用,并且实现了Subject接口。从客户端的角度,Proxy代理对象可以作为Subject接口的替代品。

静态代理

public interface Payment {
    void pay();
}
public class RealPayment implements Payment {
    public void pay() {
        System.out.println("Paying...");
    }
}
public class Proxy implements Payment {
    private Payment payment;

       // 保存被代理的类的引用
    public Proxy(Payment p) {
        payment = p;
    }

    public void pay() {
        // 扩展功能
        System.out.println("Before payment");
        // 调用被代理的类的方法
        payment.pay();
        System.out.println("After payment")
    }
}

Java动态代理

// 定义代理对象调用方法时希望执行的动作
Object invoke(Object proxy, Method method, Object[] args)

// 构造实现指定接口的代理类的一个新实例,需要提供类加载器、接口和处理器
static Object new ProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
                                InvocationHandler handler)

以下程序使用代理对象对二分查找进行跟踪。首先使用1 - 1000 整数的代理填充数组,然后调用Arrays类中的binarySearch方法在数组中查找一个随机整数,最后打印出与之匹配的元素。

public class ProxyTest {
    public static void main(String[] args) {
        Object[] elements = new Object[100];
        for (int i = 0; i < elements.length; i++) {
            Integer value = i + 1;
            InvocationHandler handler = new TraceHandler(value);
            // 创建一个代理类
            Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class }, handler);
            elements[i] = proxy;
        }

        // 生成一个随机数
        Integer key = new Random().nextInt(elements.length) + 1;

        // 在数组中寻找该随机数
        int result = Arrays.binarySearch(elements, key);

        System.out.println(result);
    }
}

// 动态代理类,用于跟踪方法的执行流程
class TraceHandler implements InvocationHandler {
    private Object target;

    public TraceHandler(Object t) {
        target = t;
    }

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable{
        System.out.print(target);
        // 打印方法名
        System.out.print("." + m.getName() + "(");
        // 打印方法参数
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                System.out.print(args[i]);
                if (i < args.length - 1) System.out.print(", ");
            }
        }
        System.out.println(")");
        // 执行方法
        return m.invoke(target, args);
    }
}

运行结果:

50.compareTo(77)
75.compareTo(77)
88.compareTo(77)
81.compareTo(77)
78.compareTo(77)
76.compareTo(77)
77.compareTo(77)
76

最后更新于