设计原则
开闭原则、里氏替换原则、依赖倒转原则、接口隔离原则、最少知道原则、单一职责原则、合成复用原则
开闭原则
对修改关闭,对扩展开发。
里氏替换原则
子类可以扩展父类的功能,但是不能改变父类原有的功能。比如子类可以覆盖父类的抽象方法(抽象方法在父类中没有实现),但是不能覆盖父类的非抽象方法(非抽象方法属于父类的原有功能,子类覆盖相当与改变父类的功能)
依赖倒转原则
模块之间依赖接口编程,不能依赖实现编程。
接口隔离原则和单一职责原则
接口隔离原则:细化接口,不要建立臃肿的接口。
单一职责原则:类的功能单一。
这两个原则咋一看挺像的,都是强调类/接口的设计要单一。接口隔离原则指的是接口设计方面。单一职责指的是业务方面。
最少知道原则
高内聚、低耦合
合成复用原则
尽量使用聚合、组合的方式,而不是使用继承。
装饰模式
给类增加功能可以通过几种方式?
1、直接修改类的代码。 违背开闭原则。
2、增加子类。 影响类的继承链。
3、通过装饰模式动态给类增加功能。
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();
}
}
java Proxy是通过反射不用解释。cglib底层是通过asm实现的。
asm是什么?
asm的作用就是读取class文件,然后动态改变class(生成代理类)并且加载到jvm中,它代替了jvm对类进行加载。
通过asm生成的代理类如下:
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();
}
}
}
jdk代理是通过反射来调用目标方法,反射的效率及其低,所以asm采用了fastClass机制。通过如下代码来理解一下fastclass:
public class test10 {
public static void main(String[] args){
Test tt = new Test();
Test2 fc = new Test2();
int index = fc.getIndex("f()V");
fc.invoke(index, tt, null);
}
}
class Test{
public void f(){
System.out.println("f method");
}
public void g(){
System.out.println("g method");
}
}
class Test2{
public Object invoke(int index, Object o, Object[] ol){
Test t = (Test) o;
switch(index){
case 1:
t.f();
return null;
case 2:
t.g();
return null;
}
return null;
}
public int getIndex(String signature){
switch(signature.hashCode()){
case 3078479:
return 1;
case 3108270:
return 2;
}
return -1;
}
}
- test是被代理类,test2就是通过asm生成的fastClass,fastclass中对目标类的所有方法都生成一个索引下标,当调用invoke的时候,先根据方法名获取到方法的索引下标,然后直接调用目标方法。
命令模式
/***
* 命令模式:
* 1、将“行为请求者”与“行为实现者”解耦
*
* 2、撤销、重做、记录:命令模式对这三种场景支持的特别好,
* 发起请求只是发起的commond,并不是真正的执行,当需要撤销、重做、记录的时候,
* 如果commond还没被reciever执行,直接对commond拦截即可
*
* 3、会产生大量的类
*
*
* 命令模式涉及到的角色:
* commond:命令接口
* concreteCommond:命令实现类、持有reciever引用
* reciever:
* concreteReciever:真正负责执行命令的类
* invoker:负责调用的类、持有很多commond的引用
*
* 模拟餐厅点菜场景:
* 顾客来餐厅点菜,点了米饭(concreteCommond)、
* 饺子(concreteCommond)、面条(concreteCommond),
* 服务员(invoker)记录下来小单子,
* 将小单子交给厨师,不同食物有不同厨师来做,
* 厨师1做米饭(concreteReciever)、厨师2做饺子(concreteReciever)、厨师3做面条(concreteReciever)
*
*/
public class Ads2Application {
public static void main(String[] args) {
Invoker invoker = new Invoker();//创建一个服务员
invoker.setCommond(new MfCommond());//点米饭
invoker.setCommond(new JzCommond());//点饺子
invoker.setCommond(new MtCommond());//点面条
invoker.action();//交给厨师
}
}
//持有Reciever
interface Commond{
void doAction();
}
class MfCommond implements Commond{
private MfReciever reciever = new MfReciever();
@Override
public void doAction() {
reciever.cook();
}
}
class JzCommond implements Commond{
private JzReciever reciever = new JzReciever();
@Override
public void doAction() {
reciever.cook();
}
}
class MtCommond implements Commond{
private MtReciever reciever = new MtReciever();
@Override
public void doAction() {
reciever.cook();
}
}
interface Reciever{
void cook();
}
class MfReciever implements Reciever{
@Override
public void cook() {
System.out.println("做米饭");
}
}
class JzReciever implements Reciever{
@Override
public void cook() {
System.out.println("做饺子");
}
}
class MtReciever implements Reciever{
@Override
public void cook() {
System.out.println("做面条");
}
}
/***
* 持有很多commond
* 有个方法往里添加commond
* 有一个执行方法
*/
class Invoker{
private List<Commond> commondList = new ArrayList<>();
public void setCommond(Commond commond){
commondList.add(commond);
}
public void action(){
for(Commond commond:commondList){
commond.doAction();
}
}
}
工厂模式
简单工厂:一个工厂用来生成所有的产品
工厂方法:对工厂进行一层抽象,抽象出一个工厂接口,一个工厂只生成一种产品
抽象工厂方法:超级工厂,用来生产不同工厂
简单工厂
产品类
abstract class Product {
abstract void intro();
}
public class AProduct extends Product{
@Override
void intro() {
System.out.println("可乐");
}
}
public class BProduct extends Product{
@Override
void intro() {
System.out.println("奶茶");
}
}
public class CProduct extends Product{
@Override
void intro() {
System.out.println("咖啡");
}
}
工厂类
public class Factory {
public static Product getProduct(String type) {
switch (type) {
case "A":
return new AProduct();
case "B":
return new BProduct();
case "C":
return new CProduct();
default:
return null;
}
}
}
main方法
public class Test {
public static void main(String[] args) {
//创建具体的工厂
Factory factory = new Factory();
//根据传入的参数生产不同的产品实例
//(按下不同的按钮,获取饮料)
Product A = Factory.getProduct("A");
A.intro();
Product B = Factory.getProduct("B");
B.intro();
Product C = Factory.getProduct("C");
C.intro();
}
}
当增加产品的时候需要修改工厂类的代码,解决此问题接下来看工厂方法模式。
工厂方法模式
产品类同上
工厂类
abstract class Factory {
abstract Product getProduct();
}
public class FactoryA extends Factory{
@Override
Product getProduct() {
return new ProductA();
}
}
public class FactoryB extends Factory{
@Override
Product getProduct() {
return new ProductB();
}
}
main方法
public class Test {
public static void main(String[] args) {
//创建具体的工厂
FactoryA factoryA = new FactoryA();
//生产相对应的产品
factoryA.getProduct().intro();
FactoryB factoryB = new FactoryB();
factoryB.getProduct().intro();
}
}
工厂方法对工厂类做了一层抽象,当需要增加新产品的时候不是改工厂类代码,而是增加删除工厂类。
工厂类的缺点是类太多(一个产品对应一个类),解决此问题继续看抽象工厂方法(一个工厂生产多个产品)。
抽象工厂
/***
* 抽象工厂模式:
* 将一系列产品作为一个产品族,用一个具体产品族工厂来创建。
*
* 扩展不方便
*
*
* 场景:应用程序要设计一个按钮,在不同操作系统中这个按钮有不同的属性,
* 属性包括:颜色、位置、形状。
* 比如在linux系统中这个按钮是红色、上边、圆形。
* 在windows中这个按钮是黑色、下边、正方形。
*
* 操作系统代表一个产品族。 颜色、形状、位置都当成具体产品。不同产品组成不同产品族
*
* 具体工厂生产不同的产品族(linux工厂生产linux按钮、window生产windows按钮)
*
* 在抽象出一个超级工厂,用来负责生产具体工厂
*
* 角色包括:
* 1、product:
* 2、concreteProduct:产品(比如红色、黑色、圆形、下边等等)
* 3、productFamily:
* 4、concreteProductFamily:产品族(linux按钮,三种产品:红色、上边、原型)
* 5、Factory:
* 6、concreteFactory:产品族工厂,用来生产产品族
*
*/
public class Ads2Application {
public static void main(String[] args) {
SuperFactory factory = new SuperFactory();
System.out.println(factory.create(FactoryEnum.linux));
}
}
//定义产品
interface Product{
String produce();//方便观察,把返回的产品改成字符串
}
class RedColorProduct implements Product{
@Override
public String produce() {
return "red";
}
}
class BlackColorProduct implements Product{
@Override
public String produce() {
return "black";
}
}
class TopProduct implements Product{
@Override
public String produce() {
return "top";
}
}
class FootProduct implements Product{
@Override
public String produce() {
return "foot";
}
}
class CircleProduct implements Product{
@Override
public String produce() {
return "circle";
}
}
class SquareProduct implements Product{
@Override
public String produce() {
return "square";
}
}
//定义产品族
interface ProductFamily{
String produce();////方便观察,把返回的产品族改成字符串
}
class LinuxButton implements ProductFamily{
@Override
public String produce() {
//linux生产红色 + 上边 + 圆形按钮
return new RedColorProduct().produce() + "_" + new TopProduct().produce() + "_" + new CircleProduct().produce();
}
}
class WindowsButton implements ProductFamily{
@Override
public String produce() {
//linux生产黑色 + 下边 + 正方形按钮
return new BlackColorProduct().produce() + "_" + new FootProduct().produce() + "_" + new SquareProduct().produce();
}
}
//定义产品族工厂
interface Factory{
String doCreate();
}
class LinuxFactory implements Factory{
@Override
public String doCreate() {
return new LinuxButton().produce();
}
}
class WidowsFactory implements Factory{
@Override
public String doCreate() {
return new WindowsButton().produce();
}
}
//加一个面向客户端的超级工厂角色
class SuperFactory{
Factory linuxFactory= new LinuxFactory();
Factory windowsFactory= new WidowsFactory();
public String create(FactoryEnum para){
if(para.equals(FactoryEnum.linux)){
return linuxFactory.doCreate();
}else if(para.equals(FactoryEnum.windows)){
return windowsFactory.doCreate();
}else{
return null;
}
}
}
enum FactoryEnum{
linux,windows
}
策略模式
这个模式比较简单,定义几个策略,客户端告诉我用哪个策略,就执行哪个策略即可。客户端来选择策略。
比如实现一个计算机的加减乘除
首先定义一个策略的公共接口
public interface Strategy {
int count(int a,int b);
}
然后加、减法分别实现该接口
public class AddStrategy implements Strategy{
@Override
public int count(int a, int b) {
return a+b;
}
}
public class DivStrategy implements Strategy{
@Override
public int count(int a, int b) {
return a-b;
}
}
main方法
public static void main(String[] args) {
//客户端需要知道具体策略,执行创建策略发起调用
new AddStrategy().count(2,3);
}
委派模式
模板方法
定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模板方法可以不改变一个算法的结构即可重新定义该算法的某些特定步骤
tomcat组件生命周期就是用的模板方法。
public interface Lifecycle {
void init() throws LifecycleException;
}
public final synchronized void init() throws LifecycleException {
if (!this.state.equals(LifecycleState.NEW)) {
this.invalidTransition("before_init");
}
try {
this.setStateInternal(LifecycleState.INITIALIZING, (Object)null, false);
this.initInternal();//此方法由子类实现
this.setStateInternal(LifecycleState.INITIALIZED, (Object)null, false);
} catch (Throwable var2) {
ExceptionUtils.handleThrowable(var2);
this.setStateInternal(LifecycleState.FAILED, (Object)null, false);
throw new LifecycleException(sm.getString("lifecycleBase.initFail", new Object[]{this.toString()}), var2);
}
}
建造模式
将构建对象的过程封装到一个Builder中,客户端不需要直到构造过程,只认识Builder这个对象即可。
mybatis缓存对象就的通过建造模式构建的。
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
//CacheBuilder
public Cache build() {
// 设置默认的缓存类型(PerpetualCache)和淘汰策略(LruCache)
setDefaultImplementations();//入口1
// 通过反射创建缓存
//入口2
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
//接下来是给缓存设置装饰器(缓存的各种属性都封装成一个个装饰器)
if (PerpetualCache.class.equals(cache.getClass())) {//默认缓存
// 遍历装饰器集合,应用装饰器
for (Class<? extends Cache> decorator : decorators) {
// 通过反射创建装饰器实例
cache = newCacheDecoratorInstance(decorator, cache);
// 设置属性值到缓存实例中
setCacheProperties(cache);
}
// 应用标准的装饰器,比如 LoggingCache、SynchronizedCache
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {// 具有日志装饰功能的缓存
cache = new LoggingCache(cache);
}
return cache;
}