Java进阶(二):Java设计模式

目录

设计模式

一.建模语言

二.类之间的关系

1.依赖关系

2.关联关系

3.聚合关系

4.组合关系

5.继承关系

6.实现关系

三.面向对象设计原则

单一职责原则

开闭原则

里氏替换原则

依赖倒置

接口隔离原则

迪米特原则

组合/聚合(关联关系)复用原则

四.23种设计模式

五.单例模式(只创建一个对象)

六.工厂模式

简单工厂模式(一个工厂对应多类产品)

工厂方法模式(一个工厂对应一类产品)

抽象工厂(一个工厂对应多类商家工厂)

七.原型模式

八.代理模式

九.模板方法模式

十.策略模式


设计模式

设计模式产生背景

设计模式概念首先起源于建筑领域,1990在软件领域也诞生设计模式概念。

直到 1995 年,艾瑞克·伽马(ErichGamma)、理査德·海尔姆(Richard Helm)、拉尔夫·约翰森(Ralph Johnson)、约翰·威利斯迪斯(JohnVlissides),在《设计模式:可复用面向对象软件的基础》中收纳总结了23种设计模式。

什么是设计模式?

在长期编程的过程中,针对某一类问题经过反复的优化,最终总结出一个固定的解决方案,这些方案经过反复的使用,具有普遍性。

为什么要学习设计模式?

①学习设计模式就是学习好的编程思想,学习前辈们的经验。

②可以提高程序员的思维能力、编程能力和设计能力。

③使程序设计更加标准化、使软件开发效率大大提高。

④使设计的代码可重用性高、可扩展性提高。

⑤能够更好的去理解源码架构。


一.建模语言

统一建模语言(Unified Modeling Language,UML),是一套软件设计和分析的语言工具

用图形化的方式,记录表示类与类类与接口接口与接口之间的关系, 一般把图形化方式也称为UML类图.

类图中两个基本的要素:

1.类 :是对具有相同属性和行为的一组对象的抽象描述

2.接口:是一种特殊的类,它具有类的结构但不可被实例化,只可以被子类实现。


二.类之间的关系

1.依赖关系

在一个类中的方法,把另一个类作为参数进行使用 ,具有临时性,方法执行结束后,依赖关系就不存在了。

一般把xxx类用到了xxx类 ,这种关系,称为依赖关系,也称为 use-a关系。例如:下图的人与手机的关系,人类中的方法call()中的参数MobilePhone mp就是作为依赖关系。方法结束,依赖关系结束。用虚线箭头表示。

2.关联关系

是一种has-a 的关系**,xxx 有 xxx**

在一个类中,把另一个当做自己的成员。用实心三角实线箭头表示。

有单向关联,双向关联,自关联,一对一关联,一对多关联。

关联关系根据强弱又分为:聚合关系和组合关系。

3.聚合关系

聚合关系也是一种关联关系,是强关联关系 ,是一种整体和部分的关系

学校包含老师, 即使学校不存在了, 老师可以依然独立的存在。

在 UML 类图中,聚合关系可以用带空心菱形的实线 来表示,菱形指向整体

4.组合关系

聚合关系也是一种关联关系, 是一种整体和部分 的关系, 是一种更强烈的关联关系。

头和嘴关系,头如果不在了,嘴也会跟着销毁。

在 UML 类图中,组合关系用带实心菱形的实线 来表示,菱形指向整体

5.继承关系

继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的

关系,是一种继承关系,是is-a 的关系。

在 UML 类图中,继承关系用带空心三角箭头的实线 来表示,箭头从子类指向父类

6.实现关系

实现关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接

口中所声明的所有的抽象操作。

在 UML 类图中,实现关系使用带空心三角箭头的虚线 来表示,箭头从实现类指向接口


三.面向对象设计原则

单一职责原则

一个类只负责某一个具体功能,细化类的粒度。比如:Person类只负责关于人的信息,Address类只负责关于地址的信息。


开闭原则

对修改关闭,对扩展开放(不修改,多使用多态的思想)

尽可能的在扩展功能时,不要修改已有的代码,尽可能扩展一个新的类来实现新功能。


里氏替换原则

继承优势: 提高代码复用性,子类继承父类的功能,提高代码的扩展性,子类还可以扩展自己的功能,不影响其他类,重写父类方法。

继承劣势: 继承使得类的体系结构变得复杂了

里氏替换:首先时由里斯科夫女士提出的。

其次是关于继承使用的,当子类继承了父类后, 在使用时,用子类替换父类后,要确保父类中的功能不受影响。(子类重写父类方法时,不得改变改变方法的结果)

主要的思想: 就保证程序的稳定性。


依赖倒置

面向抽象编程,不要面向具体的实现编程。

具体实现应该依赖抽象层(使用多态,抽象层用来表示,定义--> 具体的由子实现类实现)


接口隔离原则

不要把所有的功能都定义到一个总的接口中,应该把不同的种类的功能,定义在不同的接口中,让实现类,根据自己的需要去灵活的选择。(不同功能,不同接口)


