当前位置:首页 > 服务端 > 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结)

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结)

2022年09月16日 22:51:20服务端4

要分析常用框架spring、mybatis、springboot、springcloud等的源码,首先要了解各种设计模式,因为框架里面应用了各种设计模式

一、设计思想

学习设计模式最重要的是掌握设计思想和设计原则,理解了设计思想和设计原则并运用到平时的编码中是最重要的!!!

1. 我们先来看下面的问题:

天天加班编程,编程到底都做的是什么?

  撸代码,加班撸代码,写接口、写类、写方法

用设计模式或做设计的作用是什么?
  指导、规定该如何撸代码,如何来写接口、写类、写方法

为什么要做设计、用设计模式?

  代码会变,为应对变化,为了以后方便扩展。做到以不变应万变,做一个会偷懒的程序员!

2.下面来看一下什么是设计思想

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

 

首先是从现实出发理清现实,在写代码之前先从实际分析,然后就开始写代码,写代码时要区分出不变的代码和会变化的代码,会变得代码会怎么变,使用者如何隔绝这种变化,所谓的隔绝这种变化就是不让调用者感知到内部的变化,只需要很简单的方式就能使用不必关心内部的逻辑,这样的话就要用到各种设计模式。不同的变化方式对应不同的设计模式。

 设计的最终体现:如何来定义类、接口、方法

3. 设计思想—OOP

3.1 OOP中的几个元素

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

1)类是做什么用的?
  模拟现实,封装数据与代码

2)接口是做什么用的?
  定义相接的口子

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

  定义功能使用者和功能提供者间的接口
3)为什么要有接口?
  隔离变化

4)抽象类是做什么用的?
包容不变与变的

3.2 OOP的三大特性

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

1)多态为我们提供了什么?
  一种实现变化的方式

3.3 类与类之间的关系有哪些?

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

二、设计原则

1. 找出变化,分开变化和不变的

   隔离,封装变化的部分,让其他部分不受它的影响。

2. 面向接口编程 ——隔离变化的方式

   使用者使用接口,提供者实现接口。“接口”可以是超类!

3. 依赖倒置原则(里氏替换原则)——隔离变化的方式

   依赖抽象,不要依赖具体类!

4. 对修改闭合,对扩展开放——隔离变化的方式

  可以继承一个类或者接口扩展功能,但是不能修改类或者接口的原有功能

5. 多用组合,少用继承——灵活变化的方式

  “有一个”可能比“是一个”更好。

6. 单一职责原则——方法设计的原则

  每个方法只负责一个功能,不要把很多功能写在一个方法里面

三、各种设计模式介绍

应用设计模式的目的:

易扩展,易维护

少改代码,不改代码

1.策略模式

示例:

京东、天猫双十一促销,各种商品有多种不同的促销活动:
满减:满400减50
每满减:每满100减20
数量折扣:买两件8折、三件7折
数量减:满三件减最低价的一件
……
顾客下单时可选择多种促销活动的其中一种来下单
后端代码中如何来灵活应对订单金额的计算?以后还会有很多的促销活动出现!

1.1 该如何来实现订单金额的计算?

控制器OrderController.java

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     *  计算订单的促销金额
     */
    @RequestMapping("prepare")
    public Order prepareOrder(Order order, String promotion) {
        ……..
        return this.orderService.prepareOrder(order, promotion);
    }
}

OrderService.java改怎么来写呢

@Service
public class OrderService {
    public Order prepareOrder(Order order, String promotion) {
    
    // 该如何写
    return order;
    }
}

1.2 这样可以吗?

@Service
public class OrderService {
    public Order prepareOrder(Order order, String promotion) {
        switch (promotion) {
        case "promotion-1":
            // 促销1的算法
            ……
            break;
        case "promotion-2":
            // 促销2的算法
            ……
            break;
        case "promotion-3":
            // 促销3的算法
            ……
            break;
        ……
        }
        return order;
    }
}

营销活动有很多,这个switch会变得很庞大,不利于维护,并且很容易引入新的问题

1.3 改进一下,这样是不是好些了?

@Service
public class OrderService {
    public Order prepareOrder(Order order, String promotion) {
        switch (promotion) {
        case "promotion-1":
            // 促销1的算法
            return calPromotion1(order);
        case "promotion-2":
            // 促销2的算法
            return calPromotion2(order);
        case "promotion-3":
            // 促销3的算法
            return calPromotion3(order);
        ……
        }
        return order;
    }

    private Order calPromotion1(Order order) {
        System.out.println("促销1计算..............................");
        return order;
    }
    …….
}

把每个促销算法单独抽出一个方法,新加入一个促销活动只需要新增一个方法和case就可以了

这里利用了设计原则的方法设计原则:单一职责原则

但是这样写还会存在如下问题:

营销活动经常变,这个switch就得经常改,还得不断加促销的算法方法…….

改代码是bug的源泉,我们希望少改动OrderService!!!

分析:这里变的是什么?

  促销的金额的算法!同一行为的不同算法!

我们不希望OrderService被算法代码爆炸!

1.4 再次改进

同一行为的不同算法实现,我们可以用接口来定义行为,不同的算法分别去实现接口。

这里利用了设计原则:对修改关闭,对扩展开放!

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

这就是策略模式的应用!

策略模式的的定义:

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的用户而独立变化。

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

1.5 使用策略模式再次改进后的OrderService

@Service
public class OrderService {
    public Order prepareOrder(Order order, String promotion) {
        switch (promotion) {
        case "promotion-1":
            // 促销1的算法
            return new Promotion1Calculation().calculate(order);
        case "promotion-2":
            // 促销2的算法
            return new Promotion2Calculation().calculate(order);
        case "promotion-3":
            // 促销3的算法
            return new Promotion3Calculation().calculate(order);
        ......
        }
    }
}

 但是switch中的代码还是会不断变!!!switch中需要知道所有的实现!

 如何让OrderService的代码不要改变?

  把变的部分移出去!改怎么移呢?

1.6 通过一个工厂来专门负责创建各种促销计算实现,就把变化移出来了!

@Component
public class PromotionCalculationFactory {

    public PromotionCalculation getPromotionCalculation(String promotion) {
        switch (promotion) {
        case "promotion-1":
            // 促销1的算法
            return new Promotion1Calculation();
        case "promotion-2":
            // 促销2的算法
            return new Promotion2Calculation();
        case "promotion-3":
            // 促销3的算法
            return new Promotion3Calculation();
        ......
        }
    }
}

 这是简单工厂模式:所有产品由一个工厂创建

