Go和Java实现原型模式
Go和Java实现原型模式
下面将通过一个克隆的示例来说明原型模式的使用。
1、原型模式
原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对
象的最佳方式之一。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种
模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它
的克隆,在需要的时候更新数据库,以此来减少数据库调用。
-
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
-
主要解决:在运行期建立和删除原型。
-
何时使用:1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定
时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只
能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类
更方便一些。
-
如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。
-
关键代码:1、实现克隆操作,在 JAVA 实现 Cloneable 接口,重写 clone(),在 .NET 中可以使用 Object 类的
MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔
离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。
-
应用实例:1、细胞分裂。 2、JAVA 中的 Object clone() 方法。
-
优点:1、性能提高。 2、逃避构造函数的约束。
-
缺点:1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很
容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable
接口。
-
使用场景:1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、
性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型
模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要
修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出
现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型
模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
-
注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象
的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。
-
适用性:
当一个系统应该独立于它的产品创建、构成和表示时。
当要实例化的类是在运行时刻指定时,例如通过动态装载。
为了避免创建一个与产品类层次平行的工厂层次时。
当一个类的实例只能有几个不同状态组合中的一种时。
建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
2、Go实现原型模式
package prototype// ========== Cloneable ==========
// Cloneable是原型对象需要实现的接口
type Cloneable interface {Clone() Cloneable
}
package prototypeimport "encoding/json"// ========== DeepCopy ==========
// 深拷贝实现Cloneable
type DeepCopy struct {Name string
}// clone也可以使用序列化与反序列化的方式实现深拷贝
func (t *DeepCopy) SerializableClone() Cloneable {var cloneable Cloneableb, _ := json.Marshal(t)json.Unmarshal(b, &cloneable)return cloneable
}func (t *DeepCopy) Clone() Cloneable {tc := *treturn &tc
}
package prototype// ========== ShallowCopy ==========
// 浅拷贝实现Cloneable
type ShallowCopy struct {Name string
}func (t *ShallowCopy) Clone() Cloneable {return t
}
package prototype// ========== PrototypeManager ==========
type PrototypeManager struct {prototypes map[string]Cloneable
}func NewPrototypeManager() *PrototypeManager {return &PrototypeManager{prototypes: make(map[string]Cloneable),}
}func (p *PrototypeManager) Get(name string) Cloneable {return p.prototypes[name].Clone()
}func (p *PrototypeManager) Set(name string, prototype Cloneable) {p.prototypes[name] = prototype
}
package mainimport ("fmt". "proj/prototype"
)var (deepCopyManager *PrototypeManagershallowCopyManager *PrototypeManager
)func init() {deepCopyManager = NewPrototypeManager()deepCopyManager.Set("dc", &DeepCopy{Name: "DeepCopy"})shallowCopyManager = NewPrototypeManager()shallowCopyManager.Set("sc", &ShallowCopy{Name: "ShallowCopy"})
}func main() {// ========== TestDeepCopyClone ==========t1 := deepCopyManager.Get("dc")t2 := t1.Clone()// 深拷贝,指向的不是同一个变量的地址// falsefmt.Println(t1 == t2)t3 := t2.(*DeepCopy)t3.Name = "DeepCopyUpdate"t4 := t1.(*DeepCopy)// 深拷贝Name,不会影响到copy前的变量// DeepCopyUpdatefmt.Println(t3.Name)// DeepCopyfmt.Println(t4.Name)// ========== TestShallowCopyClone ==========t5 := shallowCopyManager.Get("sc")t6 := t5.Clone()// 浅拷贝,变量地址的指向不变// truefmt.Println(t5 == t6)t7 := t6.(*ShallowCopy)t7.Name = "ShallowCopyUpdate"t8 := t5.(*ShallowCopy)// 浅拷贝Name,copy之前的变量和copy之后的变量同时更改// ShallowCopyUpdatefmt.Println(t7.Name)// ShallowCopyUpdatefmt.Println(t8.Name)
}
3、Java实现原型模式
创建一个抽象类 Shape 和扩展了 Shape 类的实体类,然后定义类 ShapeCache,该类把 shape 对象存储在一个
Hashtable 中,并在请求的时候返回它们的克隆。
package com.prototype;// ========== Shape ==========
public abstract class Shape implements Cloneable {private String id;protected String type;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getType() {return type;}public void setType(String type) {this.type = type;}abstract void draw();@Overridepublic Object clone() {Object clone = null;try {clone = super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return clone;}
}
package com.prototype;// ========== Circle ==========
public class Circle extends Shape {public Circle(){type = "Circle";}@Overridepublic void draw() {System.out.println("Draw Circle!");}
}
package com.prototype;// ========== Rectangle ==========
public class Rectangle extends Shape {public Rectangle(){type = "Rectangle";}@Overridepublic void draw() {System.out.println("Draw Rectangle!");}
}
package com.prototype;// ========== Square ==========
public class Square extends Shape {public Square(){type = "Square";}@Overridepublic void draw() {System.out.println("Draw Square!");}
}
package com.prototype;import java.util.Hashtable;// ========== ShapeCache ==========
public class ShapeCache {private final static Hashtable<String, Shape> SHAPE_MAP = new Hashtable<>();public static Shape getShape(String shapeId) {Shape cachedShape = SHAPE_MAP.get(shapeId);// 这里是浅拷贝,返回一个新的对象// 新对象里的引用类型变量地址指向的还是原对象内引用类型地址return (Shape) cachedShape.clone();}// 对每种形状都运行数据库查询,并创建该形状public static void loadCache() {Circle circle = new Circle();circle.setId("1");SHAPE_MAP.put(circle.getId(),circle);Square square = new Square();square.setId("2");SHAPE_MAP.put(square.getId(),square);Rectangle rectangle = new Rectangle();rectangle.setId("3");SHAPE_MAP.put(rectangle.getId(),rectangle);}
}
package com.prototype;public class Test {public static void main(String[] args) {ShapeCache.loadCache();Shape clonedShape = ShapeCache.getShape("1");System.out.println("Shape : " + clonedShape.getType());clonedShape.draw();Shape clonedShape2 = ShapeCache.getShape("2");System.out.println("Shape : " + clonedShape2.getType());clonedShape.draw();Shape clonedShape3 = ShapeCache.getShape("3");System.out.println("Shape : " + clonedShape3.getType());clonedShape3.draw();}
}
# 输出
Shape : Circle
Draw Circle!
Shape : Square
Draw Circle!
Shape : Rectangle
Draw Rectangle!
4、深拷贝和浅拷贝的区别
深拷贝和浅拷贝是指在复制一个对象时,复制的方式不同。
在进行深拷贝时,复制的是对象及其内部的所有对象。这意味着,如果原始对象中包含一个列表,那么在深拷贝
后,原始对象和拷贝对象中的列表是两个独立的对象,即使它们看起来完全相同,也不会相互影响。
相反,浅拷贝仅复制对象本身,但是如果对象内部包含其他对象,则这些对象并不会被复制。因此,如果原始对象
中包含一个列表,那么在浅拷贝后,原始对象和拷贝对象中的列表是同一个对象。如果你在拷贝对象中更改了列
表,那么原始对象中的列表也会发生变化。
总:
复制对象后,如果修改了原对象或新对象的数据,造成了对其他对象的数据也同时发生了变化的现象,就是浅拷
贝,对象之间仍然存在关联。
如果复制后的对象与原对象,无论数据如何变化,都不会对其它对象带来变化,就是深拷贝,对象之间已经毫无关
系。
5、补充Java的深拷贝和浅拷贝的实现
5.1 浅拷贝
package com.shallowcopy;// ========== ShallowCopyDemo ==========
public class ShallowCopyDemo {public static void main(String[] args) {// ======浅拷贝=============System.out.println("======浅拷贝=============");// 初始化对象User user = new User();user.setAge(25);Name name = new Name();name.setFirst("li");name.setSecond("si");user.setName(name);// 实现对象克隆User cloneUser = (User) user.clone();// 修改原始对象属性值cloneUser.setAge(27);cloneUser.getName().setFirst("wang");cloneUser.getName().setSecond("wu");// 源对象:User{age=25, name=Name{first='wang', second='wu'}}System.out.println("源对象:" + user);// 新对象:User{age=27, name=Name{first='wang', second='wu'}}System.out.println("新对象:" + cloneUser);// 在clone()方法中,我们仅对User对象实现了克隆,但是没有对User类下的属性类Name进行克隆// 执行克隆后,新User对象下的Name属性与原对象下的Name属性,仍然指向同一块内存// 如果Name属性发生变更,所有克隆对象的Name属性都会变化,此即为浅克隆}
}class User implements Cloneable{int age;Name name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Name getName() {return name;}public void setName(Name name) {this.name = name;}@Overridepublic String toString() {return "User{" +"age=" + age +", name=" + name +'}';}// 实现Cloneable接口并重写Object类的clone()方法@Overridepublic Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}
}class Name{String first;String second;public String getFirst() {return first;}public void setFirst(String first) {this.first = first;}public String getSecond() {return second;}public void setSecond(String second) {this.second = second;}@Overridepublic String toString() {return "Name{" +"first='" + first + '\'' +", second='" + second + '\'' +'}';}
}
5.2 深拷贝
package com.deepcopy;// ========== DeepCopyDemo ==========
public class DeepCopyDemo {public static void main(String[] args) {// ======深拷贝=============System.out.println("======深拷贝=============");// 初始化对象User user = new User();user.setAge(25);Name name = new Name();name.setFirst("li");name.setSecond("si");user.setName(name);// 实现对象克隆User cloneUser = (User) user.clone();// 修改原始对象属性值cloneUser.setAge(27);cloneUser.getName().setFirst("wang");cloneUser.getName().setSecond("wu");// 源对象:User{age=25, name=Name{first='li', second='si'}}System.out.println("源对象:" + user);// 新对象:User{age=27, name=Name{first='wang', second='wu'}}System.out.println("新对象:" + cloneUser);}
}class Name implements Cloneable{String first;String second;public String getFirst() {return first;}public void setFirst(String first) {this.first = first;}public String getSecond() {return second;}public void setSecond(String second) {this.second = second;}@Overridepublic String toString() {return "Name{" +"first='" + first + '\'' +", second='" + second + '\'' +'}';}// 实现Cloneable接口并重写Object类的clone()方法@Overridepublic Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}
}class User implements Cloneable{int age;Name name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Name getName() {return name;}public void setName(Name name) {this.name = name;}@Overridepublic String toString() {return "User{" +"age=" + age +", name=" + name +'}';}// 实现Cloneable接口并重写Object类的clone()方法// 这种方法实现的深克隆比较笨重,如果User类下有多个属性类时,要实现深克隆就需要对所有类重写clone()方法@Overridepublic Object clone() {try {User u = (User) super.clone();//调用属性的克隆方法u.setName((Name) this.name.clone());return u;} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}
}
5.3 fastjson实现深拷贝
package com.fastjsondeepcopy;import com.alibaba.fastjson.JSON;public class FastjsonDemo {public static void main(String[] args) {// ======深拷贝=============System.out.println("======深拷贝=============");// 初始化对象User user = new User();user.setAge(25);Name name = new Name();name.setFirst("li");name.setSecond("si");user.setName(name);// 实现对象克隆String jsonString = JSON.toJSONString(user);User cloneUser = JSON.parseObject(jsonString, User.class);// 修改原始对象属性值cloneUser.setAge(27);cloneUser.getName().setFirst("wang");cloneUser.getName().setSecond("wu");// 源对象:User{age=25, name=Name{first='li', second='si'}}System.out.println("源对象:" + user);// 新对象:User{age=27, name=Name{first='wang', second='wu'}}System.out.println("新对象:" + cloneUser);}
}class User{int age;Name name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Name getName() {return name;}public void setName(Name name) {this.name = name;}@Overridepublic String toString() {return "User{" +"age=" + age +", name=" + name +'}';}
}class Name{String first;String second;public String getFirst() {return first;}public void setFirst(String first) {this.first = first;}public String getSecond() {return second;}public void setSecond(String second) {this.second = second;}@Overridepublic String toString() {return "Name{" +"first='" + first + '\'' +", second='" + second + '\'' +'}';}
}
相关文章:
Go和Java实现原型模式
Go和Java实现原型模式 下面将通过一个克隆的示例来说明原型模式的使用。 1、原型模式 原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对 象的最佳方式之一。 这种模式是实现了一个原型接口&am…...

linux I/O性能优化
Linux 文件系统 磁盘和文件系统的关系: 磁盘为系统提供了最基本的持久化存储。 文件系统则在磁盘的基础上,提供了一个用来管理文件的树状结构。 文件系统工作原理 索引节点和目录项 文件系统,本身是对存储设备上的文件,进行组织…...

PHP最简单自定义自己的框架model使用(七)
1、实现model使用效果 2、自动加载model,KJ.php //自动加载文件public static function _autoload($className){switch ($className){//自动model类case substr($className,-5)Model:$path MODEL./.$className..php;if(is_file($path)) include $path;break;//自动加载控制器…...

程序猿成长之路之密码学篇-分组密码加密模式及IV(偏移量)的详解
Cipher.getInstance("AES/ECB/PKCS5Padding"); Cipher cipher Cipher.getInstance("AES/CBC/PKCS5Padding"); 在进行加解密编程的时候应该有很多小伙伴接触过以上的语句,但是大伙儿在编码过程中是否了解过ECB/CBC的含义、区别以及PKCS5Padding…...
Windows下批处理删除文件
最近我使用Maven的时候会出现下载jar包不成功的现象,然后需要把它删除然后重新下载,但是有时候文件过多,一个个删除太花费时间,所以用bat的批处理会很舒服。 bat的语法我之前没遇到过,然后我是边学习边试验࿰…...
html中文件上传储存到本地路径
第一步:写html文件 <form action"/uplode" method"post" enctype"multipart/form-data">姓名:<input type"text" name"username"><br>年龄:<input type"text" name"age"><…...
第九章 SpringBoot 自动配置原理 入门
1. 引导加载自动配置类 SpringBootApplication -- SpringBootConfiguration -- EnableAutoConfiguration -- ComponentScan //SpringBootApplicationSpringBootConfiguration EnableAutoConfiguration ComponentScan(excludeFilters { Filter(type FilterType.CUSTOM, cl…...
String str=new String(“tango“) 创建了几个对象?
面试回答 创建的对象数 应该是1个或者2个。 首先要清楚什么是对象? Java 是一种面向对象的语言,而 Java 对象在 JVM 中的存储也是有一定的结构的,在 HotSpot 虚拟机中,存储的形式就是 oop-klass model,即 Java 对象模型…...

引入三阶失真的非线性放大器的模拟输出及使用中值滤波器去除峰值研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

【观察者设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
简介 观察者模式(Observer Pattern)是一种行为型模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 观察者模式使用三个类Subject、Observer和Client。Subject…...
精细解析中文公司名称:智能分词工具助力地名、品牌名、行业词和后缀提取
精细解析中文公司名称:智能分词工具助力地名、品牌名、行业词和后缀提取 中文公司名称分词工具,支持公司名称中的地名,品牌名(主词),行业词,公司名后缀提取。 对公司名文本解析,识…...

网络编程(JavaEE初阶系列10)
目录 前言: 1.网络编程的基础 1.1为什么需要网络编程 1.2什么是网络编程 1.3网络编程中的基本概念 1.3.1发送端和接收端 1.3.2请求和响应 1.3.3客户端和服务端 2.Socket套接字 2.1概念 2.2分类 3.UDP数据报套接字编程 3.1DataGramSocket API 3.2Datagr…...
Git常用的指令
Git常用的指令 OMMP提交代码的流程 0、配置: git config --list 查看当前配置 git congig --global user.name user 这个会显示你的提交到git的名字 格式:git config [–local|–global|–system] –unset section.key 格式:git config [–l…...

LoadRunner(2)
一、Controller 1.1场景设计 1.通过VUG打开 施压机器:发起请求的角色(用户本地电脑) 被压机器:处理请求的角色(服务器) 2.直接双击Controller 场景设计:需要关注三个部分 第一部分: 第二部分: 2.1运行场景…...

CTF之逆向之阿里巴巴
题目地址:http://www.shiyanbar.com/ctf/13 题目预览: 解题过程: 1、下载附件发现是exe文件 2、使用PEid和Detect It Easy查壳 和 开发语言,发现没有加壳,都是用C#开发的 3、C#和Java Python属于解释型语言ÿ…...

Labview控制APx(Audio Precision)进行测试测量(五)
驱动程序 VIs如何处理配置设置中的单元 APx500 应用程序具有复杂的控件,具有以下功能: 数值和单位组合在一个控制中(例如,1.000 Vrms ) •值转换为 SI 格式(例如,1.000 mVrms 或 1.000 μVrms) •单位之间的转换发生在控制(例如,V…...

在单元测试中使用Jest模拟VS Code extension API
对VS Code extension进行单元测试时通常会遇到一个问题,代码中所使用的VS Code编辑器的功能都依赖于vscode库,但是我们在单元测试中并没有添加对vscode库的依赖,所以导致运行单元测试时出错。由于vscode库是作为第三方依赖被引入到我们的VS C…...

django boostrap html实现可拖拽的左右布局,鼠标拖动调整左右布局的大小或占比
一、实现的效果 最近需要在Django项目中,实现一个左右布局的html页面,页面框架使用的是boostrap。但这个布局不是简单的左右分栏布局,而是需要实现可以通过鼠标拖拽的方式动态调整左右两侧布局的大小和占比。效果大致如下: 一开始,页面分为左右两块布局: 鼠标放到中间的…...

谈谈闭包和闭包使用场景
一、什么是闭包 概念:闭包还是作用域的一种特殊应用 二、触发闭包的情况 1.函数当做返回值被返回 2.函数当做参数被传递 3.自执行匿名函数 //情况1:函数当做返回值被返回 function fn(){const a 1;return function(){console.log(a) //1}; } const a …...
MATLAB算法实战应用案例精讲-【图像处理】边界框锚框
目录 目标检测 应用场景 目标检测发展历程 常用数据集 边界框(bounding box)...

JUC并发编程(二)Monitor/自旋/轻量级/锁膨胀/wait/notify/锁消除
目录 一 基础 1 概念 2 卖票问题 3 转账问题 二 锁机制与优化策略 0 Monitor 1 轻量级锁 2 锁膨胀 3 自旋 4 偏向锁 5 锁消除 6 wait /notify 7 sleep与wait的对比 8 join原理 一 基础 1 概念 临界区 一段代码块内如果存在对共享资源的多线程读写操作…...

生产管理系统开发:专业软件开发公司的实践与思考
生产管理系统开发的关键点 在当前制造业智能化升级的转型背景下,生产管理系统开发正逐步成为企业优化生产流程的重要技术手段。不同行业、不同规模的企业在推进生产管理数字化转型过程中,面临的挑战存在显著差异。本文结合具体实践案例,分析…...

LeetCode 2894.分类求和并作差
目录 题目: 题目描述: 题目链接: 思路: 思路一详解(遍历 判断): 思路二详解(数学规律/公式): 代码: Java思路一(遍历 判断&a…...

智慧城市项目总体建设方案(Word700页+)
1 背景、现状和必要性 1.1 背景 1.1.1 立项背景情况 1.1.2 立项依据 1.2 现状 1.2.1 党建体系运行现状 1.2.2 政务体系运行现状 1.2.3 社会治理运行现状 1.2.4 安全监管体系现状 1.2.5 环保体系运行现状 1.2.6 城建体系运行现状 1.2.7 社区体系运行现状 1.2.8 园区…...
Unity基础-Mathf相关
Unity基础-Mathf相关 一、Mathf数学工具 概述 Mathf是Unity中封装好用于数学计算的工具结构体,提供了丰富的数学计算方法,特别适用于游戏开发场景。它是Unity开发中最常用的数学工具之一,能够帮助我们处理各种数学计算和插值运算。 Mathf…...
C++.OpenGL (9/64)摄像机(Camera)
颜色(Color) 颜色理论在OpenGL中的应用 #mermaid-svg-dKNDfS4EKDUmG4Ts {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-dKNDfS4EKDUmG4Ts .error-icon{fill:#552222;}#mermaid-svg-dKNDfS4EKDUmG4Ts .error-text…...

leetcode238-除自身以外数组的乘积
leetcode 238 思路 可以在不使用除法的情况下,利用前缀积和后缀积来实现解答 前缀积:对每个位置,计算当前数字左侧的所有数字的乘积后缀积:对每个位置,计算当前数字右侧的所有数字的乘积 结合这两种思想࿰…...

下一代设备健康管理解决方案:基于多源异构数据融合的智能运维架构
导语: 在工业4.0深度演进的关键节点,传统设备管理面临数据孤岛、误诊率高、运维滞后三大致命瓶颈。本文解析基于边缘智能与数字孪生的新一代解决方案架构,并实测验证中讯烛龙PHM-X系统如何通过多模态感知→智能诊断→自主决策闭环,…...
Java线程池核心原理与最佳实践
Java 线程池详解 线程池是Java并发编程的核心组件,它能高效管理线程生命周期,避免频繁创建销毁线程的开销,提升系统性能和资源利用率。 一、线程池核心优势 降低资源消耗:复用已创建的线程,减少线程创建销毁开销提高…...

Vue ④-组件通信 || 进阶语法
组件三大部分 template:只有能一个根元素 style:全局样式(默认):影响所有组件。局部样式:scoped 下样式,只作用于当前组件 script:el 根实例独有,data 是一个函数,其他配置项一致…...