当前位置: 首页 > news >正文

设计模式の命令访问者迭代器模式

文章目录

  • 前言
  • 一、命令模式
  • 二、访问者模式
  • 三、迭代器模式


前言

  本篇是关于设计模式中命令模式、访问者模式、以及迭代器模式的学习笔记。


一、命令模式

  命令模式是一种行为型设计模式,其核心目的在于将命令的发送者和接受者解耦,提供一个中间层对命令进行统一的管理,请求的发送者不需要知道接收者的具体实现,而只需依赖一个抽象的命令接口。
  命令模式通常包括以下的角色:

  • 抽象/接口命令层:定义了执行命令的抽象方法,例如执行,撤销。
  • 具体命令类:继承/实现了命令层,对命令的具体内容做一个描述。
  • 接收者:实际执行操作的对象,包含与请求相关的逻辑。
  • 调用者:调用命令对象以触发请求的执行。
  • 客户端:创建具体的命令对象,并将其绑定到调用者上。

  举一个生活中的案例,假设对于家电,想要通过一个万能遥控器去控制某个家电的开/关,而不需要每个家电都配一个遥控器,则可以将开,关设为具体命令类:

/*** 命令抽象类*/
public interface Command {/*** 执行命令*/void execute();/*** 回滚操作*/void undo();
}

  以对灯的开关操作为例,开灯的命令类:

/*** 开灯*/
public class LightOnCommand implements Command{private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}@Overridepublic void undo() {light.off();}
}

  关灯的命令类:

/*** 关灯*/
public class LightOffCommand implements Command{private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.off();}@Overridepublic void undo() {light.on();}
}

  具体的电器,有开关的操作。

public class Light {public void on(){System.out.println("把灯打开");}public void off(){System.out.println("关灯了");}
}

  以及一个空的命令类

public class NoCommand implements Command{/*** 执行命令*/@Overridepublic void execute() {}/*** 回滚操作*/@Overridepublic void undo() {}
}

  命令的统一管理类,主要作用:

  • 对命令组进行初始化。
  • 设置操控某一个对象的具体命令。
  • 执行具体的命令。
  • 撤销命令。
public class RemoteController {//存放各种设备的开命令Command[] onCommands;//存放各种设备的关命令Command[] offCommands;//撤销的命令Command unCommand;/*** 初始化空命令*/public RemoteController(int number) {onCommands = new Command[number];offCommands = new Command[number];for (int i = 0; i < number; i++) {onCommands[i] = new NoCommand();offCommands[i] = new NoCommand();}}/*** 设置具体的命令** @param no         设备组编号* @param onCommand  具体设备组设备的开命令* @param offCommand 具体设备组设备的关命令*/public void setCommand(int no, Command onCommand, Command offCommand) {onCommands[no] = onCommand;offCommands[no] = offCommand;}/*** 按下某个设备的开按钮** @param no 设备组编号*/public void onButtonWasPushed(int no) {onCommands[no].execute();//记录本次操作,便于撤销unCommand = onCommands[no];}/*** 按下某个设备的关按钮** @param no 设备组编号*/public void offButtonWasPushed(int no) {offCommands[no].execute();//记录本次操作,便于撤销unCommand = offCommands[no];}/*** 撤销*/public void undo() {unCommand.undo();}
}

  客户端:

public class Client {public static void main(String[] args) {Light light = new Light();//初始化开启电灯命令LightOnCommand lightOnCommand = new LightOnCommand(light);//初始化关闭电灯命令LightOffCommand lightOffCommand = new LightOffCommand(light);//初始化遥控器面板RemoteController remoteController = new RemoteController(5);remoteController.setCommand(0, lightOnCommand, lightOffCommand);System.out.println("--------开启电灯--------");remoteController.onButtonWasPushed(0);System.out.println("--------关闭电灯--------");remoteController.offButtonWasPushed(0);System.out.println("--------撤销上一步操作--------");remoteController.undo();}
}

  如果需要进行扩展,比如电视,只需要去增加一个电视机对象,以及两个实现了命令接口的具体命令类,最后在客户端进行初始化即可,无需对命令管理类进行修改。

二、访问者模式

