Java - 基础 - 设计模式

[TOC]

设计模式 - Java篇

文章目录
[设计模式简介 \ 菜鸟教程 (runoob.com)](https://www.runoob.com/design-pattern/design-pattern-intro.html)
《设计模式(Java版)》—— 韩敬海
/

一、简述

​ 设计模式的存在相当于对代码的设计,你写不写的出代码可能跟它关系不大,但是要想写的优美,你就必须要懂,同时它也是地基一样的存在,我尽量以逻辑图形式呈现。

1.1 设计模式的分类

  1. 创建型 - 用于在创建对象的时候用其他更简洁灵活的方式,而不是直接new一个对象拿出来

    • 工厂模式(Factory Pattern)
    • 单例模式(Singleton Pattern)
    • 建造者模式(Builder Pattern)
    • 原型模式(Prototype Pattern)
  2. 结构型 - 类和对象的灵活性,一般是设计类与类之间的继承隔离关系

    • 适配器模式(Adapter Pattern)
    • 桥接模式(Bridge Pattern)
    • 过滤器模式(Filter、Criteria Pattern)
    • 组合模式(Composite Pattern)
    • 装饰器模式(Decorator Pattern)
    • 外观模式(Facade Pattern)
    • 享元模式(Flyweight Pattern)
    • 代理模式(Proxy Pattern)
  3. 行为型 - 对象之间的通信

    • 责任链模式(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)
  4. J2EE模式 - 表示层,我不太理解,后面看看

    • MVC 模式(MVC Pattern)
    • 业务代表模式(Business Delegate Pattern)
    • 组合实体模式(Composite Entity Pattern)
    • 数据访问对象模式(Data Access Object Pattern)
    • 前端控制器模式(Front Controller Pattern)
    • 拦截过滤器模式(Intercepting Filter Pattern)
    • 服务定位器模式(Service Locator Pattern)
    • 传输对象模式(Transfer Object Pattern)

2.2 设计原则

  • 开闭原则

    最核心的一句话,对扩展开放,对修改关闭,简单来说就是我们的Get/Set方法。

  • 里氏代换原则

    “任何基类可以出现的地方,子类一定可以出现”,其实就是多态

  • 依赖倒转原则

    接口编程,核心是面向接口编程,减少代码交互的耦合性。

  • 接口隔离原则

    调用者调这个接口的时候,防止调用到用不上的方法,每个应用都在最小接口上。

  • 迪米特法则

    实体之间尽量相互独立,不要过于与其他实体之间产生作用,可以利用VO。

  • 单一职责原则

    一个类应该就干它自己应该干的事情,类似于MVC模式下,持久化层负责数据库操作,控制层负责外部交互。

二、代码级设计模式

2.1 创建型

主要是围绕进行对象的实例化的方式,提供对对象的创建和管理,分为如下几种

  1. 单例模式
  2. 工厂模式
  3. 原型模式
  4. 建造者模式

单例模式 - 懒汉与饿汉

​ 意思是,就算在多个线程下,这个类也只会有一个实例,一般是会配合static关键字去做,下面聊聊两种代表性的单例模式。

  • 饿汉式 - 类加载时就初始化
  • 懒汉式 - 要用的时候才初始化

先通过代码去了解一下饿汉式

public class HungryDemo {
    /**
     * 类加载时就开始实例化
     */
    private static final HungryDemo hungryDemo = new HungryDemo();
    /**
     * 开闭原则获取实例
     *
     * @return
     */
    public static HungryDemo getHungry() {
        return hungryDemo;
    }
}

如果在懒汉式单例模式下,就会存在,两个线程都是进行获取实例操作,可能会拿到两个实例,这就违背了单例模式的原则,所以接下来看看很厉害的,双重检查锁+懒汉式单例

public class LazyDemo {
    private static LazyDemo lazyDemo;
    public static synchronized LazyDemo getLazyDemo() {
        // 第一关检查:静态锁
        if (lazyDemo == null) {
            synchronized (LazyDemo.class) {
                // 第二关检查:字节码同步锁
                if (lazyDemo == null) {
                    lazyDemo = new LazyDemo();
                }
            }
        }
        return lazyDemo;
    }
}

单例模式的优点及应用

  1. 减少内存开支,只有一个实例,可以用在一个对象需要频繁创建销毁的场景
  2. 避免资源的重复占用
  3. 全局化资源共享

工厂模式 - 简单/抽象/方法

分为了三种情况

  • 简单工厂
  • 抽象工厂
  • 工厂方法

具体先看看代码,再去理解它的真实意图,首先是简单工厂

Java - 基础 - 设计模式

工厂方法

工厂方法模式是简单工厂的升级版本,先看看逻辑图

Java - 基础 - 设计模式

代码就不写了,就是没有抽象类了,变成接口了,跟简单工厂差不多。

抽象工厂

抽象工厂模式又是工厂方法模式的升级版本,主要是为了解决多接口选择的问题,还是画逻辑图。

Java - 基础 - 设计模式

不写代码了好懒

原型模式

这个模式的好处是大对象的复制会比较快节省本地服务的性能,先上代码,首先是接口

public interface TopInter extends Cloneable {
    TopInter clone();
}

其次准备一个继承它的类,重写一下clone方法,其实这个就是深拷贝

public class DemoCre implements TopInter {
    @Override
    public TopInter clone() {
        try {
            return (TopInter) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}

一般对于大对象我们会这么做

    public void testMethod(TopInter topInter){
        TopInter topInter1 = topInter.clone();
    }

这个模式下构造函数不会执行:因为是从内存中完整复制而不是new,所以不会执行。

建造者模式/生成器模式

当一个类的构造函数参数超过4个,而且参数有些是可选的参数,考虑使用建造者模式,常规情况下,我们可能会这样涉及

  • 写好几行构造方法,利用重载,写一堆没用的代码
  • Get/Set传参

其实这么写不仅累,代码还又臭又长,且不美观,所以相应的解决方案也应运而生,大概逻辑如下

  1. 一个实体类,里面一些参数在new的时候有些必须存在,有些不用存在
  2. 一个Builder的静态内部类
  3. 对于必选参数,内部类就做成构造,可选参数,就做成set方法
  4. 最后,在这个静态内部类里,写一个返回new对象,传入this的实例
文章目录
秒懂设计模式之建造者模式(Builder pattern) - 知乎 (zhihu.com)
建造者模式的使用场景 - yujiwei - 博客园 (cnblogs.com)

算了,上面的说法我也不太理解,直到我看到了这么一句话:

“我曾经做过一个这样的模块,一个业务系统,数据产生在子系统A里面,同时子系统B需要A所产生的数据,但是需要经过"改造"和"加工",我们打个比方,假如A得到了一个关于交易的情况的一个Json类型的数据包,给B的时候也许有一些属性和字段不需要,或者A没有的而B却需要进行自主的构建,其实假如我们只有这一项的话,完全不需要使用到设计模式,但是假如A和B之间需要交换的数据种类非常的多,并且有可能C系统也会参与进来,甚至D系统也来了。。。这个时候我们不得不想一个统一的办法来管理它们,没错,就是建造者模式。”

我感觉整个人顿时茅塞顿开,下面写个代码示例

undetermined

2.2 结构型

主要围绕的内容是类之间的组装,就像一辆车的选配一样,也分如下

  1. 代理模式
  2. 装饰者模式
  3. 适配器模式
  4. 组合模式
  5. 外观模式
  6. 享元模式

代理模式

通过代理来控制核心对象的访问,非常典型的案例就是JDK动态代理及CgLib动态代理,下面上一个JDK动态代理的案例代码:

接口和实现类

package com.springAOP;
public interface UserDao {
    int add(int a, int b);
    String update(String id);
    void say();}

public class UserDaoImp implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println("add 方法正在执行");
        return a + b;
    }
    @Override
    public String update(String id) {
        System.out.println("update 方法正在执行");
        return id;
    }
    @Override
    public void say() {
        System.out.println("hello word say");
    }}

代理

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

public class JDKProxyRun {
    public static void main(String[] args) throws ClassNotFoundException {
        
        // 接口对象数组
        Class[] userDaoInter = {UserDao.class};
        
        // new一个实现类对象
        UserDaoImp userDao = new UserDaoImp();

        // 获得类加载器对象
        Class jdk_proxy_run2 = JDKProxyRun.class;
        ClassLoader loader2 = jdk_proxy_run2.getClassLoader();
        
        //  newProxyInstance 方法的三个参数,代理的类加载器、接口数组对象、接口的增强逻辑实现对象(继承InvocationHandler)
        UserDao dao =
                (UserDao)  Proxy.newProxyInstance (loader2,userDaoInter,new UserDaoProxy (userDao) );
        
        int result = dao.add(2, 3);
        dao.update("我要疯了");
        dao.say();
        System.out.println("result 的返回值:" + result);
    }
}
==============================================================================
class UserDaoProxy implements InvocationHandler {
    // 1 把代理对象的原对象进行传入
    private Object obj;

    UserDaoProxy(Object obj) {
        this.obj = obj;
    }
    // 增强的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 原方法执行前
        System.out.println("方法执行前的代码 _ " +
                "该方法的名字:" + method.getName()
                + "_传递的参数:" + Arrays.toString(args));

        // 增强的逻辑代码
        // 第一个是传参过来的方法对象,第二个是传递的参数
        Object res = method.invoke(obj, args);

        // 方法执行后
        System.out.println("方法之后执行的代码:" + obj);
        return res; }}