@Service
public class OrderService {
    @Autowired
    private PromotionCalculationFactory promotionCalculationFactory;
    
    public Order prepareOrder(Order order, String promotion) {
        return promotionCalculationFactory.getPromotionCalculation(promotion).calculate(order);
    }
}

 想要工厂中的代码也不要随促销的变化而变化,你觉得该怎么办?

 方式一:promotion = beanName

  把各种促销算法的实现交给spring容器来管理,用户选择的促销活动promotion 作为bean的名字,在PromotionCalculationFactory 工厂里面通过getBean("promotion")就能拿到各种促销算法的实现了

方式一的伪代码实现:

spring里面的bean配置:

<bean id="promotion1" calss="Promotion1Calculation">
<bean id="promotion2" calss="Promotion2Calculation">
<bean id="promotion3" calss="Promotion3Calculation">

PromotionCalculationFactory 工厂改写:

@Component
public class PromotionCalculationFactory {

    public PromotionCalculation getPromotionCalculation(String promotion) {
        return getBean("promotion1/promotion2/promotion3");
        }
    }
}

方式二: 配置promotion与实现类的对应关系

   把用户选择的促销活动promotion和对应的促销算法的实现类放到map里面,或者存到数据库里面,在PromotionCalculationFactory 工厂里面通过map.get("promotion"),或者从数据库里面获取对应促销算法的实现类路径通过Class.forName("促销算法的实现类路径")就能拿到各种促销算法的实现了

方式二的伪代码实现:

PromotionCalculationFactory 工厂改写:

package com.study.design.mode.service;

import java.util.Map;

import org.springframework.stereotype.Component;

@Component
public class PromotionCalculationFactory {

    private Map<String, PromotionCalculation> maps;

    public PromotionCalculation getPromotionCalculation(String promotion) {
        PromotionCalculation prom = maps.get(promotion);
        if (prom == null) {
            // 从配置的地方加载
            prom = getFromDb(promotion);
            if (prom != null)
                maps.put(promotion, prom);
        }

        return prom;
    }

    public void init() {
        // 第一次将所有的促销策略都加载到Map中
    }

    private PromotionCalculation getFromDb(String promotion) {
        // 从数据库中取到对应的类名
        //配置的格式: promotion1=com.study.dn.promotion.calculation.Promotion1
        String className = 从数据库(或其他配置源)中获得;
        //
        Class c = Class.forName(className);

        // 实例化
        
        // 返回
    }
}

2. 工厂模式

2.1 简单工厂模式

一个工厂负责创建所有实例。比如上面的策略模式中使用的就是简单工厂模式

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

根据传入的工厂类型参数String创建对应的实例(产品)

2.2 工厂方法模式

父类中定义工厂方法,各子类在+factoryMethod():Product方法里面实现具体的实例创建

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

使用者持有具体的工厂ChildAClass、ChildBClass、ChildCClass,传入对应的工厂ChildAClass、ChildBClass、ChildCClass创建对应的工厂实例

2.3 抽象工厂模式

 定义一个工厂接口,所有具体工厂实现工厂接口

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

使用者调用FactoryProducer的getFactory(type)方法传入type,type为AFactory、BFactory、CFactory对应的类型,就会返回对应的工厂AFactory、BFactory、CFactory,不需要传入AFactory、BFactory、CFactory,因为type已经跟AFactory、BFactory、CFactory绑定了。

3. 装饰者模式

 示例:促销活动可多重叠加,该如何灵活实现订单金额计算?

 OrderController

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * 计算订单的促销金额,促销按给入的顺从叠加
     */
    @RequestMapping("prepare")
    public Order prepareOrder(Order order, String... promotion) {
        return this.orderService.prepareOrder(order, promotion);
    }
}

 OrderService

@Service
public class OrderService {
    @Autowired
    private PromotionCalculationFactory promotionCalculationFactory;

    public Order prepareOrder(Order order, String... promotion) {

        for (String p : promotion) {
            order = promotionCalculationFactory.
            getPromotionCalculation(p).calculate(order);
        }
        return order;

    }
}

 装饰者模式的定义:以装饰的方式,动态地将责任附加到对象上。

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

说明:

 不改变具体类代码(被装饰者ConcreteComponent),动态叠加增强行为功能。

 若要扩展功能,装饰者提供了比继承更有弹性的替代方案

 相较于前面的for循环,有何区别?

   当需要对一个类的多个方法进行增强,使用者会随意使用被增强方法时,for循环就不够灵活了。

责任链和装饰者模式完成的是相同的事情。

装饰者模式-代码示例:

共同的需装饰的行为定义成接口

public interface Component {
    String methodA();
    int methodB();
}

 被装饰者实现接口Component

public class ConcreteComponent implements Component {
    public String methodA() {
        return "concrete-object";
    }
    
    public int methodB() {
        return 100;
    }
}

 装饰者实现接口Component

public class Decorator implements Component {
    //装饰者包含被装饰者(被装饰者实现的接口)
    protected Component component;

    public Decorator(Component component) {
        super();
        this.component = component;
    }
    public String methodA() {
        return this.component.methodA();
    }
    public int methodB() {
        return this.component.methodB();
    }
}

 装饰者派生出的装饰者

public class DecoratorA extends Decorator {
    
    public DecoratorA(Component component) {
        super(component);
    }
    public String methodA() {
        //在这里可以进行前置增强,实现要处理的逻辑
        return this.component.methodA() + " + A";
        //在这里可以进行后置增强,实现要处理的逻辑
    }
    public int methodB() {
        //在这里可以进行前置增强,实现要处理的逻辑
        return this.component.methodB() + 10;
        //在这里可以进行后置增强,实现要处理的逻辑

    }
}

调用示例:

public class DecoratorSample {
    public static void main(String[] args) {
       //创建一个被装饰者
        Component cc = new ConcreteComponent();
       //创建一个派生的装饰者,同时把被装饰者传入装饰者里面,即说的装饰者包含被装饰者
        cc = new DecoratorA(cc);
        //方法调用
        System.out.println(cc.methodA());
        System.out.println(cc.methodB());
    }
}

输出结果: 

concrete-object + A

110

4. 代理模式

4.1 定义

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

作用:不改变原类的代码,而增强原类对象的功能,可选择前置、后置、环绕、异常处理增强

 代理模式的类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

类图与装饰者模式一样,那么代理模式和装饰者模式有什么区别呢?

代理模式意在在代理中控制使用者对目标对象的访问,以及进行功能增强。装饰者模式意在对功能的叠加,比如对多种促销活动的叠加

