设计模式的一点思考

4/16/2022 设计模式工厂模式代理模式

# 设计模式

抓住变化,封装变化

关键词:面向对象变化抽象复用可扩展

# What

Christopher Alexander says, "Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice"。

在《设计模式》一书中,引用了Alexander的话,模式是重复问题的解决方案。下面这句话对于设计模式的概念则更为贴切。

describe simple and elegant solutions to specific problems in object-oriented software design

面向对象软件设计中特定问题的简单而优雅的解决方案。一种经验总结。

# Why

面向对象的思想,公式:对象 + 交互。其核心就是抽象出人的思维方式,为了可扩展性。

至于设计模式,即面向对象设计的优秀实践(圣经典范)。

You'll have insights that can make your own designs more flexible, modular, reusable, and understandable.

为了更灵活、模块化、可复用、可读性的设计(代码)。这不就是抽象的品质特性吗。

# How

  1. Consider how design patterns solve design problems.
  2. Scan Intent sections.
  3. Study how patterns interrelate.
  4. Study patterns of like purpose.
  5. Examine a cause of redesign.
  6. Consider what should be variable in your design. This approach is the opposite of focusing on the causes of redesign. Instead of considering what might force a change to a design, consider what you want to be able to change without redesign. The focus here is on encapsulating the concept that varies, a theme of many design patterns. Table 1.2 lists the design aspect(s) that design patterns let you vary independently, thereby letting you change them without redesign.

image-20220110203626155

image-20220110203724728

下面就是实战了,理解了每个设计模式的意图,以及设计模式之间的关联。

设计模式并不局限于这些,选择最适合的模式去封装这些变化。很多小伙伴并不完全了解这些模式,但是面向对象(抽象)思维很好的话,无形之中就已经使用了。但是我们学习设计模式,学习这些经验,可以省去一些步骤。如何找到变化呢?

一般需要从设计分析,对象分析,职责分析以及扩展性等。之后再去根据设计原则、设计模式、具体措施去封装变化。

# Creational Patterns(创建型模式)

Creational design patterns abstract the instantiation process.

# 工厂模式

# SIMPLE FACTORY(简单工厂模式)

客户端决定传递什么类型的参数,在工厂中根据不同的参数决定创建不同的对象,即工厂内部有决策创建产品的逻辑。新增产品时,需修改工厂逻辑。不符合开闭原则。

# FACTORY METHOD(工厂模式)

具体工厂内部很纯净,什么类型的工厂就负责创建对应的产品,决策的逻辑全部在客户端中实现。新增产品时,只需新增工厂类型。

# ABSTRACT FACTORY(抽象工厂)

强调产品族(一系列相关产品)

参考https://mp.weixin.qq.com/s/ZQmys84pgfIkWuYU6ClngQ

# BUILDER(建造者模式)

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

典范StringBuilder、BeanDefinitionBuilder。

使用场景:

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。

  • 当构造过程必须允许被构造的对象有不同的表示时。

# PROTOTYPE(原型模式)

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

Clone。

# SINGLETON(单例模式)

  • 懒汉式 DCL...
  • 饿汉式

# Structural Patterns(结构型 模式)

Structural patterns are concerned with how classes and objects arecomposed to form larger structures.

# ADAPTER(适配器)

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

image-20220110222827168

# Participants:

适配器模式(Adapter)包含以下主要角色。

  1. 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
  2. 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
  3. 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

# BRIDGE(桥接模式)

Decouple an abstraction from its implementation so that the two can vary independently. 将抽象部分与它的实现部分分离,使它们都可以独立地变化。

实现方式为组合

image-20220111152350608

# Participants:

桥接(Bridge)模式包含以下主要角色。

  1. 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
  2. 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  3. 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
  4. 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。

# COMPOSITE(组合模式)

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

image-20220111155848918

