标签:源码 location put nullable 需要 eager hat abs conf
公司使用自行开发的一套系统进行日常工作办理,但在使用过程中,我们需要每周上传周报总结工作内容。基于此,我们简单的通过代码模拟这一过程。
1.首先定义一个周报类:
public class WeeklyReport {
// 填写人
private String name;
// 周报内容
private String content;
// 上报时间
private String date;
@Override
public String toString() {
return "姓名=‘" + name + ‘\‘‘ +
", 内容=‘" + content + ‘\‘‘ +
", 周次=‘" + date + ‘\‘‘;
}
// 省略getter/setter
}
2.客户端使用:
public class Main {
public static void main(String[] args) {
WeeklyReport weeklyReport1 = new WeeklyReport();
weeklyReport1.setName("张三");
weeklyReport1.setContent("一周都在努力工作~~");
weeklyReport1.setDate("第一周");
System.out.println("第一周周报:" + weeklyReport1);
WeeklyReport weeklyReport2 = new WeeklyReport();
weeklyReport2.setName("张三");
weeklyReport2.setContent("一周都在努力工作~~");
weeklyReport2.setDate("第二周");
System.out.print("第二周周报:" + weeklyReport2);
}
}
其实我们在填写周报的时候,其实会有很多必要但又不太重要的描述需要填写。比如以上面的代码来看,只有date
(周报时间)是变化的,而name
(填写人)和content
(周报内容部分)是不变的,然而我们也不得不重新填写不变的部分。如果我们能够复制一份相同的内容,然后对其只修改不一样的地方,对于我们的工作来说就相对简单一点,这样我们就可以引入原型模式。
原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
角色构成:
特点:
一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype
编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。
1.抽象类:
/**
* 抽象类
*/
public abstract class Prototype {
// 实现这个接口,返回自身对象的克隆对象
public abstract Prototype create();
private String name;
private String content;
private String date;
@Override
public String toString() {
return "姓名=‘" + name + ‘\‘‘ +
", 内容=‘" + content + ‘\‘‘ +
", 周次=‘" + date + ‘\‘‘;
}
// 省略getter/setter
}
2.周报类:
/**
* 具体实现类
*/
public class WeeklyReport extends Prototype {
// 实现接口,返回自身对象的克隆对象
public Prototype create() {
WeeklyReport weeklyReport = new WeeklyReport();
weeklyReport.setName(this.getName());
weeklyReport.setContent(this.getContent());
weeklyReport.setDate(this.getDate());
return weeklyReport;
}
}
3.客户端使用:
public class Main {
public static void main(String[] args) {
Prototype weeklyReport1 = new WeeklyReport();
weeklyReport1.setName("张三");
weeklyReport1.setContent("一周都在努力工作~~");
weeklyReport1.setDate("第一周");
System.out.println("第一周周报:" + weeklyReport1);
// 直接克隆使用,减少常见的属性赋值操作
Prototype weeklyReport2 = weeklyReport1.create();
weeklyReport2.setDate("第二周");
System.out.println("第一周周报:" + weeklyReport2);
}
}
通过上面的改造,我们在写后面的周报的时候就可以基于第一次写的周报修改其中我们需要修改的属性,就可以快速的简单的创建出对象出来。这样我们的对象在创建时需要设置大量相同属性时,就会很有用。
学过Java语言的人都知道,所有的Java类都继承自java.lang.Object。事实上,Object类提供一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。只需要重写clone()
方法就可以了,此时,Object类相当于抽象原型类,所有实现了Cloneable接口的类相当于具体原型类。
这里有两种克隆方式,称之为浅克隆和深克隆。
public class WeeklyReportJDK implements Cloneable {
public static void main(String[] args) {
WeeklyReportJDK weeklyReport1 = new WeeklyReportJDK();
weeklyReport1.setName("张三");
weeklyReport1.setContent("一周都在努力工作~~");
weeklyReport1.setDate("第一周");
System.out.println("第一周周报:" + weeklyReport1);
// 直接使用jdk克隆,减少常见的属性赋值操作
WeeklyReportJDK weeklyReport2 = weeklyReport1.clone();
weeklyReport2.setDate("第二周");
System.out.println("第一周周报:" + weeklyReport2);
}
// 填写人
private String name;
// 周报内容
private String content;
// 上报时间
private String date;
// 重写 Object 中的方法
public WeeklyReportJDK clone() {
WeeklyReportJDK clone = null;
try {
clone = (WeeklyReportJDK) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
@Override
public String toString() {
return "姓名=‘" + name + ‘\‘‘ +
", 内容=‘" + content + ‘\‘‘ +
", 周次=‘" + date + ‘\‘‘;
}
// 省略getter/setter
}
Attachment
对象:public class WeeklyReportJDKDeep implements Cloneable {
public static void main(String[] args) {
WeeklyReportJDKDeep weeklyReport1 = new WeeklyReportJDKDeep();
weeklyReport1.setName("张三");
weeklyReport1.setContent("一周都在努力工作~~");
Attachment attachment = new Attachment();
attachment.setName("附件一");
weeklyReport1.setAttachment(attachment);
weeklyReport1.setDate("第一周");
System.out.println("第一周周报:" + weeklyReport1);
// 直接使用jdk克隆,减少常见的属性赋值操作
WeeklyReportJDKDeep weeklyReport2 = weeklyReport1.clone();
weeklyReport2.setDate("第二周");
System.out.println("第一周周报:" + weeklyReport2);
// 这里使用深克隆之后,是两个对象,所以 false
System.out.println(weeklyReport1.getAttachment() == weeklyReport2.getAttachment());
}
// 填写人
private String name;
// 周报内容
private String content;
// 上报时间
private String date;
// 周报附件
private Attachment attachment;
// 重写 Object 中的方法
public WeeklyReportJDKDeep clone() {
WeeklyReportJDKDeep clone = null;
try {
clone = (WeeklyReportJDKDeep) super.clone();
clone.setAttachment(attachment.clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
@Override
public String toString() {
return "姓名=‘" + name + ‘\‘‘ +
", 内容=‘" + content + ‘\‘‘ +
", 周次=‘" + date + ‘\‘‘;
}
// 省略getter/setter
}
class Attachment implements Cloneable {
private String name;
@Override
public Attachment clone() {
Attachment clone = null;
try {
clone = (Attachment) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
// 省略getter/setter
}
注意:Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等。
我们使用 Spring 来配置 Bean 时,可以通过 scope 来区分创建的对象是单例的还是圆形的。如果是单例的那么每次从 Spring 中获取 Bean 就都是同一个;如果是原型的那么每次获取时就是不同的。
? 1.pom 文件:
<properties>
<spring.version>5.1.15.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
? 2.实体类:
public class Person {
private String name;
private Integer age;
// 省略getter/setter
}
? 3.spring 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.phoegel.prototype.analysis.Person" scope="prototype">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
</beans>
? 4.获取 Bean 实例:
public class Main {
public static void main(String[] args) {
// spring 配置文件
String config = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
Person person1 = (Person) applicationContext.getBean("person");
Person person2 = (Person) applicationContext.getBean("person");
System.out.println(person1 == person2);// false
}
}
可以看到我们在配置 Bean 时scope="prototype"
属性使得,我们两次获取到的person
都是不同的。通过深入源码后,我们找到了 Bean 创建时在AbstractBeanFactory
中doGetBean()
的核心判断代码:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// ...
// Create bean instance.
// 这里判断是否设置为单例模式
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 这里判断是否设置为原型模式
else if (mbd.isPrototype()) {
// It‘s a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// ...
}
1.主要优点:
2.主要缺点:
3.适用场景:
本篇文章github代码地址:https://github.com/Phoegel/design-pattern/tree/main/prototype
转载请说明出处,本篇博客地址:https://www.cnblogs.com/phoegel/p/13909477.html
标签:源码 location put nullable 需要 eager hat abs conf
原文地址:https://www.cnblogs.com/phoegel/p/13909477.html