设计模式

设计原则
装饰模式
动态代理
命令模式

设计原则

开闭原则、里氏替换原则、依赖倒转原则、接口隔离原则、最少知道原则、单一职责原则、合成复用原则

开闭原则

对修改关闭,对扩展开发。

里氏替换原则

子类可以扩展父类的功能,但是不能改变父类原有的功能。比如子类可以覆盖父类的抽象方法(抽象方法在父类中没有实现),但是不能覆盖父类的非抽象方法(非抽象方法属于父类的原有功能,子类覆盖相当与改变父类的功能)

依赖倒转原则

模块之间依赖接口编程,不能依赖实现编程。

接口隔离原则和单一职责原则

接口隔离原则:细化接口,不要建立臃肿的接口。
单一职责原则:类的功能单一。
这两个原则咋一看挺像的,都是强调类/接口的设计要单一。接口隔离原则指的是接口设计方面。单一职责指的是业务方面。

最少知道原则

高内聚、低耦合

合成复用原则

尽量使用聚合、组合的方式,而不是使用继承。

返回顶部

装饰模式

给类增加功能可以通过几种方式?
1、直接修改类的代码。 违背开闭原则。
2、增加子类。 影响类的继承链。
3、通过装饰模式动态给类增加功能。
优点:扩展性更强,装饰类为可插拔
缺点:会产生多余的小对象(装饰类),相对继承,出现问题排查起来更困难。

设计模式

Component:最上层接口
ConcreteComponent:被装饰类
Decorator:装饰器接口
ConcreteDecorator:装饰器实现类

public interface Human {
    void eat();
}

public class Man implements Human{
    public void eat(){
        System.out.println("eat");
    }
}

public abstract class Decorator implements Human{
    private Human human;
    public Decorator(Human human){
        this.human = human;
    }

    public void eat(){
        human.eat();
    }

}

public class ConcreteDecorator extends Decorator{
    public ConcreteDecorator(Human human){
        super(human);
    }

    private void wash(){
        System.out.println("wash");
    }

    @Override
    public void eat() {
        this.wash();
        super.eat();
    }
}

public class Main {
    /***
     * 设计模式 -- 装饰模式
     * 人类都会吃饭,通过装饰模式,给吃饭增加一个洗手的功能
     * @param args
     */
    public static void main(String[] args) {
        Human man = new Man();
        Decorator d = new ConcreteDecorator(man);
        d.eat();
    }
}

返回顶部

动态代理

介绍三部分内容:静态代理、jdk动态代理、cglib

静态代理

被代理接口

public interface HelloInterface {
    void sayHello();
}

被代理类

public class Hello implements HelloInterface{
    @Override
    public void sayHello() {
        System.out.println("Hello zhanghao!");
    }
}

代理类

public class HelloProxy implements HelloInterface{
    private HelloInterface helloInterface = new Hello();
    @Override
    public void sayHello() {
        System.out.println("Before invoke sayHello" );
        helloInterface.sayHello();
        System.out.println("After invoke sayHello");
    }
}

在编译时就要为每个被代理类编写一个代理类,这样如果有多个被代理类的,就需要写多个代理类。接下来动态代理解决这个问题。

jdk动态代理

被代理接口

public interface UserService {
     public String execute() throws Throwable ;
}

被代理类

public class UserServiceImpl implements UserService{
    @Override
    public String execute() throws Throwable {
        System.out.println("step 2 执行方法啦!!");
        return "step 2 执行方法啦!!";
    }
}

自定义拦截器

public class MyInvocationHandler implements InvocationHandler {
    private UserService userService;
    public MyInvocationHandler(UserService userService) {
        this.userService = userService;
    }
   @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();   //事务开启
        method.invoke(userService, args);
        after();    //事务关闭
        return "成功了";
    }
    private void before() {
        System.out.println("事务开启!");
    }
    private void after() {
        System.out.println("事务关闭");
    }
}

运行

public class MyTest {
 
    public static void main(String[] args) throws Throwable {
        System.out.println("---------------JDK动态代理----------------");
        UserService userService = (UserService) Proxy.newProxyInstance(MyTest.class.getClassLoader(),//指定由哪个加载器来加载代理类
                new Class<?>[]{UserService.class},//指定被代理的接口,生成的代理类会实现该接口
                new MyInvocationHandler(new UserServiceImpl()));
 
        userService.execute();
    }
}