  访问者模式是一种行为型设计模式,主要用来将操作与对象结构分离。核心思想在不修改对象结构的前提下,定义作用于这些对象的新操作。
  访问者模式通过引入一个访问者对象,将对结构中各个元素的操作从元素类中抽离出来。这样,当需要添加新操作时,无需修改元素类,而只需添加新的访问者类,访问者模式由以下几个角色组成:

  • 访问者接口:声明对每种元素类型的访问操作方法。
  • 具体访问者:实现访问者接口,定义具体的操作逻辑。
  • 元素接口:定义接受访问者对象的方法。
  • 具体元素:实现元素接口,在接受访问者对象的方法中调用访问者的相应方法。
  • 对象结构:包含一组元素,可以遍历这些元素并让访问者访问它们。

  举一个生活中的案例,例如在网上商城将商品加入购物车,需要计算商品总价,还需要获取商品的详细清单,利用访问者模式,可以将商品作为被访问的类

/*** 被访问的元素接口*/
public interface Item {void accept(Visitor visitor);
}
/*** 被访问的元素:书籍*/
public class Book implements Item {private String title;private double price;public Book(String title, double price) {this.title = title;this.price = price;}public String getTitle() {return title;}public double getPrice() {return price;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}
/*** 被访问的元素:电子产品*/
public class Electronic implements Item {private String name;private double price;public Electronic(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}

  定义一个访问者接口层,访问具体的商品(书籍,电子产品):

public interface Visitor {void visit(Book book);void visit(Electronic electronic);
}

  定义计算价格和打印清单具体的行为:

/*** 打印清单*/
public class ItemPrinterVisitor implements Visitor {@Overridepublic void visit(Book book) {System.out.println("Book: " + book.getTitle() + " - Price: " + book.getPrice());}@Overridepublic void visit(Electronic electronic) {System.out.println("Electronic: " + electronic.getName() + " - Price: " + electronic.getPrice());}
}
/*** 计算书籍和电子产品的价格*/
public class PriceCalculatorVisitor implements Visitor{private double totalPrice = 0;@Overridepublic void visit(Book book) {totalPrice = totalPrice+book.getPrice();}@Overridepublic void visit(Electronic electronic) {totalPrice = totalPrice + electronic.getPrice();}public double getTotalPrice(){return totalPrice;}
}

  以及具体的对象接口,让访问者去操作书籍和电子产品:

/*** 对象接口*/
public class ShoppingCart {private List<Item> items = new ArrayList<>();public void addItem(Item item) {items.add(item);}public void accept(Visitor visitor) {for (Item item : items) {item.accept(visitor);}}
}

  客户端:

public class Client {public static void main(String[] args) {// 创建购物车ShoppingCart cart = new ShoppingCart();cart.addItem(new Book("Spring源码深度解析", 50));cart.addItem(new Electronic("Iphone17", 6300));// 计算总价PriceCalculatorVisitor priceCalculator = new PriceCalculatorVisitor();cart.accept(priceCalculator);System.out.println("Total Price: " + priceCalculator.getTotalPrice());// 打印清单ItemPrinterVisitor itemPrinter = new ItemPrinterVisitor();cart.accept(itemPrinter);}
}

  这样的好处在于,将具体的操作对象结构分离,后续增加具体的操作,无需对对象结构进行修改。因此适用于需要对对象结构中的对象执行多种不相关的操作,而操作的实现细节需要彼此独立的场景。
  同时也可以体会一下如果不使用访问者模式,会存在怎么样的弊端:

  1. 代码耦合度高:在ShoppingCart类中,我们需要判断每个商品的类型,并为不同类型的商品执行不同的操作。这导致了ShoppingCart 类与具体商品类型的紧密耦合。如果要新增一种商品?就又要加一个条件分支。
  2. 扩展性差:同样地,除了计算价格和打印清单,如果还需要再增加一种操作?那么购物车的代码还需要进行修改。
// 书籍类(Book)
public class Book {private String title;private double price;public Book(String title, double price) {this.title = title;this.price = price;}public String getTitle() {return title;}public double getPrice() {return price;}
}// 电子产品类(ElectronicProduct)
public class ElectronicProduct {private String name;private double price;public ElectronicProduct(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}
}

