四、设计模式
1、单例模式
定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
使用场景
- 确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源
- 某个类型的对象只应该有一个
常见的实现方式
饿汉单例模式
public class Singleton {
private static final Singleton singleton = new Singleton();
//构造函数私有化
private Singleton() { }
//公有的静态函数,对外暴露获取单例对象的接口
public static Singleton getInstance() {
return singleton;
} }
- 饿汉单例模式采用的是静态变量 + fianl关键字的方式来确保单例模式,应用启动的时候就生成单例对象,效率不高
懒汉模式
public class Singleton {
private static Singleton singleton;
//构造函数私有化
private Singleton() { }
//公有的静态函数,对外暴露获取单例对象的接口
public static synchronized Singleton getInstance()
{
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
} }
- 懒汉模式的主要问题在于由于加了synchronized关键字,每调用一次getInstance方法,都会进行同步,造成了不必要的开销
以上的2种模式用的都不多,了解一下就好,下面介绍平时用得比较多的单例模式
Double Check Lock(DCL)模式(双重检查锁定模式)
public class Singleton {
private volatile static Singleton singleton = null;
//构造函数私有化
private Singleton() { }
//公有的静态函数,对外暴露获取单例对象的接口
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
} } }
return singleton;
} }
- DCL模式是使用最多的单例模式,它不仅能保证线程安全,资源利用率高,第一次执行getInstance时单例对象才会实例化;同时,后续调用getInstance方法时又不会有懒汉模式的重复同步的问题,效率更高;在绝大多数情况下都能保证单例对象的唯一性
- DCL模式需要注意要用volatile关键字,否则还是会导致创建多个实例
- DCL模式的缺点是第一次加载时由于需要同步反应会稍慢;在低于JDK1.5的版本里由于Java内存模型的原因有可能会失效
静态内部类单例模式
public class Singleton {
private Singleton() { }
public static Singleton getInstance() {
return SingletonHolder.sInstance;
}
//静态内部类
private static class SingletonHolder {
private static final Singleton sInstance = new Singleton();
} }
- 第一次加载Singleton类时不会初始化sInstance,只有在第一次调用getInstance方法时才会初始化sInstance,延迟了单例对象的实例化
- 静态内部类单例模式不仅能保证线程安全也能保证单例对象的唯一性
静态内部类单例模式和DCL模式是推荐的单例实现模式
枚举单例
public enum Singleton { INSTANCE; }
- 默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例
- 其他的单例模式,在一种情况下会出现失效的情况——反序列化,但是枚举即使在反序列化情况下也不会失效
- 总结
- 单例模式是运用频率很高的模式,由于在客户端一般没有高并发的情况,现在的JDK版本也已经到了9了,一般推荐用DCL模式和静态内部类2种实现。
- 单例对象的生命周期很长,如果持有Context,很容易引发内存泄漏,所以传递给单例对象的Context最好是Application Context
2、工厂模式
定义
定义一个用于创建对象的接口,让子类决定实例化哪个类
使用场景
- 在任何需要生成复杂对象的地方,都可以使用工厂方法模式。复杂对象适合使用工厂模式,用new就可以完成创建的对象无需使用工厂模式。
- 简单工厂模式
public interface Car {
void run();
}
public class Audi implements Car{
@Override
public void run() {
System.out.println("=====奥迪run");
}
}
public class Jeep implements Car {
@Override
public void run() {
System.out.println("======Jeep run");
}
}
public class CarFactory {
public static Car createCar(String type) {
if ("Audi".equals(type)) {
return new Audi();
} else if ("Jeep".equals(type)) {
return new Jeep();
} else {
return null;
}
}
}
使用:
public class TestFactory {
public static void main(String[] args) {
Car audiCar = CarFactory.createCar("Audi");
audiCar.run();
Car jeepCar = CarFactory.createCar("Jeep");
jeepCar.run();
}
}
- 工厂方法模式:工厂方法模式和简单工厂模式最大的不同在于简单工厂模式只有一个(对于一个项目或者一个独立模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。
public interface CarFactory {
Car createCar();
}
public class JeepFactory implements CarFactory{
@Override
public Car createCar() {
return new Jeep();
}
}
public class AudiFactory implements CarFactory{
@Override
public Car createCar() {
return new Audi();
}
}
使用:
Car audiCar = new AudiFactory().createCar();
audiCar.run();
Car jeepCar = new JeepFactory().createCar();
jeepCar.run();
- 抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族);抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。
public interface Car {
void run();
void start();
}
class Audi implements Car{
@Override
public void run() {
System.out.println("====奥迪车跑的快");
}
@Override
public void start() {
System.out.println("====奥迪车启动快");
}
}
class Otto implements Car {
@Override
public void run() {
System.out.println("====奥拓车跑的慢");
}
@Override
public void start() {
System.out.println("====奥拓车启动慢");
}
}
public interface Animal {
void run();
}
class Cat implements Animal{
@Override
public void run() {
System.out.println("====小喵跑的慢");
}
}
class Dog implements Animal{
@Override
public void run() {
System.out.println("=====旺财跑的快");
}
}
public interface AbstractFactory {
Animal createAnimal();
Car createCar();
}
public class CreateObjectFactory implements AbstractFactory{
@Override
public Animal createAnimal() {
return new Dog();
}
@Override
public Car createCar() {
return new Audi();
}
}
public class CreateObjectFactory2 implements AbstractFactory{
@Override
public Animal createAnimal() {
return new Cat();
}
@Override
public Car createCar() {
return new Otto();
}
}
public class Test {
public static void main(String[] args) {
Car audiCar = new CreateObjectFactory().createCar();
Animal dogAnimal = new CreateObjectFactory().createAnimal();
audiCar.run();
audiCar.start();
dogAnimal.run();
Car ottoCar = new CreateObjectFactory2().createCar();
Animal cataAnimal = new CreateObjectFactory2().createAnimal();
ottoCar.run();
ottoCar.start();
cataAnimal.run();
}
3、适配器模式
定义
适配器模式把一种接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
使用场景
- 系统需要使用现有的类,而此类的接口不符合系统的需要,即接口不兼容
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的一些类一起工作
- 需要一个统一的输出接口,而输入端的接口不可预知
3大角色
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
实现的要点
- 适配器模式分2种,类适配器模式和对象适配器模式。2种模式的区别在于实现适配的方法不同,类适配器模式通过继承需要适配的接口,而对象适配器模式则是通过组合的形式实现接口兼容的效果。因而对象适配器模式更加灵活也使用得更多,我们这里主要就介绍对象适配器模式
- 对象适配器模式的实现关键在于直接将要被适配的对象传递到适配器类里面,并且适配器类实现目标的接口,从而在内部进行接口的转换
- 被适配的类:
public class Adaptee {
public void request() {
System.out.println("=====可以完成客户需要的功能");
}
}
目标抽象类/接口:
public interface Target {
void hanleRequest();
}
适配器:
public class Adapter extends Adaptee implements Target{
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
super();
this.adaptee = adaptee;
}
@Override
public void hanleRequest() {
adaptee.request();
}
}
public class Test {
public void test1(Target target) {
target.hanleRequest();
}
public static void main(String[] args) {
Test test = new Test();
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
test.test1(target);
}
}
4、观察者模式
定义
定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
使用场景
- 关联行为场景
- 事件多级触发场景
- 跨系统的信息交换场景,如消息队列、事件总线的处理机制
四大角色
- 抽象主题,也就是被观察者(Observable),抽象主题把所有的观察者对象的引用保存在一个集合里,每个主题可以有任意数量的观察者,抽象主题提供接口,可以增加和删除观察者对象
- 具体的主题(具体的被观察者),也就是抽象主题的子类,该角色将有关状态存入具体观察者对象,在具体主题内部状态发生改变时,通知所有注册过的观察者
- 抽象观察者,观察者的抽象类,定义了一个更新的接口
- 具体的观察者,实现了抽象观察者的更新接口,在被观察者状态发生变化时更新自身的状态
Subject:就是“被观察”的角色,它将所有观察者对象的引用保存在一个集合中。
Observer:是抽象的“观察”角色,它定义了一个更新接口,使得在被观察者状态发生改变时通知自己。
ConcreteObserver:具体的观察者。
public interface Observer {
void update(Subject subject);
}
public class Subject {
protected List<Observer> list = new ArrayList<Observer>();
public void registerObserver(Observer obs) {
list.add(obs);
}
public void removeObserver(Observer obs) {
list.remove(obs);
}
//通知所有观察者更新状态
public void notifyAllObservers() {
for (Observer obs : list) {
obs.update(this);
}
}
}
public class ConcreteSubject extends Subject{
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
//主题对象(目标对象)值发生了变化,请通知所有的观察者
this.notifyAllObservers();
}
}
public class ObserverA implements Observer{
private int myState;//myState需要和目标对象的state保持一致
@Override
public void update(Subject subject) {
setMyState(((ConcreteSubject)subject).getState());
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
public class Test
public static void main(String[] args) {
// 目标对象
ConcreteSubject subject = new ConcreteSubject();
//创建多个观察者
ObserverA obs1 = new ObserverA();
ObserverA obs2 = new ObserverA();
ObserverA obs3 = new ObserverA();
//将观察者添加到subject对象的观察队伍中
subject.registerObserver(obs1);
subject.registerObserver(obs2);
subject.registerObserver(obs3);
System.out.println("===" +obs1.getMyState());
System.out.println("===" +obs2.getMyState());
System.out.println("===" +obs3.getMyState());
//改变subject的状态
subject.setState(1000);
System.out.println("===" +obs1.getMyState());
System.out.println("===" +obs2.getMyState());
System.out.println("===" +obs3.getMyState());
}
}
5、代理模式(委托模式)
定义
为其他对象提供一种代理以控制对这个对象的访问。
自己的事交给别人去做,分别返回处理结果即可,如异步线程;
通过代理,控制对对象的访问。可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。
静态代理
public interface Star {
/**
* 面谈
*/
void confer();
/**
* 签合同
*/
void signContract();
/**
* 订票
*/
void bookTicket();
/**
* 唱歌
*/
void sing();
/**
* 收钱
*/
void collectMoney();
}
public class RealStar implements Star{
@Override
public void confer() {
System.out.println("====RealStar.confer()");
}
@Override
public void signContract() {
System.out.println("====RealStar.signContract()");
}
@Override
public void bookTicket() {
System.out.println("====RealStar.bookTicket()");
}
@Override
public void sing() {
System.out.println("====RealStar.sing()");
}
@Override
public void collectMoney() {
System.out.println("====RealStar.collectMoney()");
}
}
public class ProxyStar implements Star{
private Star star;
public ProxyStar(Star star) {
super();
this.star = star;
}
@Override
public void confer() {
System.out.println("====ProxyStar.confer()");
}
@Override
public void signContract() {
System.out.println("====ProxyStar.signContract()");
}
@Override
public void bookTicket() {
System.out.println("====ProxyStar.bookTicket()");
}
@Override
public void sing() {
star.sing();
}
@Override
public void collectMoney() {
System.out.println("====ProxyStar.collectMoney()");
}
}
public class Test {
public static void main(String[] args) {
Star real = new RealStar();
Star proxyStar = new ProxyStar(real);
proxyStar.bookTicket();
proxyStar.collectMoney();
proxyStar.confer();
proxyStar.sing();
}
}
动态代理
JDK自带的动态代理
-java.lang.reflect.Proxy
作用:动态生成代理类和对象
-java.lang.reflect.InvocationHandler(处理器接口)
·可以通过invoke方法实现对真实角色的代理访问。
·每次通过Proxy生成代理类对象时都要指定对应的处理对象
public interface Star {
/**
* 面谈
*/
void confer();
/**
* 签合同
*/
void signContract();
/**
* 订票
*/
void bookTicket();
/**
* 唱歌
*/
void sing();
/**
* 收钱
*/
void collectMoney();
}
public class RealStar implements Star{
@Override
public void confer() {
System.out.println("====RealStar.confer()");
}
@Override
public void signContract() {
System.out.println("====RealStar.signContract()");
}
@Override
public void bookTicket() {
System.out.println("====RealStar.bookTicket()");
}
@Override
public void sing() {
System.out.println("====RealStar.sing()");
}
@Override
public void collectMoney() {
System.out.println("====RealStar.collectMoney()");
}
}
public class StarHandler implements InvocationHandler{
private Star realStar;
public StarHandler(Star realStar) {
super();
this.realStar = realStar;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(realStar, args);
return null;
}
}
public class Test {
public static void main(String[] args) {
Star real = new RealStar();
StarHandler handler = new StarHandler(real);
Star proxy = (Star)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Star.class}, handler);
proxy.bookTicket();
proxy.collectMoney();
proxy.signContract();
proxy.sing();
}
}
6、命令模式
命令模式:调用对象与作用对象之间分离,由中间件来协调两者之间的工作,如控制器
public class Receiver {
public void action() {
System.out.println("=====Receiver.action" );
}
}
//调用者、发起者
public class Invoke {
private Command command;//也可以通过容器List<Command>容纳很多命令对象,进行批处理。数据库底层的事务管理就是类似的结构
public Invoke(Command command) {
super();
this.command = command;
}
//业务方法,用于调用命令类的方法
public void call() {
command.execute();
}
public interface Command {
/**
* 这个方法是一个返回结果为空的方法。
* 实际项目中,可以根据需求设计多个方法
*/
void execute();
}
class ConcreteCommand implements Command{
private Receiver receiver;//命令的真正执行者
public ConcreteCommand(Receiver receiver) {
super();
this.receiver = receiver;
}
@Override
public void execute() {
//命令执行前或后可以做相关的处理
receiver.action();
}
}
public class Test {
public static void main(String[] args) {
Command command = new ConcreteCommand(new Receiver());
Invoke invoke = new Invoke(command);
invoke.call();
}
}