《设计模式》提到的几个注意:

  • Maximizing the Component interface.

  • Declaring the child management operations.The decision involves a trade-off between safety and transparency:

    • Defining the child management interface at the root of the class hierarchy gives you transparency, because you can treat all components uniformly. It costs you safety, however, because clients may try to do meaningless things like add and remove objects from leaves.

    • DefiningchildmanagementintheCompositeclassgivesyousafety, because any attempt to add or remove objects from leaves will be caught at compile-time in a statically typed language like C++. But you lose transparency, because leaves and composites have different interfaces.

    透明性和安全性之间平衡

  • Should Component implement a list of Components?

# DECORATOR(装饰器模式)

**Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.**动态地给一个对象添加一些额外的职责,就扩展功能来说,DECORATOR相比于继承更加灵活。

image-20220111220721093

# Participants:

  1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

# PROXY(Surrogate代理)

Provide a surrogate or placeholder for another object to control access to it.

提供一种代理或占位符以控制其他对象的访问。

image-20220111222129657

# 静态代理

package proxy;

public class ProxyTest {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.Request();
    }
}

//抽象主题
interface Subject {
    void Request();
}

//真实主题
class RealSubject implements Subject {
    public void Request() {
        System.out.println("访问真实主题方法...");
    }
}

//代理
class Proxy implements Subject {
    private RealSubject realSubject;