  在购物车中直接计算总价,打印清单:

import java.util.ArrayList;
import java.util.List;public class ShoppingCart {private List<Object> items;public ShoppingCart() {items = new ArrayList<>();}public void addItem(Object item) {items.add(item);}public List<Object> getItems() {return items;}// 计算总价public double calculateTotalPrice() {double totalPrice = 0;for (Object item : items) {if (item instanceof Book) {totalPrice += ((Book) item).getPrice();} else if (item instanceof ElectronicProduct) {totalPrice += ((ElectronicProduct) item).getPrice();}}return totalPrice;}// 打印订单清单public void printOrderDetails() {System.out.println("Shopping Cart Details:");for (Object item : items) {if (item instanceof Book) {Book book = (Book) item;System.out.println("Book: " + book.getTitle() + ", Price: " + book.getPrice());} else if (item instanceof ElectronicProduct) {ElectronicProduct product = (ElectronicProduct) item;System.out.println("Product: " + product.getName() + ", Price: " + product.getPrice());}}}
}

三、迭代器模式

  迭代器模式是一种行为型设计模式,核心思想是将集合的遍历操作与集合的实现分离,从而不暴露集合的内部结构,只需要依赖一个公共的接口来访问集合中的元素。通过这种方式,可以在不改变集合结构的情况下,提供不同的遍历方式和逻辑,通常包含以下的角色:

  • 迭代器接口:定义遍历集合元素的方法,可以使用JDK的Iterator接口。
  • 具体迭代器:实现迭代器接口,负责具体的遍历逻辑。
  • 聚合接口:定义返回一个迭代器的方法。
  • 具体聚合:实现聚合接口,维护集合的实际数据结构,并提供创建迭代器的方法。

  假设现在有一个电商项目,需要管理多个订单。每个订单由多个商品组成。希望能够顺序遍历订单中的商品,而不暴露订单内部的实现细节,首先创建一个商品的实例

public class Product {private String productName;private double price;public Product(String productName, double price) {this.productName = productName;this.price = price;}public String getProductName() {return productName;}public double getPrice() {return price;}
}

  再编写一个聚合接口

public interface Aggregate {Iterator createIterator();
}

  创建具体聚合迭代器接口

public class Order implements Aggregate{private String orderId;private Product[] items;public Order(String orderId, Product[] items) {this.orderId = orderId;this.items = items;}public String getOrderId() {return orderId;}public Product[] getItems() {return items;}@Overridepublic Iterator createIterator() {return new OrderIterator(this);}
}
public class OrderIterator implements Iterator {private Order order;private int index;public OrderIterator(Order order) {this.order = order;this.index = 0;}@Overridepublic boolean hasNext() {return index < order.getItems().length;}@Overridepublic Object next() {if (hasNext()) {return order.getItems()[index++];}return null;}
}

  客户端,创建产品并且遍历:

public class Client {public static void main(String[] args) {Product p1 = new Product("产品1", 5);Product p2 = new Product("产品2", 6);Product p3 = new Product("产品3", 7);Order order = new Order("10001", new Product[]{p1, p2, p3});Iterator iterator = order.createIterator();while (iterator.hasNext()){Product next = (Product) iterator.next();System.out.println(next);}}
}

  同时也可以对比一下不使用迭代器模式,在客户端中直接遍历,体会一下存在哪些弊端:

