16.设计模式
1.设计模式是什么?设计模式的作用?
常见的设计模式有大概23种,我们并不会将这23种设计模式挨个学习,我们找出3个设计模式为大家介绍一些,从而体会这个设计模式的好处。
JAVA相关的开发,都不可避免的要用到抽象和封装,这是JAVA的一个特点,同时也是每个开发者必须掌握的,JAVA是这样,Android更是如此。而设计模式就是告诉我们应该如何写出高效且更具应用性和拓展性的代码.
2.单例模式的两种表示方式以及区别?
单例设计模式[单态模式]
单例设计模式的一般定义:一个类中只允许有一个实例。
通常情况下我们的类可以创建出若干个实例【对象】,通过new 就可以完成,如果是单例模式构造的java类,那么他就只能由一个实例【对象】。
实现思路:让类的构造方法私有化【private】,同时提供一个静态方法[static]去实例化这个类。
单例模式有两种写法,懒汉式写法和饿汉式写法。
懒汉式:在静态方法中初始化。时间换空间。(不推荐,时间很重要)
package demo_singleton;
/*懒汉式的单例模式,有线程安全问题,当多线程访问的时候,会出现多个实例*/
public class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy() {
}
}
饿汉式:在声明对象就初始化。空间换时间。(推荐,空间不是问题)
package demo_singleton;
public class SingletonHungry {
private static SingletonHungry singletonhunary = new SingletonHungry();
public static SingletonHungry getinstance() {
return singletonhunary;
}
private SingletonHungry() {
}
}
比较一下这两种写法的单例模式:
懒汉式:是在得到本类对象的静态方法中创建的本类对象
饿汉式:是在定义本类对象的变量时就创建好了本类对象
从程序的运行速度角度分析:饿汉式的单例模式要比懒汉式的单例模式更快一些。
从程序的内存使用角度分析:懒汉式的单例模式要比饿汉式的单例模式更节省内存一些。
以后我们在构造自己的程序是如果需要某一个java类的对象在整个程序中只有一个对象的话,那么我们首先要想到的就是单例模式。
3.简单工厂模式的角色,编写简单工厂模式?
简单工厂设计模式
简单工厂设计模式的一般定义:简单工厂又叫静态工厂,由一个工厂对象决定创建哪一个产品对象。
实现思路:写一个类,让他制造出我们想要的对象。
抽象产品角色,工厂角色,具体产品角色
例如:有一个农场负责生产各种水果,有苹果,有草莓,有葡萄。
农场----工厂角色【java类】
水果—抽象产品角色【抽象类/接口】
苹果,草莓,葡萄----具体产品角【java类与抽象产品角色有继承/实现关系】
实例:
public abstract class Product {
public Product() {
}
}
每个具体的产品需要继承上面那个抽象类,一下依次为船,车,飞机类。通过简单的输出完成我们建造交通工具的梦想.
public class Boat extends Product {
public Boat() {
System.out.println("建造小船");
}
}
public class Car extends Product {
public Car() {
System.out.println("建造汽车");
}
}
public class Plane extends Product {
public Plane() {
System.out.println("建造飞机");
}
}
工厂类中有一个判断的方法,对用户的需求做出判断,实例化哪个对象
package demo_samplefactory;
public class Factory {
public Factory() {
}
public Product create(String product) {
if (product.equals("飞机")) {
return new Plane();
}
if (product.equals("小船")) {
return new Boat();
}
if (product.equals("汽车")) {
return new Car();
}
return null;
}
}
通过这个工厂,我们可以很方便的生产出我们想要的对象。```
4.代理模式的分类?比较
代理模式
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式顾名思义,有个代理,那这个代理做什么工作呢?我们平时生活中,我们遇到过的代理就是各个品牌的代理,一个产品不能让广大的消费者去工厂去买吧,工厂生产出产品,各个地方有一个代理,代理这个工厂负责个每个商店推销产品。又比如一个歌手有一个助理,很多事情都是助理负责,助理接收到信息,看看是什么信息,自己可以干的就自己完成,就比如歌手的助理收到要去开演唱会的通知,助理不能唱歌啊,这个核心的事情还是得交给老板,助理的作用就是避免自己的老板受到打扰。所以代理是过滤掉一些事情,剩下的核心内容还是由真实的对象来负责。
代理模式存在的意义就是,降低了真实对象和要做事情的耦合性,降低了他们之间的联系,代理类对请求进行过滤,预处理,将请求交给真实对象,或者委派给真实对象。好比消费者不会去工厂去买产品,一个歌手不会每天都会接收到很多没有用的信息。
同样代理模式也符合开闭原则,对修改关闭,对扩展开放。在不改变真实类的情况下,对其类进行扩展。代理类对外提供一系列的接口,用户对其使用不会对原对象产生影响,对原对象有一个良好的封装性。
代理模式分为静态代理和JDK动态代理,还有一种是Cglib代理。
静态代理:[让代理类实现与实体类相同的接口]
1.抽象角色:是一个接口,定义了行为,没有具体实现
public interface AbstractSubject {
public abstract void sing();
}
2.代理角色:其中有真实对象的引用,从而对真实对象进行操作,可以对真实对象的操作进行扩展,不会修改原对象。
public class ProxySubject implements AbstractSubject {
private AbstractSubject abstractSubject;
public ProxySubject(AbstractSubject abstractSubject) {
this.abstractSubject = abstractSubject;
}
@Override
public void sing() {
abstractSubject.sing();
}
}
3.真实对象:有对事件的真实操作,最终我们要引用的对象。
public class RealSubject implements AbstractSubject{
@Override
public void sing() {
System.out.println("真唱");
}
}
测试类:
public class Test {
public static void main(String[] args) {
RealSubject reaal = new RealSubject();
ProxySubject proxy = new ProxySubject(reaal);
proxy.sing();
}
}
从输出结果可以看出,是真唱不是假唱。我们创建了真实对象和代理对象,用代理模式进行调用,通过代理最终我们核心的代码,执行的还是真实角色中的代码。知识对真实角色进行了封装。
JDK动态代理:
动态代理相比于静态代理,动态代理的代理类是在动态生成的,也就是jvm通过反射获取代码生成代理类,所以用户并不能决定代理角色和真实角色之间的联系,而是由程序运行时候决定的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
动态代理实现的三步走:
1.实现InvocationHandler接口,创建自己的调用处理器 。
2.给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类 。
3.执行真实角色具体任务。
要想创建处理器必须实现InvocationHandler接口,之后通过类装载器创建代理类。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:指代我们所代理的那个真实对象
method:指代的是我们所要调用真实对象的某个方法的Method对象
args:指代的是调用真实对象某个方法时接受的参数
我们依旧要创建抽象角色和真实角色类
public interface AbstractSubject {
public abstract void sing();
}
public class RealSubject implements AbstractSubject{
@Override
public void sing() {
System.out.println("真唱");
}
}
代理角色:和静态代理相比,静态代理运用对象调用完成代理,而动态代理通过实现InvocationHandler接口,创建了一个调用处理器,其实是通过Java的反射机制完成.
package com.click369.test1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class CreateProxySubject implements InvocationHandler {
private RealSubject realSubject;
public CreateProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
public Object getProxy(){
/**
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler, 我 们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), this);
}
/**
* proxy:指代我们所代理的那个真实对象
method:指代的是我们所要调用真实对象的某个方法的Method对象
args:指代的是调用真实对象某个方法时接受的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(realSubject, args);
}
}
package com.click369.test1;
public class TestMain {
public static void main(String[] args) {
//目标对象
RealSubject realSubject=new RealSubject();
//创建代理对象的对象
CreateProxySubject createProxySubject=new CreateProxySubject(realSubject);
//创建的代理对象
AbstractSubject proxy=(AbstractSubject)createProxySubject.getProxy();
proxy.sing();
}
}
看上去实现很简单,我们只需要实现接口,调用其中的方法就可以实现动态代理。
通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法。
注意:JDK动态代理只能处理有接口实现关系的java类
Cglib代理
cglib是一个强大的高性能的代码生成包,广泛的被许多AOP的框架使用,它的开源地址在https://github.com/cglib/cglib
通过它实现动态代理,主要用到import net.sf.cglib.proxy包下的MethodInterceptor、MethodProxy和Enhancer类
(1)、MethodInterceptor
MethodInterceptor类是方法拦截器,代理类实例的方法被调用时会回调MethodInterceptor的intercept()方法拦截,用来实现自己的代理逻辑,类似于jdk动态代理的InvocationHandler接口
(2)、MethodProxy
MethodProxy是intercept()方法中的第四个参数的类型,它是实际类方法的代理引用,使用methodProxy比使用jdk自身的method在效率上会有提升
(3)、Enhancer
Enhancer用来动态创建实际类子类的代理类实例,setSuperclass()方法设置实际类为父类,setCallback()方法建立方法回调,create()方法创建一个代理类实例
//被代理类
public class RealSubject {
public void Request() {
System.out.println("执行具体的功能");
}
public final void RequestFinal() {
System.out.println("执行具体的功能(final)");
}
}
//生成代理对象的java类
public class ProxyInterceptor implements MethodInterceptor {
public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib动态代理开始");
Object result = methodProxy.invokeSuper(proxy, params);
System.out.println("cglib动态代理开始");
return result;
}
//创建代理类实例
public Object newProxy(Object target)
{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
}
//测试类
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxyInterceptor proxyInterceptor = new ProxyInterceptor();
RealSubject subject = (RealSubject)proxyInterceptor.newProxy(realSubject);
subject.Request();
subject.RequestFinal();
}
}
比较
1.静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
手动创建一个与目标类相同接口的子类,包装目标类。
2.JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;【兄弟模式】
通过jdk提供的反射包中Proxy这个类,动态的创建一个与目标类实现相同接口的子类对象,包装目标。
3.CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。【父子模式】
通过CGlib提供的Enhancer这个类,动态的创建一个目标类的子类对象,包装目标类。
5.《大话设计模式》