当前位置:首页 > 单片机 > 程序员小灰
[导读]什么是 “原型模式”



—————  第二天  —————








————————————








假如有一天,小灰被外星人抓走了,外星人要拿小灰做实验,想了解小灰在吃得好、睡得好、玩得开心的场景下,与现实中小灰的生存状态有什么区别。
于是,外星人克隆了几个一模一样的小灰:

就这样,小灰的原型被留在现实中,而三个复制体分别提供了吃得好、睡得好、玩得开心三种不同环境,小灰的原型则不受三个复制体的影响。
过了一段时间,我们来观察一下本体与分身的生存状态:






Java语言中,Object类实现了Cloneable接口,个对象可以通过调Clone()方法生成对象,这就是原型模式的典型应用。
但需要注意的是,clone()方法并不是Cloneable接口里的,而是Object类里的,Cloneable是一个标识接口,标识这个类的对象是可被拷贝的,如果没有实现Cloneable接口,却调用了clone()方法,就会报错。
// protected native Object clone() throws CloneNotSupportedException;protected Object clone() throws CloneNotSupportedException {
if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException( "Class " getClass().getName() " doesn't implement Cloneable");
}
return internalClone();
}
// Native helper method for cloning.
private native Object internalClone();




Java中的数据类型,分为基本类型和引用类型。在一个方法里的变量如果是基本类型的话,变量就直接存储在这个方法的栈帧里,例如int、long等;而引用类型则在栈帧里存储这个变量的指针,指向堆中该实体的地址,例如String、Array等。深拷贝和浅拷贝是只针对引用数据类型的。
比如一个方法有一个基本类型参数和一个引用类型参数,在方法体里对参数重新赋值,会影响传入的引用类型参数,而不会影响基本类型参数,因为基本类型参数是值传递,而引用类型参数是引用传递。
先定义一个用户类:
// 这是一个非常简单的用户类
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{name='" name ", age=" age '}';
}
}
测试:
private int x=10;
public void updateValue(int value){
value = 3 * value;
}
private User user= new User("大黄",20);
public void updateUser(User student){
student.setName("小灰");
student.setAge(18);
}
public void test(){
System.out.println("调用前x的值:" x);
updateValue(x);
System.out.println("调用后x的值:" x);
System.out.println("调用前user的值:" user.toString());
updateUser(user);
System.out.println("调用后user的值:" user.toString());
}


Log打印结果如下:
调用前x的值:10
调用后x的值:10
调用前user的值:User{name='大黄, age=20}
调用后user的值:User{name='小灰, age=18}

传递基本类型的方法(updateValue())流程图:

传递引用类型的方法(updateUser())流程图:

这其中也包含着例外,比如String类型和大小不超过127的Long类型,虽然也是引用类型,却像基本类型一样不受影响。这是因为它们会先比较常量池维护的值,这涉及VM的内容,今天不做过多讨论。

浅拷贝是在按位(bit)拷贝对象,这个对象有着原始对象属性值的一份精确拷贝。我们结合应用场景分析一下,还是刚才的User类,我们增加一个存放地址的内部类Address,我们需要用户信息可以被其他module查询,但是不允许它们被其他module修改,新增代码如下:


// 这是一个稍微复杂的、支持拷贝的用户类

public class User implements Cloneable { // ……省略上文代码……
private Address address;

@NonNull
@NotNull
@Override

public User clone() {
try{
return (User)super.clone();

}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}

public class Address{
// 地市
public String city;
// 区县
public String county;
// 乡镇街道
public String street;
}
}







// 这是一个更复杂的、支持深拷贝的用户类
public class User implements Cloneable {
// ……省略上文代码……
@NonNull
@NotNull
@Override
public User clone() {
try{
User newUser = (User)super.clone();
newUser.setName(this.name);
newUser.setAddress(this.address.clone());
return newUser;
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public class Address implements Cloneable{
// ……省略上文代码……
@NonNull
@NotNull
@Override
public Address clone() {
try{
Address newAddress = (Address)super.clone();
newAddress.city = this.city;
newAddress.county = this.county;
newAddress.street = this.street;
return newAddress;
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
}

需要注意的是,上面代码的深拷贝其实并不彻底,因为彻底的深拷贝几乎是不可能实现的,那样不但可能存在引用关系非常复杂的情况,也可能存在引用链的某一级上引用了一个没有实现Cloneable接口的第三方对象的情况。

绝大多数设计模式都是牺牲性能提升开发效率的,原型模式则是为数不多的牺牲开发效率提升性能的设计模式。







private User user= new User("大黄",20);
public void testNew(){
User user1 = new User("小灰",18);
}
public void testClone(){
User user2 = user.clone();
}
通过ASM工具查看bytecode,可以看出二者对栈资源的消耗:


// access flags 0x1
public  testNew()V
……省略……
MAXSTACK  = 4
MAXLOCALS = 2
// access  flags 0x1
public  testClone()V
……省略……
MAXSTACK  = 1
MAXLOCALS = 2


@Override
public Object clone() {
return new Intent(this);
}


最后我们来总结一下原型模式的核心用途:
1.解决构建复杂对象的资源消耗问题,提升创建对象的效率。
2.保护性拷贝,防止外部对只读对象进行需修改。







本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