未使用单例模式:
小成承包了塑料生产厂之后,由于生产规模有限,只有一个仓库,不过这样仓库的商品数量也比较容易管理。小成现在想用代码来实现仓库的管理,他先建立仓库类和工人类,仓库里面的quantity表示商品数量,工人有搬运方法MoveIn(int i)和MoveOut(int i)。
然后他通过测试发现,每次工人搬运操作都会新建一个仓库,就是货物都不是放在同一仓库,这是怎么回事呢?
这是他的代码:
package scut.designmodel.SingletonPattern;
//仓库类
class StoreHouse {
private int quantity = 100;
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getQuantity() {
return quantity;
}
}
//搬货工人类
class Carrier{
public StoreHouse mStoreHouse;
public Carrier(StoreHouse storeHouse){
mStoreHouse = storeHouse;
}
//搬货进仓库
public void MoveIn(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i);
}
//搬货出仓库
public void MoveOut(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i);
}
}
//工人搬运测试
public class SinglePattern {
public static void main(String[] args){
StoreHouse mStoreHouse1 = new StoreHouse();
StoreHouse mStoreHouse2 = new StoreHouse();
Carrier Carrier1 = new Carrier(mStoreHouse1);
Carrier Carrier2 = new Carrier(mStoreHouse2);
System.out.println("两个是不是同一个?");
if(mStoreHouse1.equals(mStoreHouse2)){//这里用equals而不是用 == 符号,因为 == 符号只是比较两个对象的地址
System.out.println("是同一个");
}else {
System.out.println("不是同一个");
}
//搬运工搬完货物之后出来汇报仓库商品数量
Carrier1.MoveIn(30);
System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity());
Carrier2.MoveOut(50);
System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity());
}
}
结果:
两个是不是同一个?
不是同一个
仓库商品余量:130
仓库商品余量:50
单例模式介绍:
从上面的代码和结果就可以看出工人类操作的明显不是同一个仓库实例,他在网上查找一堆资料之后,才发现了单例模式这个设计模式:
Java代码是用各种各样的类组成的,我们使用这些类实例化之后的对象来操作这些类,类实例化是通过它的构造方法进行的,要是想实现一个类只有一个实例化对象,就要对类的构造方法下功夫了。
如图,单例模式就是这样做的:把类的构造方法私有化,把构造方法封闭,不让外部调用构造方法实例化,然后自己在内部实例化,让外部通过调用getInstance()方法来返回唯一的实例。
然后他使用单例模式改善了它的代码:
package scut.designmodel.SingletonPattern;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//单例仓库类
class StoreHouse {
//仓库商品数量
private int quantity = 100;
private static StoreHouse ourInstance = new StoreHouse();;
public static StoreHouse getInstance() {
return ourInstance;
}
//封闭构造函数
private StoreHouse() {
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getQuantity() {
return quantity;
}
}
//搬货工人类
class Carrier{
public StoreHouse mStoreHouse;
public Carrier(StoreHouse storeHouse){
mStoreHouse = storeHouse;
}
//搬货进仓库
public void MoveIn(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i);
}
//搬货出仓库
public void MoveOut(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i);
}
}
//工人搬运测试
public class SinglePattern {
public static void main(String[] args){
StoreHouse mStoreHouse1 = StoreHouse.getInstance();
StoreHouse mStoreHouse2 = StoreHouse.getInstance();
Carrier Carrier1 = new Carrier(mStoreHouse1);
Carrier Carrier2 = new Carrier(mStoreHouse2);
System.out.println("两个是不是同一个?");
if(mStoreHouse1.equals(mStoreHouse2)){
System.out.println("是同一个");
}else {
System.out.println("不是同一个");
}
//搬运工搬完货物之后出来汇报仓库商品数量
Carrier1.MoveIn(30);
System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity());
Carrier2.MoveOut(50);
System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity());
}
}
结果:
两个是不是同一个?
是同一个
仓库商品余量:130
仓库商品余量:80
这样的话,就只能有一个仓库实例了,再也不用担心搬运工人进错仓库了。
ps:上面的单例模式是一个简单的饿汉式单例模式,单例模式的各种详细写法就不具体一一介绍了,可以看这里。
应用场景
总的来说,单例模式适用于以下的情况:
- 当类只能有一个实例而且第三方可以从一个公共的访问点访问它时。就像一些网站计数器之类的就会用到单例模式。
优点
- 提供了对唯一实例的受控访问。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
- 可以根据实际情况需要,在单例模式的基础上扩展做出双例模式,多例模式。
缺点
- 单例类的职责过重,里面的代码可能会过于复杂,在一定程度上违背了“单一职责原则”。
- 如果实例化的对象长时间不被利用,会被系统认为是垃圾而被回收,这将导致对象状态的丢失。
参考
《设计模式其实很简单》,刘径舟,张玉华等编著——清华大学出版社,2013.7