设计模块
学习
spring
源码需要了解设计模式,这样可以更好的理解,毕竟在spring源码中,各种各样的接口、抽象类,各种继承、实现
单例模式: ApplicationContext
工厂模式 : BeanFactory
模板方法模式: Template
观察者模式: ContextLoaderListener
策略模式: HandlerMapping
装饰器模式:
责任链模式
代理模式: Proxy
适配器模式: HandlerApdapter
委派模式: Delegate
原型模式:Prototype
https://blog.csdn.net/Appleyk/article/details/83787334
单例模式(Singleton Pattern)
优点:
(1) 由于单例模式在内存中只有一个实例,减少内存开支,特别是一个对象需要频繁地创建销毁时,而且创建或销毁时性能又无法优化,单例模式就非常明显了
(2) 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。
缺点
(1) 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
(2) 单例对象如果持有Context,那么很容易引发内存泄漏,此时需要注意传递给单例对象的Context最好是Application Context
https://www.cnblogs.com/dongyu666/p/6971783.html
/**
* 饿汉模式
*/
class Hunger {
/**
* 类加载的时候instance就已经指向了一个实例
*/
private static Hunger instance = new Hunger();
private Hunger() {
}
public static Hunger getInstance() {
return instance;
}
}
/**
* 懒汉模式
*/
class Lan {
/**
* 类加载的时候instance就已经指向了一个实例
*/
private static Lan instance = null;
private Lan() {
}
public static Lan getInstance() {
synchronized (instance) {
if (instance == null) {
instance = new Lan();
}
}
return instance;
}
}
/**
* 双重校验锁
*/
class DoubleCheck {
/**
* 类加载的时候instance就已经指向了一个实例
*/
private volatile static DoubleCheck instance = null;
private DoubleCheck() {
}
public static DoubleCheck getInstance() {
// 仅仅加锁是不够的,由于jvm会进行指定重排,一个线程正在new的时候开辟内存地址,这时候还是没有赋值的,另一个线程获取对象直接返回可能会空指针,使用volatile禁止指令重排
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new DoubleCheck();
}
}
}
return instance;
}
}
/**
* 静态内部类
*/
class Singleton {
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
/**
* 枚举
*/
// 保证了线程安全,不能序列化、不能反射来创建对象
public enum EnumSingletonss {
INSTANCE;
}
// 如果需要将一个已有的类改造为单例类,也可以使用枚举的方式来实现
class EnumSingleton {
/**
* 构造方法私有化
*/
private EnumSingleton(){
}
/**
* 返回实例
* @return
*/
public static EnumSingleton getInstance() {
return Singleton.INSTANCE.getInstance();
}
/**
* 使用枚举方法实现单利模式
*/
private enum Singleton {//内部枚举类
INSTANCE;
private EnumSingleton instance;
/**
* JVM保证这个方法绝对只调用一次 Enum类内部会有一个构造函数,该构造函数只能有编译器调用
*/
Singleton() {
instance = new EnumSingleton();
}
public EnumSingleton getInstance() {
return instance;
}
}
}
工厂模式
优点:
工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。
缺点:
由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。当系统中的具产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;
简单工厂模式
/**
* 人类
*/
interface PeopleDemo {
/**
* 说话方法
*/
void say();
}
/**
* 男人
*/
class ManaDemo implements PeopleDemo {
@Override
public void say() {
System.out.println("男人说话");
}
}
/**
* 女人
*/
class WamnDemo implements PeopleDemo {
@Override
public void say() {
System.out.println("女人说话");
}
}
/**
* 通过反射实现工厂
*/
class FactoryDemo {
public static PeopleDemo simaple(Class c) {
// newInstance调用无参构造方法
PeopleDemo peoples = null;
try {
peoples = (PeopleDemo) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return peoples;
}
}
/**
* 实现类
*/
class TestDemo{
public static void main(String[] args) {
PeopleDemo simaple = FactoryDemo.simaple(WamnDemo.class);
simaple.say();
}
}
工厂方法模式
简单工厂模式存在一系列问题:
- 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
- 违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
- 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
public class Methodfactory {
public static void serviceFactory(AbstractFactory factory) {
Product product = factory.newProduct();
product.show();
}
public static void main(String[] args) {
//传入工厂工具类ConcreteFactoryA/B
serviceFactory(new ConcreteFactory1());
}
}
/**
* 抽象产品:提供了产品的接口
*/
interface Product {
void show();
}
/**
* 具体产品1:实现抽象产品中的抽象方法
*/
class ConcreteProduct1 implements Product {
@Override
public void show() {
System.out.println("具体产品1显示...");
}
}
/**
* 具体产品2:实现抽象产品中的抽象方法
*/
class ConcreteProduct2 implements Product {
@Override
public void show() {
System.out.println("具体产品2显示...");
}
}
/**
* 抽象工厂:提供了厂品的生成方法
*/
interface AbstractFactory {
Product newProduct();
}
/**
* 具体工厂1:实现了厂品的生成方法
*/
class ConcreteFactory1 implements AbstractFactory {
@Override
public Product newProduct() {
System.out.println("具体工厂1生成-->具体产品1...");
return new ConcreteProduct1();
}
}
/**
* 具体工厂2:实现了厂品的生成方法
*/
class ConcreteFactory2 implements AbstractFactory {
@Override
public Product newProduct() {
System.out.println("具体工厂2生成-->具体产品2...");
return new ConcreteProduct2();
}
}
抽象工厂模式
特点
(1)提供创建一组相关相互依赖对象的接口,而无需指定他们具体的类。
(2)工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。
(3)抽象工厂实现类提供的产品可以为多个,这多个产品可以实现依赖于不同的产品接口或抽象类产品。
/**
* 抽象产品1
*/
interface IProduct1 {
void show();
}
/**
* 抽象产品2
*/
interface IProduct2 {
public void show();
}
/**
* 具体产品1
*/
class Product1 implements IProduct1 {
@Override
public void show() {
System.out.println("这是1型产品");
}
}
/**
* 具体产品2
*/
class Product2 implements IProduct2 {
@Override
public void show() {
System.out.println("这是2型产品");
}
}
/**
* 抽象工厂
*/
interface IFactory {
public IProduct1 createProduct1();
public IProduct2 createProduct2();
}
/**
* 具体工厂 == 生产产品
*/
class Factory implements IFactory {
@Override
public IProduct1 createProduct1() {
return new Product1();
}
@Override
public IProduct2 createProduct2() {
return new Product2();
}
}
/**
* 客户端
*/
class Client {
public static void main(String[] args) {
IFactory factory = new Factory();
factory.createProduct1().show();
factory.createProduct2().show();
}
}
模板方法模式
模板方法模式的应用场景
Template Method模式一般应用在具有以下条件的应用中:
- 具有统一的操作步骤或操作过程
- 具有不同的操作细节
- 存在多个具有同样操作步骤的应用场景,但某些具体的操作细节却各不相同
2.模板方法模式的角色及其职责:
(1)抽象模板角色[Abstract Template]:抽象模板类,用来定义算法的基本骨架,同时定义一个或多个抽象操作,这些操作由子类进行实现。
(2)具体模板角色[Concrete Template]:具体模板角色,用来实现算法骨架中的某些步骤,完成于特定子类相关的功能。
优缺点:
优点:
- 模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
- 子类实现算法的某些细节,有助于算法的扩展。
- 通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
缺点:
- 每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。
案例
public class TemplateMethodFactory {
public static void main(String[] args) {
BaseTemplate template = new TemplateMethod();
template.useTemplateMethod();
}
}
/**
* 业务流程模板,提供基本框架
*/
abstract class BaseTemplate {
public abstract void part1();
public abstract void part2();
public abstract void part3();
public void useTemplateMethod() {
// 定义公共信息
System.out.println("模板方法");
part1();
part2();
part3();
}
}
/**
* 模板具体实现方式1
*/
class TemplateMethod extends BaseTemplate {
@Override
public void part1() {
System.out.println("模板具体方法1");
}
@Override
public void part2() {
System.out.println("模板具体方法2");
}
@Override
public void part3() {
System.out.println("模板具体方法3");
}
}
观察者模式
观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象有待改变的时候,应该考虑使用观察者模式。
观察者模式: 消息队列,只要订阅了这个主题,就会推送,可以添加消费者,生产者接送到事件,会进行推送
class Client {
public static void main(String[] args) {
Observer sina = new Sina();
Subject weatherAPI = new WeatherAPI();
// 注册观察者
weatherAPI.registerObserver(sina);
// 给观察者发送事件
weatherAPI.setData(16);
//移除新浪
weatherAPI.removeObserver(sina);
Observer baiDu = new BaiDu();
weatherAPI.registerObserver(baiDu);
weatherAPI.setData(22);
}
}
/**
* 订阅者(观察者)抽象类,自动接受输入
*/
abstract class Observer {
//温度
protected float wenDu;
/**
* 更新网站显示的天气数据
*/
public void update(float wenDu) {
this.wenDu = wenDu;
display();
}
public void display() {
System.out.println("温度" + wenDu);
}
}
/**
* 发布者接口
*/
interface Subject {
/**
* 设置数据
*/
void setData(float wenDu);
/**
* 增加一个调用API的人
*/
void registerObserver(Observer o);
/**
* 移除一个调用API的人
*/
void removeObserver(Observer o);
/**
* 通知调用API的人
*/
void notifyObservers();
}
/**
* 天气API,更新时会自动推送新数据给第三方网站
*/
class WeatherAPI implements Subject {
//温度
private float wenDu;
private LinkedList<Observer> list;
public WeatherAPI() {
this.list = new LinkedList<>();
}
@Override
public void setData(float wenDu) {
this.wenDu = wenDu;
//新数据通知调用方更新天气数据
notifyObservers();
}
@Override
public void registerObserver(Observer o) {
list.add(o);
}
@Override
public void removeObserver(Observer o) {
if (list.contains(o)) {
list.remove(o);
}
}
@Override
public void notifyObservers() {
for (Observer observer : list) {
// 发送事件到观察者
observer.update(wenDu);
}
}
}
/**
* 新浪调用天气API
*/
class Sina extends Observer {
/**
* 还可以做其他事
*/
public Sina() {
System.out.println("新浪");
}
}
/**
* 百度调用天气API
*/
class BaiDu extends Observer {
/**
* 还可以做其他事
*/
public BaiDu() {
System.out.println("百度");
}
}
策略模式
策略模式(Strategy Pattern)
- 从定义可以看出策略模式是定义了行为“算法”族,将其封装起来,给用户使用的,如果算法改变,那么只需添加或者修改算法方式便能解决问题了,而无需修改其他原有的行为“算法”,因为他们是相互独立的。
- 对客户隐藏具体行为“算法”的实现细节,彼此之间相互独立。
- 我们的场景需要完全符合策略模式,封装了不同的提交行为“算法”。
- 在使用java多线程时,我们一般使用Executors类去产生一项线程池,但是这个类中的方法最后使用的是ThreadPoolExecutor中的构造函数进行初始化。其中的ThreadFactory与RejectedExecutionHandler就可以看作是策略模式。
ThreadFactory与RejectedExecutionHandler,是两个接口,一个用于产生线程,一个是当任务超过workQueue后,任务的处理方式。
比如RejectedExecutionHandler当前有四种实现方式:
- AbortPolicy:超出时,直接抛出异常
- CallerRunsPolicy:在调用线程(即主线程)中立即执行该任务的run方法
- DiscardOldestPolicy:丢弃最老的任务
- DiscardPolicy:丢弃当前任务
public class Strategy {
public static void main(String[] args) {
// 使用支付宝付款 有顾客决定方式
Shopping shop = new Shopping(new Alipay());
shop.payMoney();
}
}
/**
* 策略上下文
*/
class Shopping {
private PayMethod payMethod;
public Shopping(PayMethod payMethod) {
this.payMethod = payMethod;
}
public void payMoney() {
System.out.println("我在购物,现在要付钱了");
payMethod.payMoney();
}
}
/**
* 抽象的付款接口
* 定义一个接口, 为付款方式, 底下有很多实现方式, 比如信用卡支付, 支付宝支付, 京东支付, 微信支付等等
*/
interface PayMethod {
void payMoney();
}
/**
* 支付宝支付
*/
class Alipay implements PayMethod {
@Override
public void payMoney() {
System.out.println("我在使用支付宝支付");
}
}
/**
* 财付通付款
*/
class TenPay implements PayMethod {
@Override
public void payMoney() {
System.out.println("我在使用财付通付款");
}
}
装饰器模式
装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
装饰器模式的主要优点有:
- 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
- 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
- 装饰器模式完全遵守开闭原则
其主要缺点是:装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
装饰器模式的应用场景
前面讲解了关于装饰器模式的结构与特点,下面介绍其适用的应用场景,装饰器模式通常在以下几种情况使用。
- 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
- 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
- 当对象的功能要求可以动态地添加,也可以再动态地撤销时。
public class DecoratorPattern {
public static void main(String[] args) {
Component p = new ConcreteComponent();
p.operation();
System.out.println("---------------------------------");
Component d = new ConcreteDecorator(p);
d.operation();
}
}
//抽象构件角色
interface Component {
void operation();
}
//具体构件角色
class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
@Override
public void operation() {
System.out.println("调用具体构件角色的方法operation()");
}
}
//抽象装饰角色
class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
//具体装饰角色
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction()");
}
}
责任链模式(职责链模式)
责任链模式(Chain of Responsibility Pattern)
https://www.javazhiyin.com/75780.html
模式的定义与特点
责任链(Chain of Responsibility)模式的定义:责任链,顾名思义,就是用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会(条件匹配)处理请求事务
责任链模式是一种对象行为型模式,其主要优点如下。
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
其主要缺点如下。
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
public void test(int i, Request request){
if(i==1){
Handler1.response(request);
}else if(i == 2){
Handler2.response(request);
}else if(i == 3){
Handler3.response(request);
}else if(i == 4){
Handler4.response(request);
}else{
Handler5.response(request);
}
}
代码的业务逻辑是这样的,方法有两个参数:整数 i 和一个请求 request,根据 i 的值来决定由谁来处理 request,如果 i==1
,由 Handler1来处理,如果 i==2
,由 Handler2 来处理,以此类推。在编程中,这种处理业务的方法非常常见,所有处理请求的类由if…else…条件判断语句连成一条责任链来对请求进行处理,相信大家都经常用到。这种方法的优点是非常直观,简单明了,并且比较容易维护,但是这种方法也存在着几个比较令人头疼的问题:
代码臃肿: 实际应用中的判定条件通常不是这么简单地判断是否为1或者是否为2,也许需要复杂的计算,也许需要查询数据库等等,这就会有很多额外的代码,如果判断条件再比较多,那么这个if…else…语句基本上就没法看了。
耦合度高: 如果我们想继续添加处理请求的类,那么就要继续添加if…else…
判定条件;另外,这个条件判定的顺序也是写死的,如果想改变顺序,那么也只能修改这个条件语句。
既然缺点我们已经清楚了,就要想办法来解决。这个场景的业务逻辑很简单:如果满足条件1,则由 Handler1
来处理,不满足则向下传递;如果满足条件2,则由Handler2
来处理,不满足则继续向下传递,以此类推,直到条件结束。其实改进的方法也很简单,就是把判定条件的部分放到处理类中,这就是责任连模式的原理。
当你想要让一个以上的对象有机会能够处理某个请求的时候,就是用责任链模式。
通过责任链模式,你可以为某个请求创建一个对象链。每个对象依序检查此请求,并对其进行处理,或者将它传给链中的下一个对象。
比如
程序员要请3天以上的假期,在OA申请,需要直接主管、总监、HR 层层审批后才生效。类似的采购审批、报销审批。。。
应用
JAVA 中的异常处理机制、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,JSP、Servlet 的 Filter 均是责任链的典型应用。
SpringMVC 请求的流程中,执行了拦截器相关方法 interceptor.preHandler 等等
在处理 SpringMVC 请求时,使用到职责链模式还使用到适配器模式
HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
HandlerExecutionChain 维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器
理解:如果当前逻辑不能处理,就需要交给下一个逻辑处理,直到匹配成功为止
public class ChinaPattern {
// 实现一个流程审批功能
public static void main(String[] args) {
Teacher teacher = new Teacher(1);
Principal principal = new Principal(2);
Leader leader = new Leader(3);
// 设置当前处理者的下一个处理者
teacher.setNextHandler(principal);
principal.setNextHandler(leader);
// 处理数据
teacher.handlerRequest(new Request(2));
}
}
class Request {
int level = 0;
public Request(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
}
/**
* 抽象的
*/
abstract class Handler {
private Handler nextHandler;
private int level; //1
public Handler(int level) {
this.level = level;
}
public void setNextHandler(Handler handler) {
this.nextHandler = handler;
}
public final void handlerRequest(Request request) {
if (level == request.getLevel()) {
this.response(request);
} else {
if (this.nextHandler != null) {
this.nextHandler.handlerRequest(request);
} else {
System.out.println("===已经没有处理器了===");
}
}
}
// 抽象方法,子类实现
public abstract void response(Request request);
}
class Teacher extends Handler {
public Teacher(int level) {
super(level);
}
@Override
public void response(Request msg) {
System.out.println("老师 审批");
}
}
class Principal extends Handler {
public Principal(int level) {
super(level);
}
@Override
public void response(Request msg) {
System.out.println("校长 审批");
}
}
class Leader extends Handler {
public Leader(int level) {
super(level);
}
@Override
public void response(Request request) {
System.out.println("领导审批");
}
}
代理模式
代理模式(Proxy)
目的是希望代码重用。跟我们以往访问对象的方式不同,代理模式不是直接通过目标对象,而是通过代理访问我们的目标对象以及方法。因为有的时候我们无法直接与目标对象建立联系或者,我们要控制客户端访问。所以便通过代理来访问我们的真实对象。
就好比「客户」-> 「明星经纪人」-> 「明星」。我们不是直接与明星联系,明星很忙的,要唱歌跳舞烫头拍电影,给的价格足够好,经纪人才告知明星接下这个任务。
静态代理
// 静态代理
public class Proxy {
public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.request(); //代理者代替真实者做事情
}
}
interface Subject {
void request();
}
class RealSubjbect implements Subject {
@Override
public void request() {
System.out.println("this is RealSubjbect.request()");
}
}
class ProxySubject implements Subject {
private Subject realSubjbect = null;
/**
* 除了代理真实角色做该做的事,代理角色提供附加操作
* 如
*/
@Override
public void request() {
preRequest(); //真实角色操作前的附加操作
if (realSubjbect == null) {
realSubjbect = new RealSubjbect();
}
realSubjbect.request();
postRequest();//真实角色操作后的附加操作
}
/**
* 真实角色操作前的附加操作
*/
private void postRequest() {
System.out.println("真实角色操作后的附加操作");
}
/**
* 真实角色操作后的附加操作
*/
private void preRequest() {
System.out.println("真实角色操作前的附加操作");
}
}
缺点
代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
另外,如果要按照上述的方法使用代理模式,那么真实角色(委托类)必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色(委托类),该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
动态代理
动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、RPC远程调用、Java 注解对象获取、日志框架、全局性异常处理、事务处理等。
主要有两种实现方式:
- 使用 JDK 实现。基于接口的动态代理。
- 使用 CGLIB 实现。不能是final修饰的类,只能修饰 public 方法。
JDK 的动态代理机制是单一的,它只能代理被代理类的接口集合中的方法,子类中自定义的方法不会被代理
适配器模式
Adapter
和策略模式很相似
public class Delegate {
public static void main(String[] args) {
LeaderDispatch leader = new LeaderDispatch();
//看上去好像是我们的项目经理在干活
//但实际干活的人是普通员工
//这就是典型,干活是我的,功劳是你的
leader.dispatch("识别颜色");
leader.dispatch("切换主题");
}
}
/**
* @description: leader 委派者 任务分发的作用
* @ModificationHistory who when What
**/
class LeaderDispatch {
private Map<String, IExcuter> targets = new HashMap<String, IExcuter>();
public LeaderDispatch() {
targets.put("识别颜色", new ExcuterA());
targets.put("切换主题", new ExcuterB());
}
public void dispatch(String command) {
//根据指令委托到对应的执行者
targets.get(command).execute(command);
}
}
/**
* 执行的接口
*/
interface IExcuter {
void execute(String command);
}
/**
* 程序猿A执行的工作
*/
class ExcuterA implements IExcuter {
@Override
public void execute(String command) {
System.out.println("员工A 开始做" + command + "的工作");
}
}
/**
* 程序猿B执行的任务
*/
class ExcuterB implements IExcuter {
@Override
public void execute(String command) {
System.out.println("员工B 开始做" + command + "的工作");
}
}
原型模式
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多
原型模式的优点:
- Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
原型模式的缺点:
- 需要为每一个类都配置一个 clone 方法
- clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
由于 Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。
模式的实现
原型模式的克隆分为浅克隆和深克隆。
- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类。
应用场景
创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。
需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。
// 浅复制
public class Prototype {
public static void main(String[] args) throws CloneNotSupportedException {
Realizetype obj1 = new Realizetype();
Realizetype obj2 = (Realizetype) obj1.clone();
System.out.println("obj1==obj2?" + (obj1 == obj2));
}
}
/**
* 具体原型类
*/
class Realizetype implements Cloneable {
@Override
public Object clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!");
return (Realizetype) super.clone();
}
}