装饰者模式

此前有一位老师告诉我的一句非常形象的话:“就像一个毛胚房,自己DIY。”,在mybatis中也有使用到该设计模式,下面看看示例代码

车的接口

public interface CarBasic {

    /**
     * 启动点火
     */
    public void starter();
}

车的真正实现

public class AudiCar implements CarBasic {
    @Override
    public void starter() {
        System.out.println("启动!点火!");
    }
}

装饰器

public class Decorator implements CarBasic {
    private CarBasic carBasic;
    public Decorator(CarBasic carBasic) {
        this.carBasic = carBasic;
    }
    @Override
    public void starter() {
        carBasic.starter();
    }
}

真正的装饰工具

public class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(CarBasic carBasic) {
        super(carBasic);
    }
    @Override
    public void starter() {
        optional();
        super.starter();
        speed();
    }
    public void optional() {
        System.out.println("选装了霸气的氛围灯");
    }
    public void speed() {
        System.out.println("百秒加速1秒");
    }
}

最后测试一下

public class DemoApplication {
    public static void main(String[] args) {
        // 装饰前
        AudiCar audiCar = new AudiCar();
        audiCar.starter();
        System.out.println("////////////////");
        // 装饰后
        Decorator decorator = new Decorator(new ConcreteDecorator(audiCar));
        decorator.starter();
    }
}