当有多个被代理类(这些被代理类都实现相同接口)时,只需要定义一个自定义拦截器,该拦截器持有被代理接口的引用,然后在运行阶段根据传入的具体是哪个被代理类生成代理类。

流程:
1、使用的时候首先创建被代理类,被代理类要实现接口
2、创建自己的MyInvocationHandler实现Jdk的InvocationHandler,在里边增加拦截逻辑,同时把被代理类传递进去
3、通过Proxy的newProxyInstance方法创建代理类,需要将被代理接口和MyInvocationHandler传递进去。
newProxyInstance里边是如何生成代理类的?
首先根据传入的被代理接口获取到class信息,然后获取到构造方法,然后拿着构造方法通过反射生成代理类(这个过程把自定义拦截器也传递进去了,这样代理类中就有了自定义拦截器的引用,可以调用自定义拦截器的方法)。
4、当调用时,实际调用的是代理类的方法,而代理类内部通过反射获取到被代理类要执行的方法,然后传递给MyInvocationHandler,MyInvocationHandler内部先执行拦截逻辑,然后通过反射执行被代理类的方法

原理
生成代理类方法

public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException { 
 
    if (h == null) { 
        throw new NullPointerException(); 
    Class cl = getProxyClass(loader, interfaces); 

    // 通过反射获取构造函数对象并生成代理类实例
    try { 
//获取构造方法的时候将
        Constructor cons = cl.getConstructor(constructorParams); 
        return (Object) cons.newInstance(new Object[] { h }); 
    } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); 
    } catch (IllegalAccessException e) { throw new InternalError(e.toString()); 
    } catch (InstantiationException e) { throw new InternalError(e.toString()); 
    } catch (InvocationTargetException e) { throw new InternalError(e.toString()); 
    } 
}

生成的代理类经过反编译后代码