    public void Request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        preRequest();
        realSubject.Request();
        postRequest();
    }

    public void preRequest() {
        System.out.println("访问真实主题之前的预处理。");
    }

    public void postRequest() {
        System.out.println("访问真实主题之后的后续处理。");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 动态代理

# JDK

/**
 * 接口
 *
 * @author Hireek
 * @date 2022/1/12 11:20
 */
public interface IHello {
    void say(String name);
}
/**
 * 目标类
 *
 * @author Hireek
 * @date 2022/1/12 11:21
 */
public class HelloService implements IHello {
    @Override
    public void say(String name) {
        System.out.println("hello " + name + "!");
    }
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * InvocationHandler
 *
 * @author Hireek
 * @date 2022/1/12 11:23
 */
public class ObjectInvocationHandler<T> implements InvocationHandler {

    private T object;

    public ObjectInvocationHandler(T object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // before
        System.out.println("before");

        Object invoke = method.invoke(object, args);

        // after
        System.out.println("after");
        return invoke;
    }
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * 生成代理类
 *
 * @author Hireek
 * @date 2022/1/12 11:22
 */
public class ProxyFactory {
    public static <T> T getProxy(T target, InvocationHandler handler) {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
    }
}

/**
 * 测试
 *
 * @author Hireek
 * @date 2022/1/12 11:30
 */
public class ProxyTest {
    public static void main(String[] args) {
        IHello hello = new HelloService();
        IHello proxy = ProxyFactory.getProxy(hello, new ObjectInvocationHandler(hello));
        proxy.say("Hireek");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

控制台输出

before
hello Hireek!
after
1
2
3

主要关注点在代理对象

如何生成?

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) 
1
2
3
  • 获取被代理(目标)对象的引用,并且获取它的所有接口,反射获取
  • JDK动态代理类重新生成一个新的代理类,同时新的代理类要实现被代理类实现的所有接口
  • 动态生成Java 代码
  • 编译Java 代码,生成.class 文件
  • 重新加载到 八VM 中运行
  • 返回代理了类

代理对象是什么?

代理对象反编译源码,jdk11配置jvm参数-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true

package com.sun.proxy;

import designpattern.proxy.IHello;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements IHello {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void say(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1}); // 调用handler的invoke方法。
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("designpattern.proxy.IHello").getMethod("say", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

总结:jdk动态代理,通过反射,字节码重组,动态生成代理类。去调用InvocationHandler的invoke方法。从而动态实现保护或扩展。

# CGLib

Java代码实现

/**
 * HelloService单独添加tell()方法
 *
 * @author Hireek
 * @date 2022/1/12 11:21
 */
public class HelloService implements IHello {
    @Override
    public void say(String name) {
        System.out.println("hello " + name + "!");
    }

    public String tell(String sth) {
        System.out.println("tell " + sth + " is ok!");
        return "tea";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * 方法拦截器
 *
 * @author Hireek
 * @date 2022/1/12 15:05
 */
public class DiMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("CGLIB before");
        Object res = methodProxy.invokeSuper(o, objects);
      //        Object invoke = methodProxy.invoke(target, objects); 传入被代理的对象也🉑️
        System.out.println("CGLIB after");
        return res;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * 获取代理类
 *
 * @author Hireek
 * @date 2022/1/12 15:01
 */
public class CglibProxy {

    public static <T> T getProxy(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类class
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new DiMethodInterceptor());
        // 创建代理类
        return (T) enhancer.create();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 测试
 *
 * @author Hireek
 * @date 2022/1/12 15:08
 */
public class CglibTest {
    public static void main(String[] args) {
      	// 将CGLib代理类的字节码文件输出到磁盘
      	System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"com");
        HelloService helloService = CglibProxy.getProxy(HelloService.class);
        helloService.tell("u");
    }
}
/* console output

CGLIB before
tell u is ok!
CGLIB after

*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

image-20220112174136884

第一个类级Cglib代理类

第二个代理类的FastClass

第三个目标类(被代理类的)FastClass

public class HelloService$$EnhancerByCGLIB$$b6cab590 extends HelloService implements Factory {
  private MethodInterceptor CGLIB$CALLBACK_0;
  private static final MethodProxy CGLIB$equals$2$Proxy;
  static void CGLIB$STATICHOOK1() {
    Class var0 = Class.forName("designpattern.proxy.HelloService$$EnhancerByCGLIB$$b6cab590");
    Class var1;
    var1 = Class.forName("designpattern.proxy.HelloService");
    // 创建MethodProxy(tell)
    CGLIB$tell$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "tell", "CGLIB$tell$0");

  }
  // ...
  final String CGLIB$tell$0(String var1) {
    return super.tell(var1);
  }
  public final String tell(String var1) {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {
      CGLIB$BIND_CALLBACKS(this);
      var10000 = this.CGLIB$CALLBACK_0;
    }

    return var10000 != null ? (String)var10000.intercept(this, CGLIB$tell$0$Method, new Object[]{var1}, CGLIB$tell$0$Proxy) : super.tell(var1);
  }
  // ...

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

MethodProxy

private void init() {
		/*
		 * Using a volatile invariant allows us to initialize the FastClass and
		 * method index pairs atomically.
		 *
		 * Double-checked locking is safe with volatile in Java 5.  Before 1.5 this
		 * code could allow fastClassInfo to be instantiated more than once, which
		 * appears to be benign.
		 */
		if (fastClassInfo == null) { // 懒加载
			synchronized (initLock) {
				if (fastClassInfo == null) {
					CreateInfo ci = createInfo;

					FastClassInfo fci = new FastClassInfo();
					fci.f1 = helper(ci, ci.c1); // 创建
					fci.f2 = helper(ci, ci.c2);
					fci.i1 = fci.f1.getIndex(sig1);
					fci.i2 = fci.f2.getIndex(sig2);
					fastClassInfo = fci;
					createInfo = null;
				}
			}
		}
	}
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
		try {
			init();
			FastClassInfo fci = fastClassInfo;
			return fci.f2.invoke(fci.i2, obj, args);
		}
		catch (InvocationTargetException e) {
			throw e.getTargetException();
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

image-20220112202934478

image-20220112203101900

大概流程:

代理对象调用tell(String vail)方法——> MethodInterceptor.intercept()方法 ——>methodProxy.invokeSuper(o, objects);——>

执行被代理类的FastClass invoke的第八个方法——> 代理类的tell方法——> (父类)代理对象的方法。

到这里就解析完毕了,有点东西的,绕来绕去。

Cglib之所以性能高,原因大概就是又生成了FastClass的,避免了发射去调用方法。

最后一个疑问?为什么MethodProxy有两个方法,invoke和invokeSuper?大家可以去了解哈。再对比下Spring如何使用的。

# 总结

讲了为数不多的几种设计模式,经验永远都是靠积累的,思维习惯也是。面对复杂的业务,如何静心思考,找出变与不变,如何提高抽象的品质,都是需要思考,笔者比较菜,就不废话了。

加油,愿你能追逐真理,即使不存在。

参考:

http://c.biancheng.net/view/1317.html

《设计模式》