你学会了吗

适配器模式

​ 大概场景描述就是,我是苹果手机,我有一条普通的耳机,但是这个耳机线插头不是苹果要用的那款,就需要一个转换头去转换才能插进来,从iPhone7开始都会赠送一个转换头,差不多就是这个意思。

文章目录
Java设计模式之《适配器模式》及应用场景 - 唯一浩哥 - 博客园 (cnblogs.com)

组合模式

一种树状的结构模式,很像一个公司下的组织架构

文章目录
简说设计模式——组合模式 - JAdam - 博客园 (cnblogs.com)

不想写了懒了

桥梁模式

大家解释下来的话语是,”让抽象和实现解耦“,但是对于我而言大概就是下面的这幅图

Java - 基础 - 设计模式

代码图大概就是这样

Java - 基础 - 设计模式

你学废了吗

外观模式

与桥梁模式相似,中间新增一个外观角色类,使得子系统与调用方解耦合,下面看看代码

Java - 基础 - 设计模式

虽然在解耦合这块做的好,但是改代码就变得麻烦了。

享元模式

池技术的重要实现,例如String常量池、数据库连接池、缓冲等,我们写了一个String字符串abc,下一次再写一个,那么这个时候引用的指针会指向同一块内存,不需要再新开辟一个内存空间了,减少了内存开销,享元模式的本质在于内存实例对象的共享

Java - 基础 - 设计模式

相当于是让我做一个池,下面先做一个简单的小模拟

class PersonFactory {
    /** 池集合 */
    public final static Map<String, Person> poolPersons = new HashMap<>();

