- 3.结构型模式(7种)
- 3.1.代理模式
- 3.2.适配器模式
- 3.3.装饰者模式
- 3.4.桥接模式
- 3.4.1.概述
- 3.4.2.结构
- 3.4.3.实现
- 3.4.4.使用场景
- 3.5.外观模式
- 3.5.1.概述
- 3.5.2.结构
- 3.5.3.实现
- 3.5.4.使用场景
- 3.5.5.源码解析
- 3.6.组合模式
- 3.6.1.概述
- 3.6.2.结构
- 3.6.3.实现
- 3.6.4.分类
- 3.6.5.优点
- 3.7.享元模式
- 3.7.1.概述
- 3.7.2.结构
- 3.7.3.实现
- 3.7.4.优缺点
- 3.7.5.JDK源码解析——Integer类
本文章笔记整理来自黑马视频https://www.bilibili.com/video/BV1Np4y1z7BU,相关资料可在评论区获取。
详解23种设计模式(基于Java)—— 设计模式相关内容介绍(一 / 五) 详解23种设计模式(基于Java)—— 创建者模式(二 / 五) 详解23种设计模式(基于Java)—— 行为型模式(四 / 五) 详解23种设计模式(基于Java)—— 综合练习之自定义Spring IoC(五 / 五)
3.结构型模式(7种)结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
3.1.代理模式有关代理模式的具体知识可以参考Java 设计模式——代理模式这篇文章。
3.2.适配器模式有关适配器模式的具体知识可以参考 Java 设计模式——适配器模式这篇文章。
3.3.装饰者模式有关装饰者模式的具体知识可以参考Java 设计模式——装饰者模式
3.4.桥接模式 3.4.1.概述(1)现在有一个需求,需要创建不同的图形,并且每个图形都有可能会有不同的颜色。我们可以利用继承的方式来设计类的关系: 我们可以发现有很多的类,假如我们再增加一个形状或再增加一种颜色,就需要创建更多的类。试想,在一个有多种可能会变化的维度的系统中,用继承方式会造成类爆炸,扩展起来不灵活。每次在一个维度上新增一个具体实现都要增加多个子类。为了更加灵活的设计系统,我们此时可以考虑使用桥接模式。 (2)桥接模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
桥接(Bridge)模式包含以下主要角色: (1)抽象化(Abstraction)角色: 定义抽象类,并包含一个对实现化对象的引用。 (2)扩展抽象化(Refined Abstraction)角色: 是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。 (3)实现化(Implementor)角色: 定义实现化角色的接口,供扩展抽象化角色调用。 (4)具体实现化(Concrete Implementor)角色: 给出实现化角色接口的具体实现。
3.4.3.实现【例】视频播放器 需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式。类图如下: 代码如下: VideoFile.java(实现化角色)
package com.itheima.patterns.structuralpattern.bridge;
//视频文件(实现化角色)
public interface VideoFile {
//解码功能
void decode(String fileName);
}
AviFile.java(具体实现化角色)
package com.itheima.patterns.structuralpattern.bridge;
//avi视频文件(具体的是实现化角色)
public class AviFile implements VideoFile{
@Override
public void decode(String fileName) {
System.out.println("avi视频文件:" + fileName);
}
}
RmvbFile.java(具体实现化角色)
package com.itheima.patterns.structuralpattern.bridge;
//rmvb视频文件(具体的是实现化角色)
public class RmvbFile implements VideoFile{
@Override
public void decode(String fileName) {
System.out.println("rmvb视频文件:" + fileName);
}
}
OperatingSystem.java(抽象化角色)
package com.itheima.patterns.structuralpattern.bridge;
//抽象的操作系统类(抽象化角色)
public abstract class OperatingSystem {
//声明VideoFile变量
protected VideoFile videoFile;
public OperatingSystem(VideoFile videoFile) {
this.videoFile = videoFile;
}
public abstract void play(String fileNamw);
}
Mac.java(扩展抽象化角色)
package com.itheima.patterns.structuralpattern.bridge;
//Mac操作系统(扩展抽象化角色)
public class Mac extends OperatingSystem{
public Mac(VideoFile videoFile) {
super(videoFile);
}
@Override
public void play(String fileName) {
videoFile.decode(fileName);
}
}
Windows.java(扩展抽象化角色)
package com.itheima.patterns.structuralpattern.bridge;
//windows操作系统(扩展抽象化角色)
public class Windows extends OperatingSystem {
public Windows(VideoFile videoFile) {
super(videoFile);
}
@Override
public void play(String fileName) {
videoFile.decode(fileName);
}
}
Client.java
package com.itheima.patterns.structuralpattern.bridge;
public class Client {
public static void main(String[] args) {
//创建mac系统对象
OperatingSystem system = new Mac(new AviFile());
//使用操作系统播放视频文件
system.play("战狼2");
}
}
从上面的代码可以看出桥接模式的一些优点: ① 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。如:如果现在还有一种视频文件类型wmv,我们只需要再定义一个类实现VideoFile接口即可,其他类不需要发生变化。 ② 实现细节对客户透明。
3.4.4.使用场景(1)当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。 (2)当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。 (3)当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
3.5.外观模式 3.5.1.概述(1)有些人可能炒过股票,但其实大部分人都不太懂,这种没有足够了解证券知识的情况下做股票是很容易亏钱的,刚开始炒股肯定都会想,如果有个懂行的帮帮手就好,其实基金就是个好帮手,支付宝里就有许多的基金,它将投资者分散的资金集中起来,交由专业的经理人进行管理,投资于股票、债券、外汇等领域,而基金投资的收益归持有者所有,管理机构收取一定比例的托管管理费用。 (2)外观模式:又名门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。 外(Facade)模式是“迪米特法则”的典型应用。
外观(Facade)模式包含以下主要角色: (1)外观(Facade)角色: 为多个子系统对外提供一个共同的接口。 (2)子系统(Sub System)角色: 实现系统的部分功能,客户可以通过外观角色访问它。
3.5.3.实现【例】智能家电控制 小明的爷爷已经60岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;操作起来都比较麻烦。所以小明给爷爷买了智能音箱,可以通过语音直接控制这些智能家电的开启和关闭。类图如下: 具体实现代码如下: Light.java(子系统角色)
package com.itheima.patterns.structuralpattern.facade;
public class Light {
public void on(){
System.out.println("打开电灯......");
}
public void off(){
System.out.println("关闭电灯......");
}
}
TV.java(子系统角色)
package com.itheima.patterns.structuralpattern.facade;
public class TV {
public void on(){
System.out.println("打开电视机......");
}
public void off(){
System.out.println("关闭电视机......");
}
}
AirCondition.java(子系统角色)
package com.itheima.patterns.structuralpattern.facade;
public class AirCondition {
public void on(){
System.out.println("打开空调......");
}
public void off(){
System.out.println("关闭空调......");
}
}
SmartAppliancesFacade.java(外观角色)
package com.itheima.patterns.structuralpattern.facade;
//外观类,用户主要和该类对象进行交互
public class SmartAppliancesFacade {
//聚合电灯对象、电视机对象、空调对象
private Light light;
private TV tv;
private AirCondition airCondition;
public SmartAppliancesFacade(){
light = new Light();
tv = new TV();
airCondition = new AirCondition();
}
//通过语言控制
public void say(String message){
if(message.contains("打开")){
on();
}else if(message.contains("关闭")){
off();
}else{
System.out.println("我还听不懂你说的!!!");
}
}
//一键打开功能
public void on(){
light.on();
tv.on();
airCondition.on();
}
//一键关闭功能
public void off(){
light.off();;
tv.off();
airCondition.off();
}
}
Client.java
package com.itheima.patterns.structuralpattern.facade;
public class Client {
public static void main(String[] args) {
//创建智能音箱对象
SmartAppliancesFacade facade = new SmartAppliancesFacade();
//控制家电
facade.on();
System.out.println("==========");
//关闭家电
facade.off();
}
}
3.5.4.使用场景
(1)对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。 (2)当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。 (3)当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。
3.5.5.源码解析(1)使用tomcat作为web容器时,接收浏览器发送过来的请求,tomcat会将请求信息封装成ServletRequest对象,如下图①处对象。但是仔细想想ServletRequest是一个接口,它还有一个 子接口HttpServletRequest,而我们知道该request对象肯定是一个HttpServletRequest对象 的子实现类对象,到底是哪个类的对象呢?可以通过输出request对象,我们就会发现是一个名为RequestFacade的类的对象。
(2)RequestFacade类就使用了外观模式。先看结构图:
(3)为什么在此处使用外观模式呢? 定义 RequestFacade 类,分别实现 ServletRequest ,同时定义私有成员变量 Request ,并且方法的实现调用 Request 的实现。然后,将 RequestFacade上转为 ServletRequest 传给servlet 的 service 方法,这样即使在 servlet 中被下转为 RequestFacade ,也不能访问私有成员变量对象中的方法。既用了 Request ,又能防止其中方法被不合理的访问。
(1)大家对于上面这个图片肯定非常熟悉,上图我们可以看做是一个文件系统,对于这样的结构我们称之为树形结构。在树形结构中可以通过调用某个方法来遍历整个树,当我们找到某个叶子节点后,就可以对叶子节点进行相关的操作。可以将这颗树理解成一个大的容器,容器里面包含很多的成员对象,这些成员对象即可是容器对象也可以是叶子对象。但是由于容器对象和叶子对象在功能上面的区别,使得我们在使用的过程中必须要区分容器对象和叶子对象,但是这样就会给客户带来不必要的麻烦,作为客户而已,它始终希望能够一致的对待容器对象和叶子对象。 (2)组合模式:又名部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。 (3)使用场景:组合模式正是应树形结构而生,所以组合模式的使用场景就是出现树形结构的地方。比如:文件目录显示,多级目录呈现等树形结构数据的操作。
组合模式主要包含以下三种角色: (1)抽象根节点(Component): 定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。 (2)树枝节点(Composite): 定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。 (3)叶子节点(Leaf): 叶子节点对象,其下再无分支,是系统层次遍历的最小单位。
3.6.3.实现【例】软件菜单 如下图,我们在访问别的一些管理系统时,经常可以看到类似的菜单。一个菜单可以包含菜单项(菜单项是指不再包含其他内容的菜单条目),也可以包含带有其他菜单项的菜单,因此使用组合模式描述菜单就很恰当,我们的需求是针对一个菜单,打印出其包含的所有菜单以及菜单项的名称。 类图如下:
具体代码如下: MenuComponent.java(抽象根节点)
package com.itheima.patterns.structuralpattern.composite;
//不管是菜单还是菜单项,都应该继承自统一的接口,这里暂时将这个统一的接口称为菜单组件。
//菜单组件,属于抽象根节点
public abstract class MenuComponent {
//菜单组件的名称
protected String name;
//菜单组件的层级
protected int level;
//添加子菜单
public void add(MenuComponent menuComponent){
throw new UnsupportedOperationException();
}
//移除子菜单
public void remove(MenuComponent menuComponent){
throw new UnsupportedOperationException();
}
//获取指定的子菜单
public MenuComponent getChild(int index){
throw new UnsupportedOperationException();
}
//获取菜单或者菜单项的名称
public String getName(){
return name;
}
//打印菜名称的方法(包含子菜单和子菜单项)
public abstract void print();
}
这里的MenuComponent定义为抽象类,因为有一些共有的属性和行为要在该类中实现,Menu和MenuItem类就可以只覆盖自己感兴趣的方法,而不用搭理不需要或者不感兴趣的方法,举例来说,Menu类可以包含子菜单,因此需要覆盖add()、remove()、getChild()方法,但是MenuItem就不应该有这些方法。这里给出的默认实现是抛出异常,也可以根据自己的需要改写默认实现。 Menu.java(树枝节点)
package com.itheima.patterns.structuralpattern.composite;
import java.util.ArrayList;
import java.util.List;
//菜单类,属于树枝节点
public class Menu extends MenuComponent{
//菜单可以有多个子菜单或者子菜单项
private List menuComponentList = new ArrayList();
//构造方法
public Menu(String name,int level){
this.name = name;
this.level = level;
}
@Override
public void add(MenuComponent menuComponent) {
menuComponentList.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
menuComponentList.remove(menuComponent);
}
@Override
public MenuComponent getChild(int index) {
return menuComponentList.get(index);
}
@Override
public void print() {
//打印菜单名称
for (int i = 0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?