Immutability 和 Singleton 解决的是同一个目的吗?
Does Immutability and Singleton solves the same purpose?
我读到的是不可变性意味着一旦分配的值保持不变,即一旦分配,它在代码中反映为相同的值。
不可变性主要用于多线程环境中的函数式编程范例。
但是
单例模式是否也解决了同样的问题,
下面是java,
写的单例
package com.main;
class Student{
private Student(){
}
public static Student INSTANCE;
static{
INSTANCE = new Student();
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString(){
return "Student is "+this.name;
}
}
public class SingletonTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student student = Student.INSTANCE;
student.setName("Arnold");
student = Student.INSTANCE;
student.setName("Kevin");
student = Student.INSTANCE;
student.setName("Jim");
student = Student.INSTANCE;
student.setName("Falcon");
student = Student.INSTANCE;
student.setName("Sarvik");
student = Student.INSTANCE;
System.out.println(student);
}
}
以上代码的输出为
Student is Sarvik
同理,我也用java脚本写过单例,
如下
var Student = (function(){
var name = "";
var obj = {
setName : function(studentName){
name = studentName;
},
getName : function(){
return name;
}
}
return {
INSTANCE : obj
}
})();
var student = Student.INSTANCE;
student.setName("Arnold")
student = Student.INSTANCE;
student.setName("Kevin");
student = Student.INSTANCE;
student.setName("Jim");
student = Student.INSTANCE;
student.setName("Falcon");
student = Student.INSTANCE;
student.setName("Sarvik");
student = Student.INSTANCE;
console.log("Student is "+student.getName());
输出结果如下
rahul@rahul:~/myPractise/JavaScript-DesignPatterns$ node singleton.js
Student is Sarvik
rahul@rahul:~/myPractise/JavaScript-DesignPatterns$
在两个(即java脚本和java)单例模式的实现中
你可以看到对象的状态保持不变,没有改变。
即使我们重置对象的值,
输出给出最新的复位值。
那么使用 Singleton 模式是否可以在我的对象代码中实现不变性 "Student" ?
不是真的。
不可变 class 也不意味着您不能创建更多相同的不可变对象 class。对于单例,通常每个JVM需要一个实例才能称为单例。
Immutable 意味着一旦分配的值就不能改变,所以通常你不会有 setter 不可变的方法,但单例没有这样的要求。但是从内存的角度来看,单例更多,只有一个实例会存活。您不能更改实例,但您可以使用单例来保存您想要的任何值,并且您也可以随时更改它。
例如我们使用 singleton class 作为服务定位器。我们可以根据需要在任何给定时间点更改服务。对于同一件事,我可以用相同的变量创建 2 个不可变 class 对象,但保存不同的值。
希望这对您有所帮助。
首先,你给出的单例示例,实际上并不是单例。
单例模式强制您在整个应用程序中每个 class 只使用一个对象。在您的示例中,可以创建尽可能多的对象。
可以使用私有构造函数强制创建单个对象,并在每次使用一些静态方法(比如 getInstance())时提供 'INSTANCE' 每当对象请求到达那个 class。
不变性有不同的用途。它主要与对象的安全性有关,例如一旦对象被创建,你就不能改变它的状态。
很明显,您必须在创建该对象时提供状态。我的意思是你需要一个参数构造函数来为该对象分配一个永久状态。
所以基本区别是:
单例:在任何情况下都不能拥有多个对象。(与不可变对象不同,您不受状态更改的限制,但大多数情况下您不应该更改单例对象的状态)
不可变性:在任何情况下都无法更改对象的状态,但您可以拥有堆允许的尽可能多的对象;-)
你的学生class几乎是一成不变的。 Student class 的 public setName() 防止状态不可变。不可变对象无法提供更改其状态的方法。
如果您的对象是不可变的,则您的学生实例Student.INSTANCE
无法更改其名称。但是,您多次更改其名称。最后的 systemOut
确认了可变性:姓氏 "setted" 由 toString()
方法显示。
Student student = Student.INSTANCE;
student.setName("Arnold");
student = Student.INSTANCE;
student.setName("Kevin");
student = Student.INSTANCE;
student.setName("Jim");
student = Student.INSTANCE;
student.setName("Falcon");
student = Student.INSTANCE;
student.setName("Sarvik");
student = Student.INSTANCE;
System.out.println(student);
此外,如果一个不可变对象想要提供与状态修改相关的操作,它不会应用到实例本身,它returns而是一个具有这个新状态的对象的新实例。
否:单例性和不变性是正交的。单例可以是可变的或不可变的;非单例可以是可变的或不可变的。
但是,如果在多线程中使用,单例必须是线程安全的;不可变对象本质上是线程安全的。
你的 Student
class 大约是单例,但不是不可变的:任何 class 你有一个 setter 方法来改变成员变量 不能不可变。
但是,你的Student
class不是线程安全的,甚至安全:
- 您可以改变
name
的值,但是无法保证 name
字段在其他线程中的可见性:因为您不同步对变量的访问(或使用AtomicReference
或 volatile
),有可能一个线程可以更新名称,但其他线程继续看到旧值。因此,您可能会观察到线程干扰效应。
- class 的用户可以将
INSTANCE
字段置空,这意味着任何人在任何时候试图访问该字段的值都必须首先检查它是否为空.这很容易通过使字段 final
; 来解决。但最好只是将 class 变成单元素枚举。
我读到的是不可变性意味着一旦分配的值保持不变,即一旦分配,它在代码中反映为相同的值。
不可变性主要用于多线程环境中的函数式编程范例。
但是
单例模式是否也解决了同样的问题,
下面是java,
写的单例 package com.main;
class Student{
private Student(){
}
public static Student INSTANCE;
static{
INSTANCE = new Student();
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString(){
return "Student is "+this.name;
}
}
public class SingletonTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student student = Student.INSTANCE;
student.setName("Arnold");
student = Student.INSTANCE;
student.setName("Kevin");
student = Student.INSTANCE;
student.setName("Jim");
student = Student.INSTANCE;
student.setName("Falcon");
student = Student.INSTANCE;
student.setName("Sarvik");
student = Student.INSTANCE;
System.out.println(student);
}
}
以上代码的输出为
Student is Sarvik
同理,我也用java脚本写过单例,
如下
var Student = (function(){
var name = "";
var obj = {
setName : function(studentName){
name = studentName;
},
getName : function(){
return name;
}
}
return {
INSTANCE : obj
}
})();
var student = Student.INSTANCE;
student.setName("Arnold")
student = Student.INSTANCE;
student.setName("Kevin");
student = Student.INSTANCE;
student.setName("Jim");
student = Student.INSTANCE;
student.setName("Falcon");
student = Student.INSTANCE;
student.setName("Sarvik");
student = Student.INSTANCE;
console.log("Student is "+student.getName());
输出结果如下
rahul@rahul:~/myPractise/JavaScript-DesignPatterns$ node singleton.js
Student is Sarvik
rahul@rahul:~/myPractise/JavaScript-DesignPatterns$
在两个(即java脚本和java)单例模式的实现中 你可以看到对象的状态保持不变,没有改变。
即使我们重置对象的值, 输出给出最新的复位值。
那么使用 Singleton 模式是否可以在我的对象代码中实现不变性 "Student" ?
不是真的。
不可变 class 也不意味着您不能创建更多相同的不可变对象 class。对于单例,通常每个JVM需要一个实例才能称为单例。
Immutable 意味着一旦分配的值就不能改变,所以通常你不会有 setter 不可变的方法,但单例没有这样的要求。但是从内存的角度来看,单例更多,只有一个实例会存活。您不能更改实例,但您可以使用单例来保存您想要的任何值,并且您也可以随时更改它。
例如我们使用 singleton class 作为服务定位器。我们可以根据需要在任何给定时间点更改服务。对于同一件事,我可以用相同的变量创建 2 个不可变 class 对象,但保存不同的值。
希望这对您有所帮助。
首先,你给出的单例示例,实际上并不是单例。
单例模式强制您在整个应用程序中每个 class 只使用一个对象。在您的示例中,可以创建尽可能多的对象。
可以使用私有构造函数强制创建单个对象,并在每次使用一些静态方法(比如 getInstance())时提供 'INSTANCE' 每当对象请求到达那个 class。
不变性有不同的用途。它主要与对象的安全性有关,例如一旦对象被创建,你就不能改变它的状态。
很明显,您必须在创建该对象时提供状态。我的意思是你需要一个参数构造函数来为该对象分配一个永久状态。
所以基本区别是:
单例:在任何情况下都不能拥有多个对象。(与不可变对象不同,您不受状态更改的限制,但大多数情况下您不应该更改单例对象的状态)
不可变性:在任何情况下都无法更改对象的状态,但您可以拥有堆允许的尽可能多的对象;-)
你的学生class几乎是一成不变的。 Student class 的 public setName() 防止状态不可变。不可变对象无法提供更改其状态的方法。
如果您的对象是不可变的,则您的学生实例Student.INSTANCE
无法更改其名称。但是,您多次更改其名称。最后的 systemOut
确认了可变性:姓氏 "setted" 由 toString()
方法显示。
Student student = Student.INSTANCE;
student.setName("Arnold");
student = Student.INSTANCE;
student.setName("Kevin");
student = Student.INSTANCE;
student.setName("Jim");
student = Student.INSTANCE;
student.setName("Falcon");
student = Student.INSTANCE;
student.setName("Sarvik");
student = Student.INSTANCE;
System.out.println(student);
此外,如果一个不可变对象想要提供与状态修改相关的操作,它不会应用到实例本身,它returns而是一个具有这个新状态的对象的新实例。
否:单例性和不变性是正交的。单例可以是可变的或不可变的;非单例可以是可变的或不可变的。
但是,如果在多线程中使用,单例必须是线程安全的;不可变对象本质上是线程安全的。
你的 Student
class 大约是单例,但不是不可变的:任何 class 你有一个 setter 方法来改变成员变量 不能不可变。
但是,你的Student
class不是线程安全的,甚至安全:
- 您可以改变
name
的值,但是无法保证name
字段在其他线程中的可见性:因为您不同步对变量的访问(或使用AtomicReference
或volatile
),有可能一个线程可以更新名称,但其他线程继续看到旧值。因此,您可能会观察到线程干扰效应。 - class 的用户可以将
INSTANCE
字段置空,这意味着任何人在任何时候试图访问该字段的值都必须首先检查它是否为空.这很容易通过使字段final
; 来解决。但最好只是将 class 变成单元素枚举。