首页 理论教育 面向对象程序设计:购物车单体模式

面向对象程序设计:购物车单体模式

时间:2023-11-03 理论教育 版权反馈
【摘要】:第八章购物车——单体模式8.1购物车时间:12月24日地点:XX超市人物:大B和他的女朋友今天是一年一度的平安夜,对于年轻男女而言,平安夜是一个狂欢的夜晚,对于大B和他的女朋友来说却是一个难得的享受幸福的时光。

面向对象程序设计:购物车单体模式

第八章 购物车——单体模式

8.1 购物车

时间:12月24日  地点:XX超市  人物:大B和他的女朋友

今天是一年一度的平安夜,对于年轻男女而言,平安夜是一个狂欢的夜晚,对于大B和他的女朋友来说却是一个难得的享受幸福的时光。他们约好今天去超市买些食物,计划今天两个人一起动手做一顿丰盛的晚餐

到了超市,大B去服务区推来购物车,我们都知道一个用户只能有一个购物车的。

大B:“今天平安夜,咱俩难得有时间一起做顿丰盛的晚餐。今晚就让我露一手好好地犒劳犒劳你吧!”

大B的女朋友:“呵呵!好啊!好久没见你亲自下厨了,今天你打算做什么好吃的呀?”

大B:“你想吃什么啊?”

大B的女朋友:“清蒸卢鱼,清蒸毛蟹,还有……”

大B:“呵呵……好好好!今天都依你。”

大B的女朋友:“今天过节,有好多东西都在打特价,水果蔬菜、肉类都好新鲜哩,我们买些回去好不好?”

大B:“好。”

转眼的功夫,大B和他的女朋友买了一大堆的东西,把整个购物车装得满满的。

大B:“我们这么多东西一会用购物车把东西推到超市停车场吧,这样就不用大包小包地往外提了。”

大B的女朋友:“嗯。好!”

8.2 单体模式

时间:12月24日  地点:大B房间  人物:大B,小A

小A:“什么是单体模式?”

大B:“对象只要利用自己的属性完成了自己的任务,那该对象就是承担了责任。除了维持了自身的一致性,该对象无需承担其他任何责任。如果该对象还承担着其他责任,而其他对象又依赖于该特定对象所承担的责任,我们就需要得到该特定对象。就像我和我的女朋友去超市购物使用的购物车一样。”

小A:“什么是单体模式的目的?”

大B:“将类的责任集中到唯一的单体对象中,确保该类只有一个实例,并且为该类提供一个全局访问点。这就是单体模式的目的。”

小A:“师兄,单体模式的难点是什么啊?”

大B:“单体模式的难点不在于单体模式的实现,而在于在系统中任何识别单体和保证单体的唯一性。”

8.3 单体模式的实现

小A:“师兄,单体模式的实现要怎么去实现?”

大B:“1、提供唯一的私有构造器,避免多个单体(Singleton)对象被创建,这也意味着该单体类不能有子类,那声明你的单例类为final是一个好主意,这样意图明确,并且让编译器去使用一些性能优化选项。如果有子类的话使用protected,protected的构造方法可以被其子类以及在同一个包中的其它类调用。私有构造器可以防止客户程序员通过除由我们提供的方法之外的任意方式来创建一个实例,如果不把构造器声明为private或protected, 编译器会自动的创建一个public的构造函数。2、使用静态域(static field)来维护实例。 将单体对象作为单体类的一个静态域实例化。使用保存唯一实例的static变量,其类型就是单例类型本身。需要的话使用final,使其不能够被重载。”

例如:private static Rutime currentRuntime = new Runtime(); ”

3、使用静态方法(Static Method)来监视实例的创建。

a、加载时实例化

例如:

b、使用时实例化(惰性初始化):这样做可以在运行时收集需要的信息来实例化单体对象,确保实例只有在需要时才被建立出来。

例如:

4、单体对象的成员变量(属性):即单体对象的状态通过单例对象的初始化来实现成员变量的初始化。通过方法对单体对象的成员变量进行更新操作。

例如:

8.4 单体模式的一般方法

小A:“单体模式一般有哪些方法?”

大B:“单体模式主要作用是保证在Java应用程序中,一个Class只有一个实例存在。一般有三种方法,下面我就具体来说说这三种方法吧。”

1、定义一个类,它的构造函数为private的,所有方法为static的。如java.lang.Math其他类对它的引用全部是通过类名直接引用。

例如:

2、定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。

例如:

3、定义一个类,它的构造函数为private的,它有一个static的private的boolean变量,用于表示是否有实例存在。(www.xing528.com)

例如:

8.5 单体模式的不同表现形式

大B:“你知不知道单体模式有哪些不同表现形式?”

小A:“我知道。不同表现形式:1、饿汉式单体类:类被加载的时候将自己初始化。更加安全些。2 、懒汉式单体类:在第一次被引用的时候将自己初始化。提高了效率。3、多例类(多例模式):除了可以提供多个实例,其他和单体类没有区别。”

大B:“不错,我再详细说给你听吧!”

单体模式的不同表现形式之:多例类(多例模式)

所谓多例(Multiton Pattern)实际上就是单例模式的自然推广。作为对象的创建模式,多例模式或多例类有以下的特点:

1、多例类可以有多个实例

2、多例类必须自己创建,管理自己的实例,并向外界提供自己的实例。这种允许有限个或无限个实例,并向整个JVM提供自己实例的类叫做多例类,这种模式叫做多例模式。

(1)有上限多例模式 。有上限的多例类已经吧实例的上限当作逻辑的一部分,并建造到了多例类的内部,这种多例模式叫做有上限多例模式。