    public static Person getPerson(String name) {
        if (poolPersons.containsKey(name)) {
            return poolPersons.get(name);
        } else {
            Person person = new Person(name, 19);
            poolPersons.put(name, person);
            return person;
        }
    }
}

public class ShareModel {
    public static void main(String[] args) {
        Person hello1 = PersonFactory.getPerson("hello");
        System.out.println(hello1);

        Person hello2 = PersonFactory.getPerson("hello");
        System.out.println(hello2);
    }
}

​ 上面的代码会有一个漏洞需要注意,如果此时我将其中一个对象进行修改,那会影响到其他使用这个对象的线程,所以我们需要线程在获取对象后防止对它进行修改。

2.3 行为型

不太好理解,主要关注的是对象与对象之间职责抽象化?对象之间的通信?,分为如下

  1. 责任链模式
  2. 命令模式
  3. 解释器模式
  4. 迭代器模式
  5. 中介者模式
  6. 备忘录模式
  7. 观察者模式
  8. 状态模式
  9. 空对象模式
  10. 策略模式
  11. 模板方法
  12. 访问者模式

责任链模式

方法的执行是一种链条式,在SpringMVC中就有非常好的例子,下面写个Demo

@Data
abstract class HandlerDemo {
    private HandlerDemo handlerDemo;
    /** 业务方法 */
    public abstract void talk();
}

class RunHandler extends HandlerDemo {
    @Override
    public void talk() {
        // 自己的业务处理
        System.out.println("自己的业务处理" );

        // 流转到下面的处理器
        if (super.getHandlerDemo() != null) {
            System.out.println("流转到下面的处理器___");
            super.getHandlerDemo().talk();
        } else {
            System.out.println("没有处理器了我是末尾");
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        HandlerDemo handler1 = new RunHandler();
        HandlerDemo handler2 = new RunHandler();
        handler2.setHandlerDemo(handler1);

        handler2.talk();
    }
}

最后的效果就是

自己的业务处理
流转到下面的处理器___
自己的业务处理
没有处理器了我是末尾

命令模式

长得很像代理模式的味道

Java - 基础 - 设计模式

类膨胀的太多了,最好跟其他设计模式搭配用

解释器模式

用来作语句解释,从网上的案例上看,大多数是用来做数学运算和SQL的底层处理,日常基本用不上

文章目录
expression4J 数学公式计算_一路向前-CSDN博客
Java表达式引擎fel/groovy/expression4j/java脚本引擎的性能对比【原创】_java_码云网 (findsrc.com)

迭代器模式

例如Java中的Collection,顺序访问一个容器内的元素,一般我们都是用JDK里面的API,自己写一个新的迭代器应该没有人这么做

文章目录
迭代器模式 - 简书 (jianshu.com)

好懒

中介者模式

主要是解决多个类之间的通信问题,通过在中间增加一个中介者类,将通信的转换处理交给中介者类,降低通信的难度及复杂度

Java - 基础 - 设计模式

中介者的核心就是将原来相互依赖成网状关系的类分离成星型结构

其实很像MVC模式,以及两个客户端通过Netty服务相互通讯,这个服务端在中间也是变相的一个中介人,不写代码了。

备忘录模式

其实也是一种备份、快照的模式,下面写了一个非常简易版的利用Clone

@Data
@AllArgsConstructor
class Person implements Cloneable {
    private String name;
    private Integer age;
    private Double version;

    @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.setVersion(person.getVersion() + 1);
        return person;
    }
}

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person test01 = new Person("test01", 18, 1.0);

        Person clone1 = test01.clone();
        clone1.setName("clone1");

        Person clone2 = clone1.clone();
        clone2.setName("clone2");

        System.out.println(test01);
        System.out.println(clone1);
        System.out.println(clone2);

    }
}

当然可以使用集合将每一个版本进入存入,当然后面备份数量膨胀的时候也要考虑到生命周期的管理。

观察者模式

也叫发布/订阅模式,在很多中间件上都有实际案例,例如RabbitMQ、Redis等。

Java - 基础 - 设计模式

实在是懒得写代码。

状态模式