  1. 客户端直接操作 Order 类的内部实现,如果以后把商品的存储方式改为其他数据结构,所有使用 Order 类的代码都需要修改。
  2. 遍历商品数组的逻辑直接写在了客户端,每个需要遍历商品的地方,都必须重复写遍历逻辑。
  3. 以及如果需要进行不同方式的遍历,需要修改遍历逻辑以及所有使用到的客户端的代码。
public class Product {private String productName;private double price;public Product(String productName, double price) {this.productName = productName;this.price = price;}public String getProductName() {return productName;}public double getPrice() {return price;}
}public class Order {private String orderId;private Product[] items;public Order(String orderId, Product[] items) {this.orderId = orderId;this.items = items;}public String getOrderId() {return orderId;}public Product[] getItems() {return items;}
}public class Client {public static void main(String[] args) {// 创建产品Product p1 = new Product("Laptop", 1000);Product p2 = new Product("Smartphone", 800);Product p3 = new Product("Headphones", 150);// 创建订单Product[] products = {p1, p2, p3};Order order = new Order("O1001", products);// 直接在客户端遍历商品Product[] items = order.getItems();for (int i = 0; i < items.length; i++) {System.out.println("Product: " + items[i].getProductName() + ", Price: " + items[i].getPrice());}}
}

相关文章:

设计模式の命令访问者迭代器模式

文章目录 前言一、命令模式二、访问者模式三、迭代器模式 前言 本篇是关于设计模式中命令模式、访问者模式、以及迭代器模式的学习笔记。 一、命令模式 命令模式是一种行为型设计模式&#xff0c;其核心目的在于将命令的发送者和接受者解耦&#xff0c;提供一个中间层对命令进行…...

信息系统项目管理 -冲突管理

信息系统项目管理题 冲突管理&#xff1a; 项目管理信息系统包括&#xff08;&#xff09;软件&#xff0c;用于监督资源的使用情况&#xff0c;协助确保合适的资源适时、适地的用于合适活动。 A资源管理或进度计划 BCRM系统 C采购系统或智能分析 DBOM系统 答案&#xff1a;A …...

Gmsh有限元网格剖分(Python)---点、直线、平面的移动

Gmsh有限元网格剖分(Python)—点、直线、平面的移动和旋转 最近在学习有限元的网格剖分算法&#xff0c;主要还是要参考老外的开源Gmsh库进行&#xff0c;写一些博客记录下学习过程&#xff0c;方便以后回忆嘞。 Gmsh的官方英文文档可以参考&#xff1a;gmsh.pdf 但咋就说&a…...

山景BP1048增加AT指令,实现单片机串口控制播放音乐(一)

1、设计目的 山景提供的SDK是蓝牙音箱demo&#xff0c;用户使用ADC按键或者IR遥控器&#xff0c;进行人机交互。然而现实很多场景&#xff0c;需要和单片机通信&#xff0c;不管是ADC按键或者IR接口都不适合和单片机通信。这里设计个AT指令用来和BP1048通信。AT指令如下图所示…...

SMMU软件指南SMMU编程之全局错误和最小配置

安全之安全(security)博客目录导读 目录 一、全局错误 二、最小配置 一、全局错误 与编程接口相关的全局错误会报告到适当的 SMMU_(*_)GERROR 寄存器&#xff0c;而不是通过基于内存的事件队列。这些错误通常是严重的&#xff0c;例如导致 SMMU 停止向前推进。例如&#xf…...

CPU条件下Pytorch、jupyter环境配置

一、创建虚拟环境 查看虚拟环境 conda env list 创建python虚拟环境 conda create -n minist python3.11 激活虚拟环境 conda activate minist 查看虚拟环境下有哪些包 pip list 二、安装pytorch 切换清华源 conda config --add channels https://mirrors.tuna.tsing…...

【自用】通信内网部署rzgxxt项目_01,后端pipeDemo部署(使用nssm.exe仿照nohup)

做完这些工作之后&#xff0c;不要忘记打开 Windows Server 的防火墙端口&#xff0c;8181、8081、8080、22、443、1521 做完这些工作之后&#xff0c;不要忘记打开 Windows Server 的防火墙端口&#xff0c;8181、8081、8080、22、443、1521 做完这些工作之后&#xff0c;不要…...

Ubuntu 安装实时内核指南

在运行需要高精度和低延迟响应的机器人驱动程序时&#xff0c;安装一个具备实时内核&#xff08;Real-Time Kernel&#xff09;的 Ubuntu 系统是至关重要的。若缺乏实时系统的支持&#xff0c;高频率的控制指令可能会导致机器人运动轨迹不流畅&#xff0c;甚至产生抖动现象。以…...

MySQL 主从复制与高可用

在现代分布式系统中&#xff0c;数据库的高可用性和可靠性至关重要。MySQL 提供了主从复制&#xff08;Master-Slave Replication&#xff09;机制来实现数据的冗余和容错&#xff0c;保证在主数据库发生故障时能够继续提供服务。而在此基础上&#xff0c;通过进一步的高可用架…...

RCE总结

文章目录 常见漏洞执行函数&#xff1a;1.系统命令执行函数2.代码执行函数 命令拼接符读取文件命令绕过&#xff1a;空格过滤绕过关键字绕过长度过滤绕过无参数命令执行绕过无字母数字绕过利用%0A截断利用回溯绕过利用create_function()代码注入无回显RCE1.反弹shell2.dnslog外…...

基于UNITY3D的照片墙演示项目技术分享

unity实现超大图片墙演示,由于拥有海量图片&#xff0c;使用了CPU 多线程&#xff0c;unity dots技术&#xff0c;图片组成文字部分&#xff0c;使用了点阵图技术&#xff0c;提取文字像素。 &#xff08;关于点阵介绍&#xff09; 点阵字体是把每一个字符都分成1616或2424个点…...

随手记:小程序兼容后台的wangEditor富文本配置链接

场景&#xff1a; 在后台配置wangEditor富文本&#xff0c;可以文字配置链接&#xff0c;图片配置链接&#xff0c;产生的json格式为&#xff1a; 例子&#xff1a; <h1><a href"https://uniapp.dcloud.net.cn/" target"_blank"><span sty…...

maven项目运行时NoSuchMethodError问题排查记录(依赖冲突解决)

控制台异常如下&#xff1a; Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: org.apache.commons.io.input.BoundedInputStream.builder()Lorg/apache/commons/io/input/BoundedInputStream$Builder;问题明显&#xff0c;根据NoSuchMethodError…...

ECharts关系图-关系图11,附视频讲解与代码下载

引言&#xff1a; 关系图&#xff08;或称网络图、关系网络图&#xff09;在数据可视化中扮演着至关重要的角色。它们通过节点&#xff08;代表实体&#xff0c;如人、物体、概念等&#xff09;和边&#xff08;代表实体之间的关系或连接&#xff09;的形式&#xff0c;直观地…...

【C语言】动态内存管理:详解malloc和free函数

前言 在C语言编程中&#xff0c;动态内存分配是一个非常重要的概念。与静态内存分配不同&#xff0c;动态内存分配允许程序在运行时根据需要分配和释放内存&#xff0c;从而更加灵活地管理内存资源。特别是在一些数据结构的引用中经常需要使用&#xff0c;下面我们就详细讲解一…...

EGO Swarm翻译

目录 摘要 Ⅰ 介绍 Ⅱ 相关工作 A . 单四旋翼局部规划 B . 拓扑规划 C. 分布式无人机集群 Ⅲ 基于梯度的局部规划隐式拓扑轨迹生成 A.无需ESDF梯度的局部路径规划 B.隐式拓扑轨迹生成 Ⅳ 无人机集群导航 A 机间避碰 B. 定位漂移补偿 C. 从深度图像中去除agent Ⅴ …...

Linux根目录

在Linux系统中&#xff0c;文件系统遵循一种标准化的目录结构&#xff0c;即文件系统层次结构标准&#xff08;Filesystem Hierarchy Standard&#xff0c;FHS&#xff09;。 根目录&#xff08;/&#xff09; /bin&#xff1a;包含二进制可执行文件&#xff0c;通常是用户和系…...

SAP-SD-参照退货订单补货时带不出行项目

业务场景&#xff1a; 当物料出现质量问题时&#xff0c;客户需要换货&#xff0c;不需要退款&#xff0c;就需要先做退货订单&#xff0c;然后参照退货订单进行补货&#xff0c;创建补货订单&#xff0c;但是创建补货订单时只是把抬头数据带入补货订单&#xff0c;没有带入行项…...

12-C语言单向链表

一、链表的概述 1.链表与数组对比 遍历数组中的数据&#xff0c;查询数据比较方便&#xff0c;但往数组中插入、删除数据需要移动大量数据&#xff1b;相反。链表遍历、查询数据不方便&#xff0c;但是插入、删除数据比较方便&#xff0c;不需要移动大量数据&#xff0c;直接…...

2024年11月 蓝桥杯青少组 STEMA考试 Scratch真题

2024年11月 蓝桥杯青少组 STEMA考试 Scratch真题&#xff08;选择题&#xff09; 题目总数&#xff1a;5 总分数&#xff1a;50 选择题 第 1 题 单选题 Scratch运行以下程宇后&#xff0c;小兔子会&#xff08; &#xff09;。 A. 变小 B. 变大 C. 变色 D. …...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...