(2)无上限多例模式 。例数目没有上限的多例模式叫无上限多例模式。

注意:由于没有上限的多例类对实例的数目是没有限制的,因此,虽然这种多例模式是单例模式的推广,但是这种多例类并不一定能够回到单例类。于事先不知道要创建多少个实例,因此,必然使用聚集管理所有的实例。

例子:购物车

例子:购物车

8.6 单体对象的同步(单体模式与线程)

大B:“在多线程模式下,惰性初始化会使多个线程同时初始化该单体,造成一个JVM中多个单例类型的实例,如果这个单例类型的成员变量在运行过程中发生变化,会造成多个单例类型实例的不一致。”

小A:“那应该怎么办?”

大B:“加个同步修饰符: public static synchronized Singleton getInstance(). 这样就保证了线程的安全性。这种处理方式虽然引入了同步代码,但是因为这段同步代码只会在最开始的时候执行一次或多次,所以对整个系统的性能不会有影响。”

小A:“在更新属性的时候,会造成属性的读写不一致。那应该怎么处理?”

大B:“ 1、读者/写者的处理方式。设置一个读计数器,每次读取信息前,将计数器加1,读完后将计数器减1。使用notifyAll()解除在该对象上调用wait的线程阻塞状态。只有在读计数器为0时,才能更新数据,同时调用wait()方法要阻塞所有读属性的调用。 2、采用‘影子实例’的办法。具体说,就是在更新属性时,直接生成另一个单例对象实例,这个新生成的单例对象实例将从数据库,文件或程序中读取最新的信息;然后将这些信息直接赋值给旧单例对象的属性。”

小A:“嘿嘿!师兄,能不能举例来看一下啊?”

大B:“好的。”

例子:

8.7 识别单体模式

小A:“师兄,要如何去识别单体模式呢?”

大B:“ 1、区别工具类和单体类在于该类是否是有状态的。无状态化,提供工具性质的功能,那就是工具类。如果愿意的话,你可以将单体类分为有他状态和无状态。有状态单体类又称为可变单体类,常用作状态库维护系统的状态。无状态单体类又称为不变单体类,常用作提供工具性质方法的对象。2、是否承担了唯一的责任,并且是否提供了唯一的实例。”

8.8 单体模式和一个所有方法都是静态的工具类之间有什么区别

小A:“单体模式和一个所有方法都是静态的工具类之间有什么区别?”

大B:“1、当一个Class被Load的时候,静态工具类的所有状态都已经被初始化了,而单体模式则可以控制自己的初始化过程2、单体可以继承别的类或被别的类继承, 而静态工具类则不能(其实也能, 但一旦继承了一个有非静态方法或静态值的类以后, 它就无法保证自己只拥有一个实例, 或达到只有一个实例的效果)3、单体可以被扩展到‘双体’,‘三体’, 等等。但静态工具类则丧失了这种可扩展性。般的无状态工具集合适合实现成静态工具类,而拥有丰富状态,但整个系统只允许有一个实例的类,适合实现成单体。”

8.9 与单体模式相关的模式

小A:“那哪些是与单体模式相关的模式?”

大B:“在抽象工厂模式中可以使用单体模式,将具体工厂类设计为单体类。建造模式可以使用单例模式,将具体类设计成单体类。”

8.10 单体模式的应用

小A:“应该怎么去应用单体模式哩?”

大B:“首先是要建立目录,数据库连接或 Socket 连接要受到一定的限制,必须保持同一时间只能有一个连接的存在等这样的单线程操作。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。”

小A:“关于数据库连接应用单体模式的问题应该怎么去解决?”

大B:“关于把单例模式应用到数据库connection的问题较为复杂,如果是简单地把一个connection对象封存在单例对象中,那么在J2EE环境中这是错误的。如果数据库连接做成单例模式也就是说系统只会存在一个数据库连接实例,大家公用。实例不可以并发使用。因此存在排队,单例模式管理是需要排队的。资源有两种,一种需要排队,一种不需要排队,或者需要一种复杂的排队,譬如数据库就是复杂的排队问题,系统的排队是不可避免的,应该由数据库引擎自行解决。解决方式就是纪录的locking,而locking不应该由Java程序解决。尽量将排队的工作交给更低一层来做,这样可以获得更高的效率。但是在单用户系统中这并不是什么严重的问题,因为在某一个时刻只有一个用户在使用,唯一的问题就是系统可能需要几个connection,譬如两个、三个等,而不是一个。 2EE服务器系统中单例模式可以用来管理一个数据库连接池(connection pool)。单例模式可以用来保存这样一个connection pool,在初始化的时候创建譬如100个connection对象,然后再需要的时候提供一个,用过之后返回到pool中。如果不是用单例模式的话,这个pool存在哪里,就是一个问题。最后可能只好存到Application对象中。”

小A:“那关于‘全局’变量的问题又应该如何去解决?”

大B:“使用单例模式来存放‘全局’变量是违背单例模式的用意的,单例模式只在有真正的‘单一实例’的需求时才可以使用。其次,一个设计得当的系统不应该有所谓的‘全局’变量的。这些变量应该放到他们所描述的实体所对应的类中去。将这些变量从他们所描述的实体类中抽出来,放到一个不相干的单体类中去,使得这些变量产生错误的依赖关系和耦合关系。所以,如果需要的话,我们可以将一个承担了责任的类作为一个单体类来实现,而不仅仅是为了一个‘全局’变量。”

大B:“有时使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的。总之:Singleton模式看起来简单,使用方法也很方便,但是真正用好,是非常不容易,需要对Java的类 线程内存等概念有相当的了解。如果你的应用基于容器,那么Singleton模式少用或者不用,可以使用相关替代技术。”

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