一个对象在它内部发生改变的时候改变它自身的行为,例如staut=1时切换执行A,staut=2时切换执行B,下面举个例子

@Data
@AllArgsConstructor
class GotoClass {
    private String time;
    @Override
    public String toString() {
        switch (this.time) {
            case "11.00":
                return "语文课";
            case "12.00":
                return "数学课";
            case "13.00":
                return "英语课";
            default:
                return "放学了";
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        GotoClass gotoClass = new GotoClass("11.00");
        System.out.println(gotoClass);
        gotoClass.setTime("12.00");
        System.out.println(gotoClass);
        gotoClass.setTime("13.00");
        System.out.println(gotoClass);
    }
}

或者还有另外一种实现方式

@Data
@AllArgsConstructor
class Course {
    private String name;
    public void getCourse() {
        System.out.println("现在上:" + this.name);
    }
}
@Data
class GotoClass  {
    private Course course;
    public void say() {
        course.getCourse();
    }
}

public class Demo {
    public static void main(String[] args) {
        Course course1 = new Course("语文");
        Course course2 = new Course("数学");
        Course course3 = new Course("英语");

        GotoClass gotoClass = new GotoClass();
        gotoClass.setCourse(course1);
        gotoClass.say();
        gotoClass.setCourse(course2);
        gotoClass.say();
        gotoClass.setCourse(course3);
        gotoClass.say();
    }
}

都差不多,上面我没用多态去写,但是实际上我们还是需要用面向对象的编程思维去实现,大概就是这样。

策略模式

与之前的结构型的设计模式还是蛮相似的,比较简单

interface Shape{
    public void run();
}

class ShapeImplA implements Shape{
    @Override
    public void run() {
        System.out.println("ShapeImplA");
    }
}

class ShapeImplB implements Shape{
    @Override
    public void run() {
        System.out.println("ShapeImplB");
    }
}

class Composite{
    private Shape shape;
    public Composite(Shape shape) {
        this.shape = shape;
    }
    public void execute(){
        this.shape.run();
    }
}

public class Demo {
    public static void main(String[] args) {
        Shape shapeA = new ShapeImplA();
        new Composite(shapeA).execute();

        Shape shapeB = new ShapeImplB();
        new Composite(shapeB).execute();
    }
}

主要是为了解决这些不同的子类所执行的方法可以独立*切换

模板方法模式

先看看代码的实现

abstract class Entitle {
    public abstract void say();

    public void templateMethod() {
        this.say();
    }
}

class EntitleA extends Entitle {
    @Override
    public void say() {
        System.out.println("EntitleA");
    }
}

public class Demo {
    public static void main(String[] args) {
        Entitle entitle = new EntitleA();
        entitle.say();
    }
}

​ 代码不多只有这么这么几行,变化的地方在子类的方法中,不变的公共的地方放在抽象类,这样在后期的业务扩展上也比较容易修改,符合开闭原则。

访问者模式

1

undetermined

1

2.4 混合设计模式

就是把上面的那些模式组合起来,形成更优雅的设计

  1. 命令链模式
  2. 工厂策略模式
  3. 观察中介者模式
  4. 规格模式

命令链模式

1

工厂策略模式

1

观察中介者模式

1

规格模式

1

三、应用级设计模式

3.1 J2EE模式

文章目录
j2ee_百度百科 (baidu.com)

是一个分布式应用程序的开发规范,全称:Java 2 Platform Enterprise Edition

MVC 模式

将MVC分开解读

  • M - 模型层
  • V - 视图层
  • C - 控制层

日常的Java开发中经常有涉及到,经典的例子就是SpringMVC,拆分为

  1. 请求控制层 - Controller
  2. 业务员接口层 - Service
  3. 具体业务执行实现 - ServiceImpl
  4. 数据库操作持久化层 - Mapper
文章目录
SpringMVC的基本使用+原理,一篇囊括__-CSDN博客

业务代表模式

1

组合实体模式

1

数据访问对象模式

1

前端控制器模式

1

拦截过滤器模式

1

服务定位器模式

1

传输对象模式

1

3.2 内容待定

111111

万事如意,阖家安康

上一篇:Clion配置远程CUDA调试环境


下一篇:unity2020学习(一)界面了解