/**
*代理类也实现了Person接口,看起来和静态代理的方式也会一样的 
*同时代理类也继承了Proxy类
*/ 
public final class $Proxy0 extends Proxy implements Person{
  private static Method m4;
  private static Method m1;
  private static Method m0;
  private static Method m3;
  private static Method m2;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
   //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用 
  public final void sayGoodBye(boolean paramBoolean, double paramDouble)
    throws 
  {
    try
    {
	// 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
	// m4为代理类通过反射获得的Method
      this.h.invoke(this, m4, new Object[] { Boolean.valueOf(paramBoolean), Double.valueOf(paramDouble) });
      return;
    }
    catch (Error|RuntimeException localError)
    {
    。
    。
    。
    。
    。
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  static
  {
    try
    {//代理类通过反射 获得的接口方法Method
      m4 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayGoodBye", new Class[] { Boolean.TYPE, Double.TYPE });
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

      m3 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayHello", new Class[] { Class.forName("java.lang.String"), Integer.TYPE });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }

cglib

如何使用

public class SampleClass {
    public void test(){
        System.out.println("hello world");
    }
 
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(SampleClass.class);//指定被代理类
        enhancer.setCallback(new MethodInterceptor() {//自定义拦截器
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("before method run...");
                Object result = proxy.invokeSuper(obj, args);此处与java proxy有区别
                System.out.println("after method run...");
                return result;
            }
        });
        SampleClass sample = (SampleClass) enhancer.create();//代理类,生成的代理类会继承被代理类,因为final类型的类无法被继承,所以final类服务通过cglib生成代理
        sample.test();
    }

}

cglib的实现与java Proxy的本质区别

java Proxy是通过反射不用解释。cglib底层是通过asm实现的。
asm是什么?
java代码从编译到执行的流程如图:
设计模式
asm做的就是动态生成class文件,放入装载器。

原理分析

被代理类每个方法会生成2个方法,例如被代理类有个方法g(),则在代理类中对应两个方法g()和CGLIB$g$0()。

流程:1、测试代码中的 sample.test();
2、直接调用到代理类的g()
3、因为代理类持有自定义拦截器的引用,所以可以直接调用自定义拦截器的intercept方法。
4、执行自定义拦截器的intecept逻辑。

注意自定义拦截器中:Object result = proxy.invokeSuper(obj, args);
此处与java proxy有区别,此处是调用的父类的方法(生成的代理类集成被代理类),而java proxy中是通过反射调用的被代理类的方法。为什么这么设计?
因为反射效率低,这里采用的是fastClass机制。
fastCLass机制大致意思就是为类中的每个方法加个索引以提高效率。

直接看生成的代理类

public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory
{
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$g$0$Method;
    private static final MethodProxy CGLIB$g$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$f$1$Method;
    private static final MethodProxy CGLIB$f$1$Proxy;
    
    static void CGLIB$STATICHOOK1()
    {
      CGLIB$THREAD_CALLBACKS = new ThreadLocal();
      CGLIB$emptyArgs = new Object[0];
      Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0");
      Class localClass2;
      Method[] tmp60_57 = ReflectUtils.findMethods(new String[] { "g", "()V", "f", "()V" }, (localClass2 = Class.forName("net.sf.cglib.test.Target")).getDeclaredMethods());
      CGLIB$g$0$Method = tmp60_57[0];
      CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0");
      CGLIB$f$1$Method = tmp60_57[1];
      CGLIB$f$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "f", "CGLIB$f$1");
    }
    
    final void CGLIB$g$0()
    {
      super.g();
    }
    
    public final void g()
    {
      MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
      if (tmp4_1 == null)
      {
          CGLIB$BIND_CALLBACKS(this);
          tmp4_1 = this.CGLIB$CALLBACK_0;
      }
      if (this.CGLIB$CALLBACK_0 != null) {
          tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy);
      }
      else{
          super.g();
      }
    }
}

返回顶部

命令模式

/***
 * 命令模式:
 * 命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。
 * 将“行为请求者”与“行为实现者”解耦
 *
 * 命令模式涉及到的角色:
 * commond:命令接口
 * concreteCommond:命令实现类
 * reciever:真正负责执行命令的类
 * invoker:负责调用的类
 * client:负责产生命令的人
 *
 * 模拟餐厅点菜场景:
 * 顾客(client)来餐厅点菜,点了西红柿炒鸡蛋(concreteCommond)、
 * 宫保鸡丁(concreteCommond)、米饭(concreteCommond),
 * 服务员记录下来小单子,
 * 将小单子交给厨师(invoker)一个接一个的做菜(reviever)
 *
 */
public class Main {
    public static void main(String[] args) {
        List<Commond> commondList = new ArrayList<>();
        Commond c1 = new XhscjdCommond(new XhscjdReciever());
        Commond c2 = new GbjdCommond(new GbjdReciever());
        Commond c3 = new MfCommond(new MfReciever());
        commondList.add(c1);
        commondList.add(c2);
        commondList.add(c3);
        Invoker invoker = new Invoker(commondList);
        invoker.invoke();
    }
}

请求封装成命令,一个请求一个命令,命令持有reciever的引用,最终执行是reciever执行的

public interface Commond {
     void execute();
}

public class GbjdCommond implements Commond{

    private Reciever reciever;

    GbjdCommond (Reciever reciever){
        this.reciever = reciever;
    }
    @Override
    public void execute() {
        reciever.action();
    }
}

public class XhscjdCommond implements Commond{
    private Reciever reciever;

    XhscjdCommond (Reciever reciever){
        this.reciever = reciever;
    }
    @Override
    public void execute() {
        reciever.action();
    }
}

public class MfCommond implements Commond{
    private Reciever reciever;

    MfCommond (Reciever reciever){
        this.reciever = reciever;
    }
    @Override
    public void execute() {
        reciever.action();
    }
}
public abstract class Reciever {
    abstract void action();
}

public class XhscjdReciever extends Reciever{
    @Override
    public void action() {
        System.out.println("做西红柿炒鸡蛋");
    }
}

public class GbjdReciever extends Reciever{

    @Override
    public void action() {
        System.out.println("做宫保鸡丁");
    }
}

public class MfReciever extends Reciever{
    @Override
    public void action() {
        System.out.println("做米饭");
    }
}

命令有了,并且命令也持有reciever的引用, 但是由谁来发起对命令的执行调用呢?

public class Invoker{

    private List<Commond> commondList;

    Invoker(List<Commond> commondList){
        this.commondList = commondList;
    }
    
    public void invoke() {
        for(Commond c:commondList){
            c.execute();
        }
    }
}

返回顶部

上一篇:Java 问题记录


下一篇:【Scrum】-NO.40.EBook.1.Scrum.1.001-【敏捷软件开发:原则、模式与实践】- Scrum