4.3 代理模式的实现方式

代理模式有两种实现方式:

静态代理:由程序员创建或由特定工具自动生成代理类源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

动态代理:代理类在程序运行时,运用反射机制动态创建而成。

静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。

4.3.1 静态代理:

有一个土豪要找苍老师约会,他不能直接和苍老师约,需要经过一个中间代理Tony

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

需要被代理控制增强的行为定义成接口或者超类

public interface Girl {
    boolean dating(float length);
}

代理和被代理的目标对象都要实现接口Girl

 代理:

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 代理类实现Girl
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class Tony implements Girl {

    //代理类持有被代理的目标对象TeacherCang(目标对象实现的超类或者接口)
    private Girl girl;

    public Girl getGirl() {
        return girl;
    }

    public void setGirl(Girl girl) {
        this.girl = girl;
    }

    //代理:控制、增强被代理对象的行为
    public boolean dating(float length) {
        // 前置增强
        doSomethingBefore();
        boolean res = this.girl.dating(length);
        // 后置增强
        doSomethingAfter();
        return res;
    }

    private void doSomethingBefore() {
        System.out.println("老板,这个我试过了,很不错,推荐给你!");
    }
    
    private void doSomethingAfter() {
        System.out.println("老板,你觉得怎样,欢迎下次再约!");
    }

}

 被代理的目标对象

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 被代理的目标对象实现Girl
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class TeacherCang implements Girl {
    public boolean dating(float length) {
        if (length >= 1.7F) {
            System.out.println("身高可以,可以约!");
            return true;
        }
        System.out.println("身高不可以,不可约!");
        return false;
    }
}

 土豪使用者

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 使用者
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class TuHao {

    private float length;

    public TuHao(float length) {
        super();
        this.length = length;
    }

    public float getLength() {
        return length;
    }

    public void setLength(float length) {
        this.length = length;
    }

    //约会
    public void dating(Girl g) {
        g.dating(length);
    }

}

 调用示例:

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 调用示例
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class PlayGame {

    public static void main(String[] args) {
        //创建土豪(使用者)、苍老师(目标对象)、tony(代理)三个对象
        TuHao th = new TuHao(1.7F);
        Girl tc = new TeacherCang();
        Tony tony = new Tony();
        //tony对苍老师进行代理
        tony.setGirl(tc);
        //土豪和tony约
        th.dating(tony);
}

输出结果:

老板,这个我试过了,很不错,推荐给你!
身高可以,可以约!
老板,你觉得怎样,欢迎下次再约!

静态代理缺点: 

  扩展能力差

    横向扩展:代理更多的类

    纵向扩展:增强更多的方法

  可维护性差

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

由于静态代理的扩展能力差、可维护性差,这就需要使用动态代理了!!!

4.3.2 动态代理

 在运行时,动态为不同类的对象创建代理,增强功能。灵活扩展,易维护!

 动态代理的实现方式:

JDK动态代理:只可对接口创建代理

CGLIB动态代理:可对接口、类创建代理

(1) JDK动态代理

 在运行时,对接口创建代理对象

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

生成代理类$Proxy0的方法:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

参数说明:

ClassLoader loader:类加载器

Class<?>[] interfaces:需要被代理的目标对象实现的接口,可以传入多个

InvocationHandler h:功能增强的接口

功能增强的接口:

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

参数说明:

Object proxy:被代理的目标对象(接口)

Method method:要调用的目标对象的方法

Object[] args:要调用的目标对象的方法的参数

eg1:JDK动态代理-代码示例

被代理控制增强的行为生成接口:

Girl

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 被代理控制增强的行为生成接口Girl
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public interface Girl {
    boolean dating(float length);
}

Boy

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 被代理控制增强的行为生成接口Boy
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public interface Boy {

    boolean dating(char cup);

    void show();
}

被代理的目标对象

TeacherCang

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 被代理的目标对象TeacherCang实现Girl
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class TeacherCang implements Girl {
    public boolean dating(float length) {
        if (length >= 1.7F) {
            System.out.println("身高可以,可以约!");
            return true;
        }
        System.out.println("身高不可以,不可约!");
        return false;
    }
}

TeacherChen

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 被代理的目标对象TeacherChen实现Boy
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class TeacherChen implements Boy {

    public boolean dating(char cup) {
        if (cup == 'E') {
            System.out.println("这个女老板品德正好,可以约!");
            return true;
        }
        System.out.println("这个女老板品德不行,不可以约!");
        return false;
    }

    public void show() {
        System.out.println("开始进入拍摄模式。。。。。。。。");
    }

}

JDK动态代理

package com.study.design.mode.samples.proxy;

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

/**
 * 
 * @Description: JDK动态代理
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class TonyCompany {

    //动态生成代理对象 传入target的是被代理的类
    public static Object proxy(Object target) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new MyInvationHandler(target));
    }

    //特定的功能增强实现
    private static class MyInvationHandler implements InvocationHandler {

        //被被代理的目标对象
        private Object target;

        public MyInvationHandler(Object target) {
            super();
            this.target = target;
        }

        public Object getTarget() {
            return target;
        }

        public void setTarget(Object target) {
            this.target = target;
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 前置增强
            doSomethingBefore();

            // 调用被代理对象的方法
            Object res = method.invoke(target, args);

            // 后置增强
            doSomethingAfter();

            return res;
        }

        private void doSomethingAfter() {
            System.out.println("老板,你觉得怎样,欢迎下次再约!");
        }

        private void doSomethingBefore() {
            System.out.println("老板,这个我试过了,很不错,推荐给你!");
        }

    }
}

调用示例:

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 调用示例
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class PlayGame {

    public static void main(String[] args) {
        System.out.println("----------------1.静态代理TeacherCang-----------------------");
        //创建土豪(使用者)、苍老师(目标对象)、tony(代理)三个对象
        TuHao th = new TuHao(1.7F);
        Girl tc = new TeacherCang();
        Tony tony = new Tony();
        //tony对苍老师进行代理
        tony.setGirl(tc);
        //土豪和tony约
        th.dating(tony);

        System.out.println("----------------2.JDK动态代理TeacherCang-----------------------");
        //生成代理类$Proxy0
        Girl tony1 = (Girl) TonyCompany.proxy(tc);
        //土豪直接和代理tony约
        th.dating(tony1);

        System.out.println("----------------3.JDK动态代理TeacherChen,横向纵向扩展:代理更多的类和方法-----------------------");
        //代理另外一个目标对象TeacherChen
        Boy tcc = new TeacherChen();
        //生成代理类$Proxy0
        Boy tony2 = (Boy) TonyCompany.proxy(tcc);
        //tony2约TeacherChen 纵向扩展:增强更多的方法
        System.out.println("----------------3.1 JDK动态代理TeacherChen,调用TeacherChen的dating方法-----------------------");
        tony2.dating('E');
        System.out.println("----------------3.2 JDK动态代理TeacherChen,调用TeacherChen的show方法-----------------------");
        tony2.show();

    }

}

输出结果:

----------------1.静态代理TeacherCang-----------------------
老板,这个我试过了,很不错,推荐给你!
身高可以,可以约!
老板,你觉得怎样,欢迎下次再约!
----------------2.JDK动态代理TeacherCang-----------------------
老板,这个我试过了,很不错,推荐给你!
身高可以,可以约!
老板,你觉得怎样,欢迎下次再约!
----------------3.JDK动态代理TeacherChen,横向纵向扩展:代理更多的类和方法-----------------------
----------------3.1 JDK动态代理TeacherChen,调用TeacherChen的dating方法-----------------------
老板,这个我试过了,很不错,推荐给你!
这个女老板品德正好,可以约!
老板,你觉得怎样,欢迎下次再约!
----------------3.2 JDK动态代理TeacherChen,调用TeacherChen的show方法-----------------------
老板,这个我试过了,很不错,推荐给你!
开始进入拍摄模式。。。。。。。。
老板,你觉得怎样,欢迎下次再约!

 (2) cglib动态代理

cglib是什么?

cglib( Byte Code Generation Library),一个高层次的java字节码生成和转换的api库.

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

ASM:一个低层次的字节码操作库

它的主要用途

在运行期为类、接口生成动态代理对象。 以达到不改动原类代码而实现功能增强的目的

常在哪里用它?

  常在 AOP、test、orm框架中用来生成动态代理对象、拦截属性访问

 如何使用它?

1)引入它的jar

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.6</version>
</dependency>

2)学习它的API

https://github.com/cglib/cglib/wiki

 cglib动态代理-类图和API

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

说明:

实现思想和前面的JDK动态代理一样,只是使用了不同的API。

代理类由Enhancer生成,代理类实现被代理的类或者接口,特定的功能增强的实现MyMethodInterceptor实现MethodInterceptor接口,特定的功能增强实现MyMethodInterceptor里面持有被代理的类或者接口target

 eg2:cglib动态代理-代码示例

 被代理对象的接口:

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 被代理控制增强的行为生成接口Girl
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public interface Girl {
    boolean dating(float length);
}

 被代理对象:

package com.study.design.mode.samples.proxy;

/**
 * 
 * @Description: 被代理的目标对象TeacherCang实现Girl
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class TeacherCang implements Girl {
    public boolean dating(float length) {
        if (length >= 1.7F) {
            System.out.println("身高可以,可以约!");
            return true;
        }
        System.out.println("身高不可以,不可约!");
        return false;
    }
}

 cglib动态代理主类:

package com.study.design.mode.samples.proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 
 * @Description: cglib动态代理
 * @author leeSamll
 * @date 2018年11月24日
 *
 */
public class CglibDemo {

    // 特定的功能增强的实现
    static class MyMethodInterceptor implements MethodInterceptor {
        //特定的功能增强实现MyMethodInterceptor里面持有被代理的类或者接口target
        private Object target;

        public MyMethodInterceptor(Object target) {
            this.target = target;
        }

        //在intercept方法进行调用被代理类或者接口的方法之前进行拦截实现前置、后置、环绕、异常处理等功能的增强
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

            System.out.println("**************** " + method.getName());
            // 前置增强
            doSomethingBefore();
            // 返回值
            Object res = null;
            // 这里可以调用父类的该方法,当是生成接口的代理时不可调用。
            // Object res = methodProxy.invokeSuper(proxy, args);
            // 通过method来调用被代理对象的方法
            if (this.target != null) {
                res = method.invoke(target, args);
            }
            // 后置增强
            doSomethingAfter();
            return res;
        }

        private void doSomethingBefore() {
            System.out.println("老板你好,这个我试过了,很不错,推荐给你!");
        }

        private void doSomethingAfter() {
            System.out.println("老板你觉得怎样? 欢迎下次.....");
        }
    };

    
    public static void main(String[] args) {
        //创建Enhancer对象用来生成代理类
        Enhancer e = new Enhancer();
        //创建需要被代理的类TeacherCang
        TeacherCang tc = new TeacherCang();
        // 设置增强回调
        e.setCallback(new MyMethodInterceptor(tc));
        
        //对接口生成代理对象
        System.out.println("--------------------cglib动态代理:对接口Girl进行代理----------------------");
        //设置要代理的接口
        e.setInterfaces(new Class[] { Girl.class });
        //生成代理的接口的动态代理对象
        Girl g = (Girl) e.create();
        //调用被代理的接口的dating方法
        g.dating(1.8f);

        // 对类生成代理对象
        System.out.println("--------------------cglib动态代理:对类TeacherCang进行代理----------------------");
        //设置要代理的类
        e.setSuperclass(TeacherCang.class);
        //把前面的设置的接口Girl置为空
        e.setInterfaces(null);
        //当有多个callback时,需要通过callbackFilter来指定被代理方法使用第几个callback
        /* e.setCallbackFilter(new CallbackFilter() {
            @Override
            public int accept(Method method) {
                return 0;
            }
        });*/

        //生成代理的类TeacherCang的动态代理对象
        TeacherCang proxy = (TeacherCang) e.create();
        //调用代理的类TeacherCang的dating方法
        proxy.dating(1.8f);
    }
}