迪米特原则

只跟朋友联系,不跟"陌生人"说话。

在程序间相互调用时,如果两个类直接没有直接联系,但是想相互调用,可以通过第三方进行转发调用。(两个类没有直接联系,通过第三方将两者联系,如:明星与粉丝,通过经纪人交流

降低模块之间的耦合度。


组合/聚合(关联关系)复用原则

继承 使得类的体系变得复杂, 如果我们只是想使用某个类中的方法时,也可以优先选择关联关系/ 依赖关系 降低类与类之间的耦合度


四.23种设计模式

设计模式分为3大类
根据模式是用来完成什么工作来划分,这种方式可分为创建型模式、结构型模式和行为型模式 3 种。
创建型模式 :用于描述"怎样创建对象",它的主要特点是"将对象的创建与使用分离"。提供了
、原型、 工厂方法 、抽象工厂、建造者5 种创建型模式。
结构型模式 :用于描述如何将类或对象按某种布局组成更大的结构,提供了代理、适配器、桥接、
装饰、外观、享元、组合 7 种结构型模式。
行为型模式 :用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及
怎样分配职责。提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问
者、备忘录、解释器 11 种行为型模式。


五.单例模式(只创建一个对象)

在一个项目中,如何确保一个类始终只有一个对象

特点:

  1. 单例类只有一个实例对象;(虽然多次创建,但只能产生一个)
  2. 该单例对象必须由单例类自行创建;(在单例类内部,在第一次使用时才创建)
  3. 单例类对外提供一个访问该单例的全局访问点;(需要提供一个公共的方法)

单例模式通常两种实现

①饿汉式单例 :单例模式中的饿汉式(急切式单例) 在加载此类时,就已经将唯一的一个对象创建出来。

好处:不会存在线程安全问题。

不足: 在类加载时,就会创建单例对象,有可能一段时间内还用不到它。

java 复制代码
public class MyWindow {
    //在内部自己创建的一个单例对象
    private static MyWindow myWindow = new MyWindow();

    private MyWindow() {
    }

    /*
       对外提供这唯一的对象
     */
    public static MyWindow getMyWindow() {
        return myWindow;
    }
}

**②懒汉式单例:**在类加载时,并没有创建单例对象,在第一次获取单例对象时,才去创建了单例对象。

好处:类加载时先不创建,在第一次使用获取时才会创建

不足: 会出现线程安全问题加锁解决(synchronized锁)

※ 双重检索 + volatile(可见性,避免重排序)

例如:A a = new A();

创建对象这一条语句编译为指令时,可以分为三个指令顺序:

1. new 申请空间

2. 调用构造方法初始化对象

3. 把对象地址赋给引用变量

如果按照这个正常的顺序执行,是没有问题的,但是执行时,如果2,3条指令顺序发生变化, 导致把没有初始化完成的对象地址返回了,拿去使用了,这么做会出问题,因为对象没有初始化完成。所有需要使用volatile关键修饰单例成员变量,确保对其赋值时,指令不重新排序。

java 复制代码
public class MyWindow {

    private volatile static MyWindow myWindow;

    public static MyWindow getMyWindow() {

        //第一重检索,隔绝后面大量的线程进入
        if (myWindow == null) {

            //加锁,只能一个一个进入
            synchronized (MyWindow.class) {

                //第二重检索,隔绝前面少量的线程进入
                if (myWindow == null) {
                    myWindow = new MyWindow();
                }
            }
        }
        return myWindow;
    }
}

六.工厂模式

解决的就是在项目将创建对象和使用对象分离的问题,(结合Spring),如何更好的组织类与类之间的关系。

简单工厂模式(一个工厂对应多类产品)

简单工厂并不是 一种设计模式,违背了开闭原则,主要是引出工厂方法抽象工厂模式

涉及的角色:

工厂角色: 根据我们的需求,创建对应的对象。

抽象产品: 具体产品的抽象,具体产品实现 / 继承抽象产品,可以使用上层的抽象父类,表示任意的子类对象。

具体产品: 具体的对象。

优点: 创建对象和使用对象分离了。

缺点: 只能创建实现了同一个父类 / 接口的子类对象, 扩展新的类型,需要**修改工厂,**违背了开闭原则。

适合简单的,子类较少的场景。

工厂方法模式(一个工厂对应一类产品)

由于简单工厂中,一个工厂,可以造同一类型的所有具体产品,导致简单工厂比较复杂,扩展一个新类型时,需要修改工厂代码。

工厂方法模式,为工厂也进行抽象,并且为同类型每个具体产品都创建了一个具体的工厂。

每一个工厂负责创建一个具体的产品 (类型)对象,这样扩展一个新的产品,与之对应就要扩展一个产品工厂,就不需要修改工厂,遵守了开闭原则,单一职责原则。

好处: 遵守了开闭原则。

不足: 类的数量增多了。

抽象工厂(一个工厂对应多类商家工厂)

如:一个工厂可分为小米工厂,华为工厂。小米工厂可造汽车,手机。华为工厂可造汽车,手机。

工厂方法模式 ,是按照产品类型进行分类的,一类产品,对应一类工厂, 不同类型产品之间,相互隔离的。例如: 华为和小米,既要汽车,又要造手机,都是属于同一家的产品,但是工厂方法这种设计,同一个公司产品与产品之间没有联系。
抽象工厂模式 对工厂重新进行分类,以公司为单位进行工厂的抽象(提取),一个工厂内,可以创建不同的产品,这样我们就可以创建出像华为工厂,小米工厂这样的具体工厂,一个工厂内,可以创建不同公司的各种产品。


七.原型模式

在某些场景下,为(解决的问题:)避免自己手动的new对象,我们可以使用对象克隆方式,创建并返回一个新的对象。这种克隆新对象的效率比我们自己new的效率要高。

对象克隆实现方式有两种:

1.实现Cloneable接口,重写clone()

2.使用对象序列化 反序列化重新生成对象

注意深克隆和浅克隆问题。


八.代理模式

早在spring aop思想中,已经用到了代理思想。在不修改原来代码的前提下,为我们方法添加额外的功能。通过代理对象帮助我们进行调用。

有些时候,目标对象(汽车厂)不想或不能直接与客户打交道,通过代理对象进行访问,代理对象可以保护目标对象,对目标对象功能进行扩展,降低了模块之间的耦合度。

涉及到三个主题:

抽象主题: (抽取的功能,让目标对象进行实现,以及代理对象进行实现)

具体主题: 真正要实现功能的类。

代理对象

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

1.静态代理

创建一个代理类,代理实现与具体对象实现相同的接口 / 抽象类,重写抽象方法。还有一个成员变量,可以用接收具体的主题,在代理类中重写的抽象方法中调用真实主题方法,这样就可以在调用之前和之后添加额外的功能。

不足: 一个代理对象,只能代理一个接口类型的对象,不灵活。

2.动态代理

只需要写一次代理类,就可以通过反射机制动态获得类的信息,可以为任何类提供代理功能。

动态代理实现分为两种:

①jdk代理

jdk代理实现是通过反射机制实现的, 目标类必须要实现一个接口,通过接口动态获得目标类中的信息。

②cglib代理

是spring中提供的一种代理技术,目标类可以不实现任何接口 ,采用字节码生成子类的方式,对方法进行拦截,实现机制不同。

注意:cglib不能代理final修饰的类以及fnal和static修饰的方法。

目前spring中两种动态代理都支持,如果目标类没有实现接口,默认使用cglib代理,如果目标类有实现接口,采用jdk代理。


九.模板方法模式

模版方法模式,是在一个类中,定义好一个算法骨架,设定好实现步骤,把一些公共的通用的在父类中实现,然后将一些不确定的实现在具体的子类中实现。

例如,去银行办理业务一般要经过以下 4 个流程:取号、排队、办理具体业务、对银行
工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样
的,可以在父类中实现,但是办理具体业务 却因人而异,它可能是存款、取款或者转账等,
可以延迟到子类中实现。
结构:

抽象类: 负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

模版方法: 定义好执行顺序的算法骨架,确定好执行流程顺序。

抽象方法: 不确定的功能,定义为抽象的,交给子类实现。

具体方法: 都一样的公共的通用的方法,在抽象父类中实现。

具体子类: 实现抽象类中的抽象方法的具体类,有不同的实现方式,就可以用多个子类。

new 具体子类对象,用具体子类对象调用模版方法,把父类中具体方法与自己实现的抽象方法一起执行。

将变化部分定义为抽象的,让子类去扩展实现,满足开闭原则,适合流程相对比较固定的,其中有变化的场景。

十.策略模式

将不同的实现算法进行封装,将功能的实现与使用相分离。(多选一策略)在使用时,可以用不同的策略实现类进行替换。重点用到的知识点:继承,多态。

结构

抽象类:抽象方法

具体实现类(多个实现类实现抽象类的抽象方法,在多个实现类中多选一

环境类(使用者)

**感谢你的阅读与关注,如有问题欢迎探讨!**💓

相关推荐
简 洁 冬冬21 分钟前
Java中的Servlet
java·开发语言·servlet
fly spider31 分钟前
多线程-线程池的使用
java·面试·线程池·多线程·juc
组合缺一1 小时前
Solon Cloud Gateway 开发:导引
java·gateway·reactor·solon·响应式
matlabgoodboy1 小时前
留学生scratch计算机haskell函数ocaml编程ruby语言prolog作业VB
开发语言·后端·ruby
玖石书2 小时前
【高内聚】设计模式是如何让软件更好做到高内聚的?
设计模式
R三哥哥啊2 小时前
【Qt】06-对话框
开发语言·qt·microsoft·qt5
hefaxiang2 小时前
【C++】类和对象(一)
开发语言·c++
陈平安Java and C2 小时前
二叉树介绍
java
S-X-S2 小时前
sunrays-framework配置重构
java·重构
gentle coder2 小时前
JVM_类的加载、链接、初始化、卸载、主动使用、被动使用
java·jvm