输出结果:

--------------------cglib动态代理:对接口Girl进行代理----------------------
**************** dating
老板你好,这个我试过了,很不错,推荐给你!
身高可以,可以约!
老板你觉得怎样? 欢迎下次.....
--------------------cglib动态代理:对类TeacherCang进行代理----------------------
**************** dating
老板你好,这个我试过了,很不错,推荐给你!
身高可以,可以约!
老板你觉得怎样? 欢迎下次.....

5.责任链模式

5.1  应用场景

http web请求处理,请求过来后将经过转码、解析、参数封装、鉴权......一系列的处理(责任),而且要经过多少处理是可以灵活调整的。

将所有的处理都写在一个类中可否?

  不行

分成多个类如何灵活组合在一起?

责任链:所有的处理者都加入到这个链式,一个处理完后,转给下一个。

责任链模式具体实现步骤:

1)抽象出责任接口,具体责任逻辑实现责任接口。

2)根据处理过程需要,将具体责任实现逻辑组合成链

3)使用者使用链

典型代表:Filter(过滤器)、Intercept(拦截器)

责任链模式类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

和装饰者模式的区别在哪里?

  装饰者模式意在功能的叠加,责任链模式意在链式的处理

eg:责任链模式代码示例

 抽象出责任接口:

/**
 * 
 * @Description: 责任接口
 * @author leeSamll
 * @date 2018年11月25日
 *
 */
public interface Responsibility {

    void process(Request request, ResponsibilityChain chain);
}

 具体的责任逻辑实现责任接口:

ResponsibilityA

package com.study.design.mode.samples.responsibility;

/**
 * 
 * @Description: 具体的责任逻辑实现
 * @author leeSamll
 * @date 2018年11月25日
 *
 */
public class ResponsibilityA implements Responsibility {

    @Override
    public void process(Request request, ResponsibilityChain chain) {
        //前置增强
        System.out.println("Before Responsibility-A done something...");
        //ResponsibilityA处理完以后调用ResponsibilityChain的process方法交给下一个责任逻辑处理
        chain.process(request);
        //后置增强
    }

}

 ResponsibilityB

package com.study.design.mode.samples.responsibility;

/**
 * 
 * @Description: 具体的责任逻辑实现
 * @author leeSamll
 * @date 2018年11月25日
 *
 */
public class ResponsibilityB implements Responsibility {

    @Override
    public void process(Request request, ResponsibilityChain chain) {
        //前置增强
        System.out.println("Before Responsibility-B done something...");
        //ResponsibilityB处理完以后调用ResponsibilityChain的process方法交给下一个责任逻辑处理
        chain.process(request);
        //后置增强
    }

}

 责任链:

ResponsibilityChain

package com.study.design.mode.samples.responsibility;

import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @Description: 责任链,所有的责任加到责任链里面进行处理
 * @author leeSamll
 * @date 2018年11月25日
 *
 */
public class ResponsibilityChain {

    //存放具体的责任逻辑
    private List<Responsibility> responsibilitys;

    private int index = 0;

    public ResponsibilityChain() {
        this.responsibilitys = new ArrayList<>();
    }

    //顺序调用加入的责任逻辑,一个处理完以后交给下一个继续处理,下一个处理完以后会通过this回调process看是否有下一个继续处理
    public void process(Request request) {
        if (this.index < this.responsibilitys.size()) {
            this.responsibilitys.get(index++).process(request, this);
        }
    }

    //加入具体的责任逻辑
    public void register(Responsibility res) {
        this.responsibilitys.add(res);
    }
}

 请求接口:

package com.study.design.mode.samples.responsibility;

/**
 * 
 * @Description: 请求接口
 * @author leeSamll
 * @date 2018年11月25日
 *
 */
public interface Request {
}

 调用者调用示例

package com.study.design.mode.samples.responsibility;

/**
 * 
 * @Description: 调用者调用示例
 * @author leeSamll
 * @date 2018年11月25日
 *
 */
public class PlayGame {
    public static void main(String[] args) {
        //创建一个责任链
        ResponsibilityChain chain = new ResponsibilityChain();
        //往责任链里面加入具体的责任逻辑
        chain.register(new ResponsibilityA());
        chain.register(new ResponsibilityB());

        //开始处理
        chain.process(new Request() {
        });
    }
}

 输出结果:

Before Responsibility-A done something...
Before Responsibility-B done something...

6. 适配器模式

6.1 应用场景

使用者依赖的接口与提供者的接口不匹配时,就加一层适配,而不改两端的代码。

适配器模式类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

 

 说明:

使用者使用Target接口,但是提供者Provider又没有实现Target接口,这个时候就需要加一层适配Adaper,Adaper里面持有Provider,在Adapter的methodA()方法里面调用Provider的methodB方法

 和代理、装饰的区别在哪里?

   适配器模式不进行功能增强

7. 外观(门面)模式

7.1 应用场景

使用方要完成一个功能,需要调用提供方的多个接口、方法,调用过程复杂时,我们可以再提供一个高层接口(新的外观),将复杂的调用过程向使用方隐藏。

外观(门面)模式类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

这里使用了设计原则:最少知识原则(迪米特原则)

8. 观察者模式

 8.1 示例:微信公众号,关注就可以收到推送的消息,取消关注,就不会再收到。

 观察者模式类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

说明:

主题Subject面向观察者接口Observer编程,主题里面可以添加、删除和通知观察者Observer;
注意每个观察者都有一个回调方法update,如果有变化就会在主题的notifyObservers()方法里面调用update方法,把最新的变化给到观察者

变化之处:观察者会变,观察者的数量会变。

不变:主题的代码要不受观察者变化的影响。

观察者模式定义:

定义了对象之间一对多的依赖关系,当一端对象改变状态时,它的所有依赖者都会收到通知并自动更新(被调用更新方法)。也称为:监听模式、发布订阅模式。提供一种对象之间松耦合的设计方式。

设计原则:为了交互对象之间的松耦合设计而努力!

8.2 Java中为我们提供了观察者模式的通用实现

Java.util. Observable 可被观察的(主题),具体主题扩展它。

java.util.Observer 观察者接口,具体观察者实现该接口。

主题Observable:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

观察者接口Observer

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

使用代码示例:

package com.study.design.mode.samples;

/**
 * 
 * @Description: java中提供的观察者设计模式
 * @author leeSamll
 * @date 2018年11月25日
 *
 */
import java.util.Observable;
import java.util.Observer;

public class ObserverSample {

    public static void main(String[] args) {
        
        //创建主题
        Observable subject1 = new Observable() {
            //通知观察者变化的数据data
            public synchronized void notifyObservers(Object data) {
                //设置 java.util.Observable.changed = true表示发生了改变
                setChanged();
                //调用父类的notifyObservers方法通知观察者发生变化 
                //调用链java.util.Observable.notifyObservers(Object)->java.util.Observer.update(Observable, Object)
                super.notifyObservers(data);
            }
        };

        //添加观察者
        subject1.addObserver(new Observer() {
            //主题回调观察者的update方法通知改变
            @Override
            public void update(Observable o, Object arg) {
                System.out.println("观察者1收到通知被更新了..." + arg);
            }
        });

        //添加观察者
        subject1.addObserver(new Observer() {
            //主题回调观察者的update方法通知改变
            @Override
            public void update(Observable o, Object arg) {
                System.out.println("观察者2收到通知被更新了..." + arg);
            }
        });

        //通知改变
        subject1.notifyObservers("change1");
        subject1.notifyObservers("change2");
    }
}

 输出结果:

观察者2收到通知被更新了...change1
观察者1收到通知被更新了...change1
观察者2收到通知被更新了...change2
观察者1收到通知被更新了...change2

9. 命令模式 

 示例:

请为你的系统设计一个命令行界面,用户可输入命令来执行某项功能。

系统的功能会不断增加,命令也会不断的增加。

如何将一项一项的加入到这个命令行界面?

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

 

 

如何让我们的命令程序写好以后,不因为功能的添加而修改,又可灵活的加入命令、功能。

命令模式类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

命令模式的定义:

以命令的方式,解耦调用者与功能的具体实现者,降低系统耦合度,提供了灵活性。

适用场景:Servlet、Controller、线程池

命令模式伪代码示例:

package com.study.design.mode.samples.command;

/**
 * 
 * @Description: 命令模式
 * @author liguangsheng
 * @date 2018年11月25日
 *
 */
public class Receiver {

    //存放具体的命令实现
    private Map<String,Command> commands;
    
    //把具体的命令和对应的实现加入commands
    public void register(String strCommand,Command command) {
        commands.put(strCommand,command);
    }
    
    //使用者调用receive方法传入命令去执行
    public void receive(String command) {
        Command commandObj = commands.get(command);
        if(null != commandObj) {
            commandObj.exceute();
            return;
        }
        System.out.println("不支持此命令" + command);
    }
}

 

 命令模式与策略模式的区别:

命令模式类图:

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

策略模式类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

 

 区别:

策略模式侧重的是一个行为的多个算法的实现,可互换算法。

命令模式侧重的是为多个行为提供灵活的执行方式

10. 状态模式

 示例:一个类对外提供了多个行为,同时该类对象有多种状态,不同的状态下对外的行为表现不同,我们该如何来设计该类,让它对状态可以灵活扩展?

如请为无人自动咖啡售卖机开发一个控制程序。

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

说明:用户可以在咖啡机上进行支付、退款、购买、取咖啡等操作

咖啡机状态转换图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

说明:

不同的状态下这四种操作将有不同的表现。如在没有支付的状态下,用户在咖啡机上点退款、购买、取咖啡,和在已支付的状态下做这三个操作。

普通实现:

package com.study.design.mode.samples.state;

/**
 * 
 * @Description: 普通的咖啡机: 没有使用状态模式的咖啡机
 * @author liguangsheng
 * @date 2018年11月25日
 *
 */
public class CoffeeMachine {

    final static int NO_PAY = 0;
    final static int PAY = 1;
    final static int SOLD = 2;
    final static int SOLD_OUT = 4;

    private int state = SOLD_OUT;
    private int store;

    public CoffeeMachine(int store) {
        this.store = store;
        if (this.store > 0) {
            this.state = NO_PAY;
        }
    }

    public void pay() {
        switch (this.state) {
        case NO_PAY:
            System.out.println("支付成功,请确定购买咖啡。");
            this.state = PAY;
            break;
        case PAY:
            System.out.println("已支付成功,请确定购买咖啡。");
            break;
        case SOLD:
            System.out.println("待取咖啡中,请稍后购买!");
            break;
        case SOLD_OUT:
            System.out.println("咖啡已售罄,不可购买!");
        }
    }

    public void refund() {
        switch (this.state) {
        case NO_PAY:
            System.out.println("你尚未支付,请不要乱按!");
            break;
        case PAY:
            System.out.println("退款成功!");
            this.state = NO_PAY;
            break;
        case SOLD:
            System.out.println("已购买,请取用!");
            break;
        case SOLD_OUT:
            System.out.println("咖啡已售罄,不可购买!");
        }
    }

    // 购买
    public void buy() {
        switch (this.state) {
        case NO_PAY:
            System.out.println("你尚未支付,请不要乱按!");
            break;
        case PAY:
            System.out.println("购买成功,请取用!");
            this.state = SOLD;
            break;
        case SOLD:
            System.out.println("已购买,请取用!");
            break;
        case SOLD_OUT:
            System.out.println("咖啡已售罄,不可购买!");
        }
    }

    // 取coffee
    public void getCoffee() {
        switch (this.state) {
        case NO_PAY:
            System.out.println("你尚未支付,请不要乱按!");
            break;
        case PAY:
            System.out.println("已购买,请取用!");
            break;
        case SOLD:
            System.out.println("请放好杯子,3秒后将出咖啡!");
            this.store--;
            if (this.store == 0) {
                this.state = SOLD_OUT;
            } else {
                this.state = NO_PAY;
            }
            break;
        case SOLD_OUT:
            System.out.println("咖啡已售罄,不可购买!");
        }
    }
}

如何让状态可以灵活扩展?

  从分析可以看出,变化的是状态,同时不同的状态同一个行为的表现不同,这样的话就可以把变化的状态抽象生成接口,然后不同的状态行为实现状态接口做该状态下的具体行为。这里可以采用状态模式

状态模式类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

 

 状态模式代码示例:

 把变化的状态抽象生成接口State,里面含有不同状态下的行为方法

package com.study.design.mode.samples.state;

/**
 * 
 * @Description: 状态接口
 * @author liguangsheng
 * @date 2018年11月25日
 *
 */
public interface State {
    void pay();

    void refund();

    void buy();

    void getCoffee();
}

 不同的状态实现状态接口State

 没有支付状态

package com.study.design.mode.samples.state;

/**
 * 
 * @Description: 没有支付状态
 * @author liguangsheng
 * @date 2018年11月25日
 *
 */
public class NoPayState implements State {

    private NewCoffeeMachine machine;

    public NoPayState(NewCoffeeMachine machine) {
        this.machine = machine;
    }

    @Override
    public void pay() {
        System.out.println("支付成功,请去确定购买咖啡。");
        this.machine.state = this.machine.PAY;
    }

    @Override
    public void refund() {
        System.out.println("你尚未支付,请不要乱按!");
    }

    @Override
    public void buy() {
        System.out.println("你尚未支付,请不要乱按!");
    }

    @Override
    public void getCoffee() {
        System.out.println("你尚未支付,请不要乱按!");
    }

}

 已支付状态

package com.study.design.mode.samples.state;

/**
 * 
 * @Description: 已支付状态
 * @author liguangsheng
 * @date 2018年11月25日
 *
 */
public class PayState implements State {

    private NewCoffeeMachine machine;

    public PayState(NewCoffeeMachine machine) {
        this.machine = machine;
    }

    @Override
    public void pay() {
        System.out.println("您已支付,请去确定购买!");
    }

    @Override
    public void refund() {
        System.out.println("退款成功,请收好!");
        this.machine.state = this.machine.NO_PAY;
    }

    @Override
    public void buy() {
        System.out.println("购买成功,请取用");
        this.machine.state = this.machine.SOLD;
    }

    @Override
    public void getCoffee() {
        System.out.println("请先确定购买!");
    }
}

 售出状态

package com.study.design.mode.samples.state;

/**
 * 
 * @Description: 售出状态
 * @author liguangsheng
 * @date 2018年11月25日
 *
 */
public class SoldOutState implements State {

    private NewCoffeeMachine machine;

    public SoldOutState(NewCoffeeMachine machine) {
        this.machine = machine;
    }

    @Override
    public void pay() {
        System.out.println("当前状态为售出,请取咖啡!");

    }

    @Override
    public void refund() {
        System.out.println("当前状态为售出,不能退款!");

    }

    @Override
    public void buy() {
        System.out.println("当前状态为售出,请取咖啡!");

    }

    @Override
    public void getCoffee() {
        System.out.println("咖啡已出,请取咖啡!");

    }

}

 售罄状态

package com.study.design.mode.samples.state;

/**
 * 
 * @Description: 售罄状态
 * @author liguangsheng
 * @date 2018年11月25日
 *
 */
public class SoldState implements State {

    private NewCoffeeMachine machine;

    public SoldState(NewCoffeeMachine machine) {
        this.machine = machine;
    }

    @Override
    public void pay() {
        System.out.println("咖啡已卖完,不能支付!");

    }

    @Override
    public void refund() {
        System.out.println("不能退款!");

    }

    @Override
    public void buy() {
        System.out.println("咖啡已卖完,不能购买!");

    }

    @Override
    public void getCoffee() {
        System.out.println("咖啡已卖完!");

    }

}

 使用了状态模式的咖啡机

package com.study.design.mode.samples.state;

/**
 * 
 * @Description: 使用了状态模式的咖啡机
 * @author liguangsheng
 * @date 2018年11月25日
 *
 */
public class NewCoffeeMachine {

    final State NO_PAY, PAY, SOLD, SOLD_OUT;
    State state;
    int store;

    //初始化状态
    public NewCoffeeMachine(int store) {
        NO_PAY = new NoPayState(this);
        PAY = new PayState(this);
        SOLD = new SoldState(this);
        SOLD_OUT = new SoldOutState(this);

        this.store = store;
        if (this.store > 0) {
            this.state = NO_PAY;
        }
    }

    //支付行为委托给当前状态实例
    public void pay() {
        this.state.pay();
    }

    //退款行为委托给当前状态实例
    public void refund() {
        this.state.refund();
    }

    //买咖啡行为委托给当前状态实例
    public void buy() {
        this.state.buy();
    }

    //取咖啡行为委托给当前状态实例
    public void getCoffee() {
        this.state.getCoffee();
    }
}

状态模式、命令模式、策略模式的区别

状态模式类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

命令模式类图:

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

策略模式类图:

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

区别:

状态模式应用于状态机的情况

策略模式侧重的是一个行为的多个算法的实现,可互换算法。

命令模式侧重的是为多个行为提供灵活的执行方式

11. 桥接模式

11.1 示例:

请开发一个画图程序,可以画各种颜色不同形状的图像,请用面向对象的思想设计图像

分析: 

1)比如有红、黄、蓝三种颜色

2)形状有方形、圆、三角形

3)圆可以是红圆、黄圆、蓝圆

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

 变化:

 会从两个维度发生变化:形状、颜色

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

任其在这两个维度各自变化,为这两个维度搭个桥,让他们可以融合在一起:桥接模式

桥接模式的实现步骤:

 1)抽象:分别对各自的维度进行抽象,将共同部分抽取出来

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

2)组合:将抽象组合在一起(桥接)

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

桥接模式的定义:将多个维度的变化以抽象的方式组合在一起。使用者面向抽象。个维度间解耦,可自由变化。

12. 单例模式

12.1 饥汉式——可用

饥汉式1——可用

package com.study.design.mode.service;

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

 饥汉式2——可用

package com.study.design.mode.service;

public class Singleton {
    private static Singleton instance;
    static {
        instance = new Singleton();
    }

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }
}

12.2 懒汉式

 懒汉式1——不可用

package com.study.design.mode.service;

public class Singleton {
    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

 当两个线程同时进入if里面时就会创建两个实例,不是单例,线程不安全,所以不可用

 懒汉式2——不推荐使用

package com.study.design.mode.service;

public class Singleton {
    private static Singleton singleton;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

 线程安全,但不推荐使用。缺点是实例化后就不应该再同步了,效率低

懒汉式3——不可用

package com.study.design.mode.service;

public class Singleton {
    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

 当两个线程同时进入if里面时就会产生两个实例,做不到单例

 懒汉式4——双重检查——推荐使用

package com.study.design.mode.service;

public class Singleton {

    private static volatile Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

注意:volatile关键字修饰很关键,保证可见性,一个线程先创建了,其他线程就就会看到这个改变,不会再创建,如果没有这个关键字还是不能保证单例。

优点:线程安全;延迟加载;效率较高

懒汉式5——静态内部类方式——推荐使用

package com.study.design.mode.service;

public class Singleton {
    private Singleton() {
    }

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

优点:避免了线程不安全,延迟加载,效率高

原理:类的静态属性只会在第一次加载类的时候初始化。在这里,JVM的加载机制帮助我们保证了线程安全性,在类进行初始化时,别的线程是无法进入的

懒汉式6——用枚举——推荐使用

package com.study.design.mode.service;

public enum Singleton {
    INSTANCE;
    public void whateverMethod() {
    }
}

13. 模板方法设计模式

 示例:

当我们设计一个类时,我们能明确它对外提供的某个方法的内部执行步骤,但一些步骤,不同的子类有不同的行为时,我们该如何来设计该类?

可以用模板方法设计模式

 框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

优点:

1)封装不变的部分,扩展可变的部分

2)提取公共代码,便于维护。

3)行为由父控制,子类实现。

适用场景:

1)有多个子类共有的方法,且逻辑相同

2)重要的、复杂的方法,可以考虑作为模板方法

模板方法设计模式代码示例:

package com.study.design.mode.service;

public abstract class Game {
    protected abstract void initialize();

    protected abstract void startPlay();

    protected abstract void endPlay();

    // 模板方法
    public final void play() {
        // 初始化游戏
        initialize();
        // 开始游戏
        startPlay();
        // 结束游戏
        endPlay();

    }
}

四、总结

设计模式总结

创建型模式

这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用新的运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

  • 工厂模式(Factory Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)
  • 单例模式(Singleton Pattern)
  • 建造者模式(Builder Pattern)
  • 原型模式(Prototype Pattern)

结构型模式

这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。

  • 适配器模式(Adapter Pattern)
  • 桥接模式(Bridge Pattern)
  • 过滤器模式(Filter、Criteria Pattern)
  • 组合模式(Composite Pattern)
  • 装饰器模式(Decorator Pattern)
  • 外观模式(Facade Pattern)
  • 享元模式(Flyweight Pattern)
  • 代理模式(Proxy Pattern)

行为型模式

这些设计模式特别关注对象之间的通信。

  • 责任链模式(Chain of Responsibility Pattern)
  • 命令模式(Command Pattern)
  • 解释器模式(Interpreter Pattern)
  • 迭代器模式(Iterator Pattern)
  • 中介者模式(Mediator Pattern)
  • 备忘录模式(Memento Pattern)
  • 观察者模式(Observer Pattern)
  • 状态模式(State Pattern)
  • 空对象模式(Null Object Pattern)
  • 策略模式(Strategy Pattern)
  • 模板模式(Template Pattern)
  • 访问者模式(Visitor Pattern)

创建型模式:主要用来创建实例的,创建实例时不要跟具体类捆绑,而是通过工厂等来创建,从而使用者就和具体类解耦了

结构型模式:主要说的是如何来组合利用,把多个实例组合在一起

行为型模式:主要是多种行为、多种功能的的变化,怎么把多个行为功能组合在一起

设计原则总结

1. 变化隔离原则:找出变化,分开变化和不变的

   隔离,封装变化的部分,让其他部分不受它的影响。

2. 面向接口编程 ——隔离变化的方式

   使用者使用接口,提供者实现接口。“接口”可以是超类!

3. 依赖倒置原则(里氏替换原则)——隔离变化的方式

   依赖抽象,不要依赖具体类!

4. 开闭原则:对修改闭合,对扩展开放——隔离变化的方式

  可以继承一个类或者接口扩展功能,但是不能修改类或者接口的原有功能

5. 最少知道原则,又称迪米特法则

6. 多用组合,少用继承——灵活变化的方式

  “有一个”可能比“是一个”更好。

7. 单一职责原则——方法设计的原则

  每个方法只负责一个功能,不要把很多功能写在一个方法里面

最后,如果都忘记了,请一定要记住这三条

框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结) _ JavaClub全栈架构师技术笔记

说明:如果前面的设计思想和设计原则都忘记了,就要找出变化,区分出不变的和变化的,把变化的部分独立出接口,或者使用组合

 

示例代码获取地址:

https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/design-mode-study

 

参考文章:

23中设计模式包括哪些 

作者:小不点啊
来源链接:https://www.cnblogs.com/leeSmall/p/10010006.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/server/42329.html

标签: 设计模式
分享给朋友:

“框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结)” 的相关文章

常用设计模式系列(三)—抽象工厂模式

常用设计模式系列(三)—抽象工厂模式

一、前言 各位大佬好,又是一个冷嗖嗖的日子,这个城市的天气最近一直都不太好,说下雪吧也不下,天气也不晴,让人甚是难受。前段时间周围的城市都下雪了,盼了好久的雪也没盼到,只等来了冷风作祟。基于我的心情,我来吟诗一首: ​ 《盼雪》 昨日已别大雪, 吾昼夜盼雪至。...

Java的几种设计模式,java面试题,java基础笔试题,BAT

Java的几种设计模式,java面试题,java基础笔试题,BAT

写在最前面,我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家。扫码加微信好友进【程序员面试学习交流群】,免费领取。也欢迎各位一起在群里探讨技术。   java的设计模式大体上分为三大类:  ...

SpringMVC的设计模式是什么?

SpringMVC的设计模式是什么?

SpringMVC设计模式是一种通用的软件编程思想。 在SpringMVC设计模式中认为, 任何软件都可以分为三部分组成: 1.控制程序流转的控制器(Controller) 2.封装数据处理数据的模型(Model) 3.负责展示数据的视图(view)...

Java常见设计模式面试题及答案

Java常见设计模式面试题及答案

文章目录 1.设计模式是什么?你是否在代码中使用过? 2. JDK 中常用的设计模式有哪些? 3.单例模式是什么?请用 Java 写出线程安全的单例模式 4...

我也简单谈下《Web应用的缓存设计模式》

拜读了Robbin的文章《Web应用的缓存设计模式》http://robbinfan.com/blog/38/orm-cache-sumup ,我觉得大体思想还是值得学习和借鉴的,借这机会顺便简单谈谈我一般的做法,基于它文章Blog的例子和场景。 以读取博客文章列表和文章为例...

常用设计模式

常用设计模式

设计模式 从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。 设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联和组合关系的充分理解。 正确使用设计模式具有一下优点: 1、...

设计模式六大原则

设计模式六大原则

2019年2月26日19:41:21 设计模式六大原则 为什么会有六大原则 有言曰,“无规矩不成方圆”,有“规”才能画“圆”,那设计模式要遵循的六大原则要画一个什么的“圆”呢? 这里要从面向对象编程说起,从面向过程编程到面向对象编程是软件设计的一大步,封装、继承...

联想高级Java研发面经+面试题:Spring+多线程+MySQL+设计模式

联想高级Java研发面经+面试题:Spring+多线程+MySQL+设计模式

上个礼拜,之前的一个同事突然联系我说他去面了联想的JAVA开发工程师,想分享一下面试经历和面试题。我当时就拍板说,好啊! 然后就整理了一下,写了这篇文章;和大家分享一下这次面试经验和面试题。 薪资还可以啊,年薪40W+啊!多少...

Java中常用的10种设计模式详解

1. 观察者模式 定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。 对于JDK或者Andorid中都有很多地方实现了观察者模式,比如XXXView.addXXXListenter , 当然...

学习 IOC 设计模式前必读:依赖注入的三种实现

学习 IOC 设计模式前必读:依赖注入的三种实现

学习 IOC 设计模式前必读:依赖注入的三种实现 学无止境,精益求精 十年河东十年河西,莫欺少年穷 呵呵,此篇博客转载自:http://www.cnblogs.com/liuhaorain/p/3747470.html 摘要 面向对象设计(OO...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。